@hanamesh/dsh-app-host 0.6.22 → 0.6.25
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/README.md +18 -14
- package/dist/client-ui.d.ts +11 -1
- package/dist/client-ui.js +121 -26
- package/dist/dsh-compat.js +1 -1
- package/dist/dsh.d.ts +1 -2
- package/dist/dsh.js +1 -3
- package/dist/git-command-worker.js +12 -0
- package/dist/git-process.js +8 -3
- package/dist/git-worker.js +37 -7
- package/dist/install-target/harness.js +2 -0
- package/dist/install-target/suite.js +5 -5
- package/dist/installation-status.d.ts +0 -8
- package/dist/library/catalog.js +4 -3
- package/dist/library/handoff.js +3 -11
- package/dist/library/install-target-http.js +39 -0
- package/dist/library/installation-cli.js +41 -16
- package/dist/library/installation-command-worker.js +3 -2
- package/dist/library/installation-discovery.js +95 -0
- package/dist/library/installation-status.js +20 -14
- package/dist/library/installation-worker.js +72 -14
- package/dist/library/routes.js +2 -10
- package/dist/node-runtime/service.js +4 -4
- package/dist/router/sources.js +5 -2
- package/dist/runtime.js +15 -1
- package/dist/store.js +12 -5
- package/dist/windows-job.js +30 -0
- package/dist/windows-job.ps1 +75 -0
- package/docs/CONTRACT.md +34 -3
- package/docs/INSTALLATION_STATUS.md +35 -18
- package/docs/INSTALL_TARGET.md +6 -2
- package/docs/LIBRARY.md +1 -1
- package/docs/LICENSES.json +207 -69
- package/docs/SECURITY.md +15 -0
- package/package.json +41 -27
- package/schemas/catalog-source.schema.json +5 -5
package/README.md
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
0.6.25 候选:本机 HTTPS 目录允许回环端口及 *.localhost,修复彩排种子初始化阻断检测监听;公网 HTTPS 非标准端口仍拒绝。Claude 官方 GitHub 市场清单按 repo 身份核对,保留不同来源拒绝。安装物回归命令见[安装状态](docs/INSTALLATION_STATUS.md),不以页面打开代替检测通过。
|
|
2
|
+
|
|
3
|
+
PX3 跨平台候选:Windows 可以运行插件/公开服务、attach 应用、受控只读 CLI 与普通包安装;`owned` 应用仍在创建进程、数据目录及请求凭据之前明确拒绝 `PLATFORM_UNSUPPORTED`。Windows 单测保留真实 attach 的 HTTP/租约/网关断言,并验证 owned 的持久失败、无进程/文件写入及不消费凭据;这些断言不构成 Windows owned 运行支持。独立 Node 可用不扩大 owned 支持;受管 Node 准备仍遵守原平台限制。Windows 本地快照采用文件 flush 加同目录 rename,不承诺断电后目录项持久;Coding OAuth Gateway 文件源因缺私有 ACL 证明继续拒绝,HTTP 提供方不变。各平台实际结果以本分支 GitHub Actions 矩阵收据为准。
|
|
4
|
+
|
|
5
|
+
R115返修(同0.6.23候选):登录shell按退出结果和封装JSON解析成功,忽略正常stderr;所选CLI保留管理器PATH与XDG目录/缺省,不再用空临时XDG隐藏全局版本。真实Bash/asdf/mise启动链回归与工具来源见 [安装状态](docs/INSTALLATION_STATUS.md)。
|
|
6
|
+
|
|
7
|
+
0.6.23 候选:install-target 对名单外有效网站提供精确CORS的固定403来源拒绝,GET预检以204和按需PNA头支持浏览器读回;不开放能力/清单/安装权限。同意检测后自动发现当前用户的 Claude Code / Codex 官方 CLI,无手动读取器入口;接受不低于 2.1.301 / 0.162.1 且版本格式与清单 JSON 校验通过的版本。安装布局、优先级、只读边界及具名预算见 [安装状态](docs/INSTALLATION_STATUS.md)。
|
|
8
|
+
|
|
9
|
+
PK3 开发供给:`vendor/hanamesh-core-0.3.33.tgz` 是 Core 分支提交 `969a89a1f7e383fca7d6aefa7f27507d3a1605d5` 的正常归档,仅供开发与公开合约测试;SHA256 和来源见 `vendor/core-fixture.json`,安装仍严格检查真实 peers。这不是已发布的 npm Core,也不进入运行时依赖。
|
|
10
|
+
|
|
11
|
+
PK1 兼容候选:开发输入更新为 DSH 0.2.2-rc.1 / Cordis 4.1.0 / Schemastery 3.19.0(适用者)。公开 peer 保留兼容范围,并显式包含 0.2.2 预发布;不锁定用户宿主版本。本分支尚未发布,安装和原生加载结果以 PK1 交件为准。
|
|
12
|
+
|
|
1
13
|
0.6.21 候选:普通市场和网站交接消费 Indexer 的固定 GitHub SHA/子目录/包名/构建名单。客户端沿原确认页展示来源、完整 commit 与本机构建提示;确认回传同一目标,目录更新不得换 SHA。执行仍走受控官方 DSH CLI / pnpm,读回精确来源与启用项后报告需完全退出重开;没有改上游或另写安装器。详见 [安装目标](docs/INSTALL_TARGET.md)。初轮已完成隔离 DSH、网站及真实 DTO 验收;本轮 R25 另修未匹配固定目标时的拒绝与退休目录版本字段,仍待复审,不宣称已发布。
|
|
2
14
|
|
|
3
15
|
0.6.22 候选:普通插件只执行目录固定目标;缺 installMethods 即拒绝。套件安装命令只消费 Core,删除普通包名的元数据查询和规则加载。
|
|
@@ -224,22 +236,14 @@ Actions runs on the standard public/free `macos-15` arm64 runner because the exi
|
|
|
224
236
|
|
|
225
237
|
## 发布检查:平台依赖闭包(不可关闭)
|
|
226
238
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
运行 `npm run release:check` 正常打包并检查;发布仅用 `npm run release:publish`,发布已检查的同一 tarball。直接目录 npm publish 会被 prepublishOnly 明确拒绝。开发工具 `@hanamesh/release-platform-gate` 仅 devDependency,先按接线请求复制经 SHA 固定的正常工具包并更新冻结锁;CI 必须接入这个发布门。详细输出包含不可关闭的原因、正常包/锁 SHA 和扫描路径;absent optional peer 按 npm 定义列明,不等同 optionalDependencies。
|
|
230
|
-
|
|
231
|
-
共享门已升级到 R2 PASS 的 0.1.10,原正常工具 tar SHA256 为 `c72010c96a5f22e36d65f6c1fd850e583dead7e843644eba1657b4416d9cbd0d`;最终 main 来源见 `vendor/release-platform-gate.json`。本仓接线仍须 R1 独审,不等于 npm 发布放行,本次不发布。
|
|
232
|
-
|
|
233
|
-
`verify:pack` 原有 tarball 参数保持;正常消费者验证之后通过共享公开 checkRelease 核对同一个归档,CI沿该入口必经硬门,不另打一个包代替原包。接线没有复制扫描实现。
|
|
234
|
-
|
|
235
|
-
有限依赖形态合同为正常开发工具包中的 `DEPENDENCY_SHAPES.md`(dependency-shapes-v1):支持 registry/git/file/link/tarball/workspace/bundled/vendor 的明确子集,不能当作 npm/pnpm 的无限全集。合同外一律 `UNSUPPORTED_DEPENDENCY_SHAPE`,逐项公开 `package/path/shape/why/action/contract`;缓存、bundle、环境变量或额外 CLI 开关都不能绕过准入。已知缺证据、冻结漂移、完整性和平台违规仍各自具名拒绝,不能以“任意失败”代替正确理由。
|
|
239
|
+
共享 `@hanamesh/release-platform-gate` **0.1.12** 已经 R119b 复审 PASS,来源 `hanamesh-docs/tools/release-platform-gate` 分支 `fix/pg1-leoventory-installer-risk-20261109`,提交 `95e54102f9380ea2e8e4d8a2e36eacd387c3f7e7`。正常工具 tgz SHA256 `caf78747649b1dd37cbe9ba331351c81f94a48dc9e779aee83c75336140068d1`;七载荷摘要、复审提交及冻结 SRI 见 `vendor/release-platform-gate.json`、`vendor/SHA256SUMS` 和锁。它仅为 devDependency,不进入运行时或产品 bundle。
|
|
236
240
|
|
|
237
|
-
|
|
241
|
+
`DSH_PNPM_PLATFORM_CLOSURE` 是不可关闭的正确性不变量(disableable:false)。历史 DSH bundled pnpm 11.7.0 位于 pnpm#12297 风险系列,修复收录于 pnpm 11.23.0。原始 HanaMesh 挂起进程首错仍未找回,不能宣称历史身份/归因已闭合。合法 optional 平台字段和完整强 SRI 也不能独自证明安装器会退出。
|
|
238
242
|
|
|
239
|
-
|
|
243
|
+
根及必需路径的 os/cpu/libc 继续 PLATFORM_FIELD 拒绝;可选子图先完整核锁、实际归档、名称/版本/范围/peer、选择器一致与强 SRI,再按 `dsh-pnpm-platform-exit-v1` 核安装矩阵。所有平台工件均扫描,不按检查机过滤。缺矩阵为 INSTALLATION_TARGETS_REQUIRED,旧风险组合为 PNPM_PLATFORM_HANG_RISK,未审组合为 INSTALLATION_TARGET_UNSUPPORTED;混入一个安全目标不能洗绿旧组合。未知依赖形态仍 UNSUPPORTED_DEPENDENCY_SHAPE,输出 shape / why / action / contract;有限 dependency-shapes-v1 与 INSTALLATION_RISK.md 均随正常工具包交付。没有 skip/关闭开关、环境推断、重试、超时、omit-optional 或白名单绕过。
|
|
240
244
|
|
|
241
|
-
|
|
245
|
+
本次发布资格的完整目标明确保存在 `release/installation-targets.json`:DSH **0.2.2-rc.1 / pnpm 11.28.5 / darwin-arm64、linux-arm64**。这是 R119b 有退出证据的两个组合,不是检查机自动选择,也不把公开宽 peer 范围当作已通过的安装矩阵;其他宿主/安装器/架构不计入本次资格,旧组合仍拒绝。开发冻结安装的 pnpm 10.33.0 不是 bundled 11.28.5 的实装证据;后续 GT19 两机门必须核实际 pnpm 版本和完整闭包。
|
|
242
246
|
|
|
243
|
-
|
|
247
|
+
`release:check` 与 `release:publish` 都显式传同一 `--installation-targets release/installation-targets.json`;`verify:pack` 在原消费者验证后通过共享公开 `checkRelease({installationTargets:...})` 检查同一归档,没有复制扫描实现或捕获忽略门错误。
|
|
244
248
|
|
|
245
|
-
|
|
249
|
+
运行 `npm run release:check` 产生正常归档并核验;`release:publish` 只发布同一已检查且复核 SHA 的 tar。`prepublishOnly` 的 source-guard 拒绝源码目录直接发布。CI 的既有 verify:pack / preflight 必经此门;无 continue-on-error 或旁路。此接线提交没有执行 npm publish、tag、部署或产品真机发布验收。
|
package/dist/client-ui.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ export function NodeRuntimeSection():unknown;
|
|
|
12
12
|
export function LibraryAction():unknown;
|
|
13
13
|
/** The HanaMesh market page (applications + plugins; install / upgrade / uninstall / restart notice).
|
|
14
14
|
* `embedded` renders it inline for the Extension Management tab instead of as the shell overlay. */
|
|
15
|
-
export function LibraryOverlay(props?:{embedded?:boolean,preferredSubsectionId?:string,verification?:boolean}):unknown;
|
|
15
|
+
export function LibraryOverlay(props?:{embedded?:boolean,mainPage?:boolean,preferredSubsectionId?:string,verification?:boolean}):unknown;
|
|
16
16
|
/** rc.32: the「只能其一」notice shown at the top of the market page when dshmarket holds the seat. */
|
|
17
17
|
export function MarketConflictNotice():unknown;
|
|
18
18
|
/** Identifiers only; metadata and confirmation are owned by the client's market. */
|
|
@@ -38,3 +38,13 @@ export const INSTALL_TARGET_CONTRACT_VERSION:'1';
|
|
|
38
38
|
|
|
39
39
|
/** AppHost-owned registry selection, independent of catalog manifest sources. */
|
|
40
40
|
export function SuiteSourceSettingsSection():unknown;
|
|
41
|
+
|
|
42
|
+
/** Additive DSH layout seats and the Core-owned environment content extension. */
|
|
43
|
+
export function registerAppHostPages(ctx:unknown):()=>void;
|
|
44
|
+
export function EnvironmentContent():unknown;
|
|
45
|
+
/** Current catalog contract supplies none of these counts; null is explicit unknown, never zero. */
|
|
46
|
+
export interface CredibilityFacts {itemId?:string;signals?:{stars?:number|null;npmDownloads30d?:number|null;storeInstalls30d?:number|null;checkedAt?:string|null}|null;popularity?:{users30d?:number|null;asOf?:string|null}|null;checkedAt?:string|null;asOf?:string|null}
|
|
47
|
+
export function credibilityFacts(facts?:CredibilityFacts|null):{id:string;label:string;value:number|null;reason:'catalog-field-unavailable'|null}[];
|
|
48
|
+
export function readCredibility(source:unknown,item:{id:string},fetcher?:typeof fetch):Promise<CredibilityFacts>;
|
|
49
|
+
/** HanaMesh-view catalog website link; absent for fixtures and other provider protocols. */
|
|
50
|
+
export function catalogWebsiteLink(source:unknown,item:unknown):string|null;
|
package/dist/client-ui.js
CHANGED
|
@@ -267,8 +267,8 @@ window.__ModuleLoader__.load({id:'@hanamesh/dsh-app-host',factory:function(requi
|
|
|
267
267
|
"copy261": "准备安装环境未完成。请查看技术详情中的准确原因;未自动换源或重试。",
|
|
268
268
|
"copy262": "只在本机 127.0.0.1 上监听,请求的 Host 必须是 127.0.0.1 或 localhost 加监听端口",
|
|
269
269
|
"copy263": "防止别的机器或 DNS 重绑定的网页冒充本机",
|
|
270
|
-
"copy264": "
|
|
271
|
-
"copy265": "
|
|
270
|
+
"copy264": "只有「允许交接的网站」能读取客户端能力、交接安装和交接状态;未许可网站仅能读取固定的来源拒绝提示",
|
|
271
|
+
"copy265": "拒绝提示只说明本机监听拒绝了该网站,不透露客户端类型、能力、安装情况或允许名单",
|
|
272
272
|
"copy266": "网站交接只会在应用库里弹出确认;只有你点「确认安装」才会安装",
|
|
273
273
|
"copy267": "网站不能替你决定装什么",
|
|
274
274
|
"copy268": "交接状态只给发起它的那个网站看",
|
|
@@ -568,8 +568,8 @@ window.__ModuleLoader__.load({id:'@hanamesh/dsh-app-host',factory:function(requi
|
|
|
568
568
|
"copy261": "Installation environment preparation did not complete. See Technical details for the exact reason. No automatic source switching or retry occurred.",
|
|
569
569
|
"copy262": "Listen only on 127.0.0.1. The request Host must be 127.0.0.1 or localhost with the listening port.",
|
|
570
570
|
"copy263": "Prevents remote machines or DNS-rebinding pages from impersonating this machine.",
|
|
571
|
-
"copy264": "Only allowed websites can
|
|
572
|
-
"copy265": "
|
|
571
|
+
"copy264": "Only allowed websites can read client capabilities, hand off installations or read handoff status. Other websites can read only a fixed origin-refusal message.",
|
|
572
|
+
"copy265": "The refusal reveals only that a local listener rejected the website, without client type, capabilities, installations or the allowlist.",
|
|
573
573
|
"copy266": "Website handoffs only open market confirmation. Installation requires your Confirm installation action.",
|
|
574
574
|
"copy267": "A website cannot decide what to install for you.",
|
|
575
575
|
"copy268": "Handoff status is visible only to the website that created it.",
|
|
@@ -609,6 +609,41 @@ window.__ModuleLoader__.load({id:'@hanamesh/dsh-app-host',factory:function(requi
|
|
|
609
609
|
};
|
|
610
610
|
Object.assign(COPY.zh,{githubRepository:'来源仓库',githubCommit:'固定 commit',githubPath:'仓库子目录',githubBuild:'会在本机执行构建脚本',githubBuildPolicy:'确认后将按名单允许构建;pnpm 会把许可保存到当前 profile。',githubNoBuild:'未授予构建脚本许可'});
|
|
611
611
|
Object.assign(COPY.en,{githubRepository:'Source repository',githubCommit:'Pinned commit',githubPath:'Repository subdirectory',githubBuild:'Build scripts will run on this computer',githubBuildPolicy:'Confirmation permits the listed builds; pnpm saves these permissions in the current profile.',githubNoBuild:'No build scripts are approved'});
|
|
612
|
+
// Owned copy for the additive HanaMesh pages.
|
|
613
|
+
COPY.zh.cl1Page0="应用库";COPY.en.cl1Page0="Library";
|
|
614
|
+
COPY.zh.cl1Page1="详情";COPY.en.cl1Page1="Details";
|
|
615
|
+
COPY.zh.cl1Page2="返回应用库";COPY.en.cl1Page2="Back to library";
|
|
616
|
+
COPY.zh.cl1Page3="关于这个应用";COPY.en.cl1Page3="About this app";
|
|
617
|
+
COPY.zh.cl1Page4="可信度";COPY.en.cl1Page4="Credibility";
|
|
618
|
+
COPY.zh.cl1Page5="GitHub 星数";COPY.en.cl1Page5="GitHub stars";
|
|
619
|
+
COPY.zh.cl1Page6="npm 下载 / 30 天";COPY.en.cl1Page6="npm downloads / 30 days";
|
|
620
|
+
COPY.zh.cl1Page7="安装人数";COPY.en.cl1Page7="People who installed";
|
|
621
|
+
COPY.zh.cl1Page8="使用人数";COPY.en.cl1Page8="People who used";
|
|
622
|
+
COPY.zh.cl1Page9="当前目录未提供";COPY.en.cl1Page9="Not supplied by this catalog";
|
|
623
|
+
COPY.zh.cl1Page10="可信度数据缺失不代表 0;本机安装状态与平台采用度分别显示。";COPY.en.cl1Page10="Missing credibility data does not mean zero. Local installation state is separate from platform adoption.";
|
|
624
|
+
COPY.zh.cl1Page11="来源与安装目标";COPY.en.cl1Page11="Source and installation target";
|
|
625
|
+
COPY.zh.cl1Page12="在网站查看";COPY.en.cl1Page12="View on website";
|
|
626
|
+
COPY.zh.cl1Page13="发现适合你的工具,查看来源后再安装;账号与模型授权分别管理。";COPY.en.cl1Page13="Find tools for your work and review their sources before installing. Account and model authorization are managed separately.";
|
|
627
|
+
COPY.zh.cl1Page14="安装确认";COPY.en.cl1Page14="Confirm installation";
|
|
628
|
+
COPY.zh.cl1Page15="我已核对来源与安装目标";COPY.en.cl1Page15="I reviewed the source and installation target";
|
|
629
|
+
COPY.zh.cl1Page16="设置与连接";COPY.en.cl1Page16="Settings and connections";
|
|
630
|
+
COPY.zh.cl1Page17="账号与设备";COPY.en.cl1Page17="Account and device";
|
|
631
|
+
COPY.zh.cl1Page18="模型连接";COPY.en.cl1Page18="Model connections";
|
|
632
|
+
COPY.zh.cl1Page19="环境与来源";COPY.en.cl1Page19="Environment and sources";
|
|
633
|
+
COPY.zh.cl1Page20="HanaMesh / 模型连接";COPY.en.cl1Page20="HanaMesh / Model connections";
|
|
634
|
+
COPY.zh.cl1Page21="连接模型,保持掌控。";COPY.en.cl1Page21="Connect models, stay in control.";
|
|
635
|
+
COPY.zh.cl1Page22="登录账号不提供模型凭据。沿用宿主连接,按应用分别查看授权。";COPY.en.cl1Page22="Signing in does not supply model credentials. Use host connections and review authorization for each app.";
|
|
636
|
+
COPY.zh.cl1Page23="对应页面暂不可用,请确认 HanaMesh Core 已启用。";COPY.en.cl1Page23="This page is unavailable. Check that HanaMesh Core is enabled.";
|
|
637
|
+
COPY.zh.cl1Page24="目录来源";COPY.en.cl1Page24="Catalog sources";
|
|
638
|
+
COPY.zh.cl1Page25="展开后读取当前设置;不会自动保存或安装。";COPY.en.cl1Page25="Expand to read the current settings. Nothing is saved or installed automatically.";
|
|
639
|
+
COPY.zh.cl1Extra0="HanaMesh / 应用库";COPY.en.cl1Extra0="HanaMesh / Library";
|
|
640
|
+
COPY.zh.cl1Extra1="好工具,让想法走得更远。";COPY.en.cl1Extra1="Good tools take your ideas further.";
|
|
641
|
+
COPY.zh.cl1Extra2="安装之前,先看清楚。";COPY.en.cl1Extra2="Review before you install.";
|
|
642
|
+
COPY.zh.cl1Extra3="精确版本";COPY.en.cl1Extra3="Exact version";
|
|
643
|
+
COPY.zh.cl1Extra4="应用筛选";COPY.en.cl1Extra4="App filter";
|
|
644
|
+
COPY.zh.cl1Extra5="全部应用";COPY.en.cl1Extra5="All apps";
|
|
645
|
+
COPY.zh.cl1Extra6="还没有已安装项。";COPY.en.cl1Extra6="No installed items yet.";
|
|
646
|
+
COPY.zh.cl1Extra7="没有找到符合条件的应用。";COPY.en.cl1Extra7="No apps match these filters.";
|
|
612
647
|
const COPY_KEYS=new Map(Object.entries(COPY.zh).map(([key,text])=>[text,key]));
|
|
613
648
|
let localeService=null;
|
|
614
649
|
function t(text){if(typeof text==='function')return text();const key=COPY_KEYS.get(text);if(!key)return text;return localeService?localeService.bind('hanamesh.apphost')(key):COPY.zh[key];}
|
|
@@ -649,6 +684,8 @@ window.__ModuleLoader__.load({id:'@hanamesh/dsh-app-host',factory:function(requi
|
|
|
649
684
|
h('details',null,h('summary',null,t('技术详情')),h('p',null,t('应用启动时按当前配置与应用声明匹配供应商并注入;这里只选择模型或撤回、恢复该连接的授权,不改变来源开关。'))),
|
|
650
685
|
routeRows.length?h('table',{className:'hm-provider-records'},h('thead',null,h('tr',null,...[t('应用'),t('用途'),t('供应商'),t('模型'),t('状态'),''].map((label,index)=>h('th',{key:index},label)))),h('tbody',null,...routeRows)):h('p',{className:'hm-provider-hint'},t('尚无需要模型服务的已安装应用。')));
|
|
651
686
|
}
|
|
687
|
+
let pageLayout=null;
|
|
688
|
+
const showLibrary=()=>{if(pageLayout){pageLayout.selectPanel('hanamesh-library');setLibraryVisible(false);}else setLibraryVisible(true);};
|
|
652
689
|
let libraryVisible=false;const libraryListeners=new Set();
|
|
653
690
|
const setLibraryVisible=value=>{libraryVisible=value;for(const listener of libraryListeners)listener(value);};
|
|
654
691
|
let targetSelection=null,targetSequence=0,pendingOperations={};
|
|
@@ -661,7 +698,7 @@ window.__ModuleLoader__.load({id:'@hanamesh/dsh-app-host',factory:function(requi
|
|
|
661
698
|
const setPending=update=>{pendingOperations=typeof update==='function'?update(pendingOperations):update;for(const listener of pendingListeners)listener(pendingOperations);};
|
|
662
699
|
/** Public client entry: identifiers only; the market fetches its own catalog and owns confirmation. */
|
|
663
700
|
async function openInstallTarget(input,{handoffId}={}){
|
|
664
|
-
const sequence=++targetSequence;if(entryVisible)
|
|
701
|
+
const sequence=++targetSequence;if(entryVisible)showLibrary();
|
|
665
702
|
try{
|
|
666
703
|
if(!input||typeof input!=='object'||Array.isArray(input))throw new Error('INVALID_INPUT');
|
|
667
704
|
// rc.8: optional contract declaration; absent keeps v1 semantics, an unsupported version is refused before any request.
|
|
@@ -709,7 +746,7 @@ window.__ModuleLoader__.load({id:'@hanamesh/dsh-app-host',factory:function(requi
|
|
|
709
746
|
function consumeInstallTargetHash(){
|
|
710
747
|
const hash=globalThis.location?.hash;if(!hash?.startsWith(INSTALL_TARGET_HASH))return;
|
|
711
748
|
const params=new URLSearchParams(hash.slice(INSTALL_TARGET_HASH.length));
|
|
712
|
-
if(new Set(params.keys()).size!==[...params.keys()].length){targetSequence++;if(entryVisible)
|
|
749
|
+
if(new Set(params.keys()).size!==[...params.keys()].length){targetSequence++;if(entryVisible)showLibrary();setTarget({status:'failed',error:'INVALID_INPUT'});return;}
|
|
713
750
|
const input=Object.fromEntries(params);
|
|
714
751
|
if(input.installTarget!==undefined){try{input.installTarget=JSON.parse(input.installTarget);}catch{setTarget({status:'failed',error:'INVALID_INPUT'});return;}}
|
|
715
752
|
void openInstallTarget(input).catch(()=>{});
|
|
@@ -739,8 +776,8 @@ window.__ModuleLoader__.load({id:'@hanamesh/dsh-app-host',factory:function(requi
|
|
|
739
776
|
function LibraryAction(){useLocale();
|
|
740
777
|
const[shown,setShown]=React.useState(entryVisible);
|
|
741
778
|
React.useEffect(()=>{entryListeners.add(setShown);return()=>entryListeners.delete(setShown);},[]);
|
|
742
|
-
if(!shown)return null;
|
|
743
|
-
return h('button',{type:'button',className:'hm-library-action',onClick:()=>
|
|
779
|
+
if(!shown||pageLayout)return null;
|
|
780
|
+
return h('button',{type:'button',className:'hm-library-action',onClick:()=>showLibrary()},t('市场'));
|
|
744
781
|
}
|
|
745
782
|
function LibrarySourcesSection(){useLocale();
|
|
746
783
|
const[sources,setSources]=React.useState([]),[draft,setDraft]=React.useState(''),[error,setError]=React.useState('');
|
|
@@ -900,8 +937,12 @@ h('div',{className:'hm-source-add'},h('input',{value:draft,placeholder:'https://
|
|
|
900
937
|
{method:'POST',path:'/apps/heartbeat',sample:{viewId:'verification-view',leaseToken:''}},
|
|
901
938
|
{method:'POST',path:'/apps/close',sample:{viewId:'verification-view',leaseToken:''}},
|
|
902
939
|
];
|
|
903
|
-
|
|
940
|
+
Object.assign(COPY.zh,{cl1Detail:'HanaMesh / 应用详情',cred0:'暂无',cred1:'当前暂无数据',cred2:'公开统计',cred3:'DSH 安装 / 30 天',cred4:'使用人数 / 30 天',cred5:'信号更新:',cred6:'使用人数更新:'});
|
|
941
|
+
Object.assign(COPY.en,{cl1Detail:'HanaMesh / Application details',cred0:'Not available',cred1:'No data available',cred2:'Public statistics',cred3:'DSH installs / 30 days',cred4:'Users / 30 days',cred5:'Signals updated: ',cred6:'Users updated: '});
|
|
942
|
+
function LibraryOverlay({embedded,mainPage=false,preferredSubsectionId,verification=false}={}){useLocale();
|
|
904
943
|
const[visible,setVisible]=React.useState(embedded?true:libraryVisible),[snapshot,setSnapshot]=React.useState(null),[error,setError]=React.useState(''),[receipt,setReceipt]=React.useState(null),[query,setQuery]=React.useState(''),[filter,setFilter]=React.useState(''),[extra,setExtra]=React.useState([]),[pending,updatePending]=React.useState(pendingOperations),[seenCategories,setSeenCategories]=React.useState([]),[changed,setChanged]=React.useState(false),[openingAppId,setOpeningAppId]=React.useState(null),[closing,setClosing]=React.useState(false);
|
|
944
|
+
const[detailId,setDetailId]=React.useState(null),[acceptedTarget,setAcceptedTarget]=React.useState(null),[libraryTab,setLibraryTab]=React.useState('all'),[credibility,setCredibility]=React.useState(null);
|
|
945
|
+
React.useEffect(()=>{let live=true;setCredibility(null);if(detailId&&visible)void readCredibility(snapshot?.source,{id:detailId}).then(value=>{if(live)setCredibility(value);});return()=>{live=false;};},[detailId,visible,snapshot?.source?.manifestUrl]);
|
|
905
946
|
const[target,updateTarget]=React.useState(targetSelection),[targetItemId,setTargetItemId]=React.useState(''),[targetPackageName,setTargetPackageName]=React.useState('');
|
|
906
947
|
const[verificationCall,setVerificationCall]=React.useState('GET /hanamesh/library/install-target'),[verificationInput,setVerificationInput]=React.useState('{}'),[verificationOutput,setVerificationOutput]=React.useState(null),[verifying,setVerifying]=React.useState(false);
|
|
907
948
|
const verificationBusy=React.useRef(false);
|
|
@@ -979,7 +1020,7 @@ h('div',{className:'hm-source-add'},h('input',{value:draft,placeholder:'https://
|
|
|
979
1020
|
setPending(p=>({...p,[key]:outcome.ok?(awaitReadback?{label,text:()=>(t(label)+t('读回中…')),busy:true}:{label,text:()=>(liveLoaded?t(label)+t('完成,已生效,可以直接使用')+baselineText(outcome.result):t(label)+t('完成,重启 DSH 后生效'))}):{label,text:()=>(outcome.unknown?t(label)+t('结果不可追溯,请查看已安装列表后再明确操作'):t(label)+t('失败:')+outcome.code+'('+failureReason(outcome.code,outcome.details)+')'+recoveryText(outcome.recovery)+suiteRecoveryText(outcome.details)),failed:true,diagnostic:outcome.details?.diagnostic}}));
|
|
980
1021
|
await load();return outcome;}catch(e){const code=String(e.message??e);setPending(p=>({...p,[key]:{label,text:()=>(t(label)+t('失败:')+code),failed:true}}));return{ok:false,code};}};
|
|
981
1022
|
const confirmTarget=async()=>{
|
|
982
|
-
const selected=targetSelection;if(selected?.status!=='confirmation-required'||pendingOperations[selected.item.id]?.busy)return;
|
|
1023
|
+
const selected=targetSelection;if(selected?.status!=='confirmation-required'||acceptedTarget!==selected||pendingOperations[selected.item.id]?.busy)return;
|
|
983
1024
|
setTarget({...selected,status:'installing'});
|
|
984
1025
|
const suite=selected.item.kind==='suite-upgrade';
|
|
985
1026
|
const reviewed={itemId:selected.item.id,packageName:selected.item.package.name,...(selected.item.installTarget?{installTarget:selected.item.installTarget}:{})};
|
|
@@ -1041,7 +1082,7 @@ h('div',{className:'hm-source-add'},h('input',{value:draft,placeholder:'https://
|
|
|
1041
1082
|
const restartRequired=changed||Boolean(snapshot?.restartRequired);
|
|
1042
1083
|
const filters=[...BASE_FILTERS,...seenCategories.filter(c=>c!=='hanamesh-app').map(c=>[c,c])];
|
|
1043
1084
|
const openButton=row=>h('button',{type:'button',disabled:Boolean(openingAppId),onClick:()=>void open(row)},openingAppId===row.appId?t('打开中…'):t('打开'));
|
|
1044
|
-
const card=item=>{const row=item.installed,op=pending[item.id],busy=op?.busy;const installBody={itemId:item.id,...(item.package?.name?{packageName:item.package.name}:{}),...(item.installTarget?{installTarget:item.installTarget}:{})};
|
|
1085
|
+
const card=(item,detail=false)=>{const row=item.installed,op=pending[item.id],busy=op?.busy;const installBody={itemId:item.id,...(item.package?.name?{packageName:item.package.name}:{}),...(item.installTarget?{installTarget:item.installTarget}:{})};
|
|
1045
1086
|
const reviewOrInstall=label=>item.installTarget?openInstallTarget(installBody).catch(()=>{}):operate('/hanamesh/library/install',installBody,item.id,label);
|
|
1046
1087
|
let action=h('span',null,t('仅收录,不可安装')),state=null;
|
|
1047
1088
|
if(row)state=h('span',{className:'hm-market-state','data-hanamesh-state':row.state},(t(STATE_TEXT[row.state]??row.state))+(row.version?' '+row.version:''));
|
|
@@ -1054,20 +1095,25 @@ h('div',{className:'hm-source-add'},h('input',{value:draft,placeholder:'https://
|
|
|
1054
1095
|
else if(row?.state==='installed')action=PROTECTED.has(row.packageName)?h('span',null,t('HanaMesh 套件')):h('button',{type:'button',onClick:()=>void operate('/hanamesh/library/plugins/uninstall',{packageName:row.packageName},item.id,t('卸载'))},t('卸载'));
|
|
1055
1096
|
else if(row?.state==='uninstalled-not-unloaded')action=h('span',null,t('已卸载,重启 DSH 后生效'));
|
|
1056
1097
|
else if(row)action=h('span',null,t('已安装,重启 DSH 后生效'));
|
|
1057
|
-
return h('article',{key:item.id,'data-hanamesh-library-item':item.id,'data-hanamesh-kind':item.kind},h('h3',null,item.displayName),h('p',null,item.summary),
|
|
1098
|
+
return h('article',{key:item.id,'data-hanamesh-library-item':item.id,'data-hanamesh-kind':item.kind,className:detail?'hm-app-detail':'hm-app-card'},detail&&mainPage?null:h('h3',null,item.displayName),detail&&mainPage?null:h('p',null,item.summary),
|
|
1099
|
+
detail?h('div',{className:'hm-app-detail-content'},h('h4',null,t('关于这个应用')),h('p',null,item.description||item.summary),h('h4',null,t('可信度')),h('div',{className:'hm-credibility'},...credibilityFacts(credibility?.itemId===item.id?credibility:null).map(row=>h('div',{key:row.id},h('span',null,t(row.label)),h('strong',{'data-hanamesh-credibility':row.id},row.value===null?t('暂无'):row.value.toLocaleString()),h('small',null,row.value===null?t('当前暂无数据'):t('公开统计'))))),h('p',{className:'hm-market-state'},t('可信度数据缺失不代表 0;本机安装状态与平台采用度分别显示。')),credibility?.checkedAt?h('small',null,t('信号更新:')+credibility.checkedAt):null,credibility?.asOf?h('small',null,t('使用人数更新:')+credibility.asOf):null,h('h4',null,t('来源与安装目标')),h('p',null,item.package?.name??t('仅收录,不可安装')),item.installTarget?h('code',null,item.installTarget.source==='github'?'https://github.com/'+item.installTarget.repo+' @ '+item.installTarget.ref:item.installTarget.name+' @ '+item.installTarget.version):null,...(item.source?.sourceLinks??[]).map((link,index)=>h('p',{key:index},h('a',{href:link.sourceUrl,target:'_blank',rel:'noopener noreferrer'},link.sourceId+' · '+link.sourceRef))),catalogWebsiteLink(snapshot?.source,item)?h('a',{href:catalogWebsiteLink(snapshot?.source,item),target:'_blank',rel:'noopener noreferrer',className:'hm-website-link'},t('在网站查看')):null):null,
|
|
1058
1100
|
h('small',null,t(KIND_TEXT[item.kind])+' · '+(item.publisher?.name??t('未知发布者'))+((item.categories??[]).length?' · '+item.categories.join('、'):'')),
|
|
1059
|
-
state,action,op&&!busy?operationNotice(op):null);};
|
|
1101
|
+
state,h('div',{className:'hm-market-actions'},action,!detail?h('button',{type:'button',onClick:()=>setDetailId(item.id)},t('详情')):null),op&&!busy?operationNotice(op):null);};
|
|
1102
|
+
const detailItem=items.find(item=>item.id===detailId);
|
|
1060
1103
|
const catalogSection=receipt?h('div',{className:'hm-app-frame'},h('div',null,h('strong',null,receipt.instance.appId),h('button',{type:'button',disabled:closing,onClick:()=>void close()},closing?t('关闭中…'):t('关闭视图'))),h('iframe',{ref:frame,src:receipt.uiUrl,title:receipt.instance.appId,sandbox:'allow-forms allow-modals allow-popups allow-same-origin allow-scripts'})):
|
|
1061
|
-
h('div',{className:'hm-library-grid',key:'catalog'},...items.map(card));
|
|
1062
|
-
const moreButton=nextCursor&&!receipt?h('button',{type:'button',className:'hm-library-more',key:'more',onClick:()=>void more()},t('更多')):null;
|
|
1063
|
-
const installedSection=!receipt&&(installed.length||plugins.length)?h('section',{className:'hm-market-installed',key:'installed'},h('h2',null,t('已安装')),h('ul',null,
|
|
1064
|
-
...installed.map(row=>h('li',{key:'app:'+row.packageName,'data-hanamesh-installed':row.packageName},t('应用 · ')+(row.name??row.packageName)+' · '+row.packageName+' '+(row.version??'')+' · '+(t(STATE_TEXT[row.state]??row.state)))),
|
|
1104
|
+
detailItem?h('div',{key:'detail'},h('button',{type:'button',onClick:()=>setDetailId(null)},t('返回应用库')),card(detailItem,true)):h('div',{className:'hm-library-grid',key:'catalog'},...items.map(item=>card(item)),snapshot&&items.length===0?h('p',{role:'status'},t('没有找到符合条件的应用。')):null);
|
|
1105
|
+
const moreButton=nextCursor&&!receipt&&!detailItem?h('button',{type:'button',className:'hm-library-more',key:'more',onClick:()=>void more()},t('更多')):null;
|
|
1106
|
+
const installedSection=!receipt&&!detailItem&&(installed.length||plugins.length)?h('section',{className:'hm-market-installed',key:'installed'},h('h2',null,t('已安装')),h('ul',null,
|
|
1107
|
+
...installed.map(row=>h('li',{key:'app:'+row.packageName,'data-hanamesh-installed':row.packageName},t('应用 · ')+(row.name??row.packageName)+' · '+row.packageName+' '+(row.version??'')+' · '+(t(STATE_TEXT[row.state]??row.state)),row.state==='registered'?openButton(row):null)),
|
|
1065
1108
|
...plugins.filter(row=>!PROTECTED.has(row.packageName)).map(row=>h('li',{key:'plugin:'+row.packageName,'data-hanamesh-installed':row.packageName},t('插件 · ')+row.packageName+' '+(row.version??'')+' · '+(t(STATE_TEXT[row.state]??row.state))+(row.bundle&&!row.active&&row.state==='installed'?t('(未启用为 profile 层)'):''),
|
|
1066
1109
|
row.state==='installed'&&!PROTECTED.has(row.packageName)&&!pending['plugin:'+row.packageName]?h('button',{type:'button',onClick:()=>void operate('/hanamesh/library/plugins/uninstall',{packageName:row.packageName},'plugin:'+row.packageName,t('卸载'))},t('卸载')):pending['plugin:'+row.packageName]?operationNotice(pending['plugin:'+row.packageName]):null)))):null;
|
|
1067
1110
|
// The seat owner may ask for a subsection to come first; `installed` is the only one it names today.
|
|
1068
|
-
const body=preferredSubsectionId==='installed'?[installedSection,catalogSection,moreButton]:[catalogSection,moreButton,installedSection];
|
|
1069
|
-
|
|
1070
|
-
|
|
1111
|
+
const body=mainPage?(libraryTab==='installed'?[installedSection??h('p',{role:'status'},t('还没有已安装项。'))]:[catalogSection,moreButton]):preferredSubsectionId==='installed'?[installedSection,catalogSection,moreButton]:[catalogSection,moreButton,installedSection];
|
|
1112
|
+
const reviewing=mainPage&&target!==null,detailing=mainPage&&Boolean(detailItem)&&!reviewing;
|
|
1113
|
+
return h('section',{className:(embedded?'hm-market hm-market-embedded':'hm-library-overlay hm-market')+(mainPage?' hm-apphost-page':'')},
|
|
1114
|
+
mainPage?h('div',{className:'hm-apphost-breadcrumb'},t(detailing?'HanaMesh / 应用详情':'HanaMesh / 应用库')):null,
|
|
1115
|
+
h('header',null,h('div',null,mainPage?h('div',{className:'hm-apphost-eyebrow'},reviewing?'APPHOST / INSTALL':detailing?'APPLICATION / DETAIL':'APPHOST / LIBRARY'):null,h('h1',null,mainPage?(detailing?detailItem.displayName:t(reviewing?'安装之前,先看清楚。':'好工具,让想法走得更远。')):t('应用库'))),mainPage?h('button',{type:'button',onClick:()=>{try{pageLayout.selectPanel('hanamesh-environment');}catch{setError(new Error('CORE_PAGE_UNAVAILABLE'));}}},t('环境与来源')):embedded?null:h('button',{type:'button',onClick:()=>setLibraryVisible(false)},t('关闭'))),
|
|
1116
|
+
mainPage?h('p',null,detailing?detailItem.summary:t('发现适合你的工具,查看来源后再安装;账号与模型授权分别管理。')):null,
|
|
1071
1117
|
h(MarketConflictNotice,null),h(HandoffIssueNotice,null),
|
|
1072
1118
|
snapshot?.restartBaseline?.known===false?h('p',{role:'alert',className:'hm-library-failed'},t('启动时没能读取 profile(')+snapshot.restartBaseline.code+t('),「需重启」状态无法判断,下面的状态都按已加载显示')):null,
|
|
1073
1119
|
verification===true?h('section',{className:'hm-install-target','aria-label':t('安装目标开发小界面')},h('h2',null,t('安装目标')),
|
|
@@ -1075,14 +1121,15 @@ h('div',{className:'hm-source-add'},h('input',{value:draft,placeholder:'https://
|
|
|
1075
1121
|
h('form',{onSubmit:event=>{event.preventDefault();void openInstallTarget({...(targetItemId.trim()?{itemId:targetItemId.trim()}:{}),...(targetPackageName.trim()?{packageName:targetPackageName.trim()}:{} )}).catch(()=>{});}},
|
|
1076
1122
|
h('input',{'aria-label':t('目录条目 ID'),value:targetItemId,placeholder:t('itemId(可选)'),onChange:event=>setTargetItemId(event.target.value)}),
|
|
1077
1123
|
h('input',{'aria-label':t('包名'),value:targetPackageName,placeholder:t('packageName(可选)'),onChange:event=>setTargetPackageName(event.target.value)}),h('button',{type:'submit'},t('查看安装目标')))):null,
|
|
1078
|
-
target?h('div',{role:target.status==='confirmation-required'?'dialog':'status','aria-label':t('市场安装确认')},
|
|
1124
|
+
target?h('div',{role:target.status==='confirmation-required'?'dialog':'status','aria-label':t('市场安装确认'),className:'hm-install-target hm-install-review'},h('h2',null,t('安装确认')),
|
|
1079
1125
|
target.item?h('dl',null,h('dt',null,t('插件名')),h('dd',null,target.item.displayName),h('dt',null,t('包名')),h('dd',null,target.item.package.name),h('dt',null,t('来源')),h('dd',null,target.source?.kind==='fixture'?t('测试目录(fixture)'):target.source?.manifestUrl??t('当前市场目录'))):null,
|
|
1126
|
+
target.item?.installTarget?.source==='npm'?h('dl',{'data-npm-confirmation':true},h('dt',null,t('精确版本')),h('dd',null,target.item.installTarget.version)):null,
|
|
1080
1127
|
target.item?.installTarget?.source==='github'?h('dl',{'data-github-confirmation':true},h('dt',null,t('来源仓库')),h('dd',null,'https://github.com/'+target.item.installTarget.repo),h('dt',null,t('固定 commit')),h('dd',null,target.item.installTarget.ref),...(target.item.installTarget.path?[h('dt',null,t('仓库子目录')),h('dd',null,target.item.installTarget.path)]:[])):null,
|
|
1081
1128
|
target.item?.installTarget?(target.item.installTarget.allowBuild.length?h('div',{role:'alert','data-build-warning':true},h('strong',null,t('会在本机执行构建脚本')),h('p',null,target.item.installTarget.allowBuild.join(', ')),h('p',null,t('确认后将按名单允许构建;pnpm 会把许可保存到当前 profile。'))):h('p',null,t('未授予构建脚本许可'))):null,
|
|
1082
1129
|
target.item?.suiteUpgrade?h('ul',null,...target.item.suiteUpgrade.components.map(c=>h('li',{key:c.name},c.label+':'+c.from+' → '+c.to)),h('li',null,t('确认后升级 HanaMesh 总包及其锁定组件;需要完全退出并重新打开客户端。'))):null,
|
|
1083
1130
|
target.item?.suiteUpgrade?.registry?h('p',null,'Registry:'+target.item.suiteUpgrade.registry):null,
|
|
1084
1131
|
...(target.item?.suiteUpgrade?.blocked??target.details?.blocked??[]).map((row,i)=>h('p',{key:'suite-blocked-'+i,role:'status'},row.code+(row.moduleName?' · '+row.moduleName:'')+(row.bundleVersion?' · HanaMesh '+row.bundleVersion:''))),
|
|
1085
|
-
target.status==='confirmation-required'?h('div',null,h('button',{type:'button',onClick:()=>void confirmTarget()},t('确认安装')),h('button',{type:'button',onClick:()=>{targetSequence++;void reportHandoff(targetSelection?.handoffId,'cancelled');setTarget({...targetSelection,status:'cancelled'});}},t('取消'))):
|
|
1132
|
+
target.status==='confirmation-required'?h('div',null,h('label',{className:'hm-install-ack'},h('input',{type:'checkbox','aria-label':t('我已核对来源与安装目标'),checked:acceptedTarget===target,onChange:event=>setAcceptedTarget(event.currentTarget.checked?target:null)}),t('我已核对来源与安装目标')),h('button',{type:'button',disabled:acceptedTarget!==target,onClick:()=>void confirmTarget()},t('确认安装')),h('button',{type:'button',onClick:()=>{targetSequence++;void reportHandoff(targetSelection?.handoffId,'cancelled');setTarget({...targetSelection,status:'cancelled'});}},t('取消'))):
|
|
1086
1133
|
target.status==='failed'?FailureNotice({error:target.diagnostic??target.error}):h('p',{role:['readback-failed','suite-upgrade-failed'].includes(target.status)?'alert':'status'},({resolving:t('正在查询市场目录…'),installing:t('安装中…'),installed:t('安装完成,请查看已安装列表'),'installed-live':t('安装完成,已加载,可以直接使用'),cancelled:t('已取消,未安装'),'result-unavailable':t('安装结果不可追溯,请查看已安装列表;不会自动重试'),'already-installed':t('已安装,无需重复安装'),failed:t('未安装:')+(target.error??pending[target.item?.id]?.text??t('安装失败')),'readback-failed':target.error,'suite-upgrade-failed':suiteTargetFailure(target)})[target.status]),
|
|
1087
1134
|
target.item?.source?h('div',{'aria-label':t('目录条目来源')},h('p',null,target.item.source.sourceId+' · '+target.item.source.sourceRef),...(target.item.source.sourceLinks??[]).map((link,index)=>h('p',{key:index},h('a',{href:link.sourceUrl,target:'_blank',rel:'noopener noreferrer'},link.sourceId+' · '+link.sourceRef),h('small',null,' '+link.firstSeenAt+' / '+link.lastSeenAt)))):null,
|
|
1088
1135
|
target.item?operationNotice(pending[target.item.id]):null,
|
|
@@ -1093,11 +1140,13 @@ h('div',{className:'hm-source-add'},h('input',{value:draft,placeholder:'https://
|
|
|
1093
1140
|
h('textarea',{'aria-label':t('合约样例输入'),value:verificationInput,onChange:event=>setVerificationInput(event.target.value)}),
|
|
1094
1141
|
h('button',{type:'button',disabled:verifying,onClick:()=>void verify()},t('调用合约')),
|
|
1095
1142
|
h('pre',{'aria-label':t('合约调用输出')},verificationOutput===null?t('尚未调用'):JSON.stringify(verificationOutput,null,2))):null,
|
|
1096
|
-
h('div',{className:'hm-library-toolbar'},h('input',{type:'search',placeholder:t('搜索应用与插件'),value:query,onChange:e=>setQuery(e.target.value),onKeyDown:e=>{if(e.key==='Enter')void load();}}),h('button',{type:'button',onClick:()=>void load()},t('搜索')),
|
|
1143
|
+
reviewing||detailing?null:h('div',{className:'hm-library-toolbar'},h('input',{type:'search',placeholder:t('搜索应用与插件'),value:query,onChange:e=>setQuery(e.target.value),onKeyDown:e=>{if(e.key==='Enter')void load();}}),h('button',{type:'button',onClick:()=>void load()},t('搜索')),
|
|
1097
1144
|
h('select',{'aria-label':t('类别'),value:filter,onChange:e=>setFilter(e.target.value)},...filters.map(([value,label])=>h('option',{key:value||'all',value},t(label))))),
|
|
1145
|
+
mainPage&&!reviewing&&!detailing?h('div',{role:'tablist','aria-label':t('应用筛选'),className:'hm-library-tabs'},...[["all","全部应用"],["installed","已安装"]].map(([value,label])=>h('button',{type:'button',role:'tab',key:value,'aria-selected':libraryTab===value,onClick:()=>{setLibraryTab(value);setDetailId(null);}},t(label)))):null,
|
|
1146
|
+
reviewing&&target.status!=='confirmation-required'&&target.status!=='installing'?h('button',{type:'button',onClick:()=>setTarget(null)},t('返回应用库')):null,
|
|
1098
1147
|
restartRequired?h(RestartNotice,null):null,
|
|
1099
1148
|
error?FailureNotice({error}):null,
|
|
1100
|
-
...body);
|
|
1149
|
+
...(reviewing?[]:body),mainPage?h('footer',null,'HanaMesh · Let every use bloom.'):null);
|
|
1101
1150
|
}
|
|
1102
1151
|
/** The `market` seat object read by the shell's Extension Management panel. */
|
|
1103
1152
|
function createMarketSeat({verification=false}={}){
|
|
@@ -1132,11 +1181,57 @@ h('div',{className:'hm-source-add'},h('input',{value:draft,placeholder:'https://
|
|
|
1132
1181
|
})();
|
|
1133
1182
|
return()=>{released=true;setMarketConflict('');setEntryVisible(true);dispose?.();};
|
|
1134
1183
|
}
|
|
1184
|
+
// These four counts are not present in the current catalog-view contract.
|
|
1185
|
+
// Never substitute local package state, publisher badges or guessed zeroes.
|
|
1186
|
+
// WX11/12: the existing Web detail projects Host 0.6.54+ githubStars as stars.
|
|
1187
|
+
// The incremental signals feed is a server ingestion API, never a browser scan.
|
|
1188
|
+
const credibilityCount=value=>Number.isSafeInteger(value)&&value>=0?value:null;
|
|
1189
|
+
const credibilityTime=value=>typeof value==='string'&&Number.isFinite(Date.parse(value))?value:null;
|
|
1190
|
+
function credibilityFacts(facts){return[
|
|
1191
|
+
{id:'stars',label:'GitHub 星数',value:credibilityCount(facts?.signals?.stars)},
|
|
1192
|
+
{id:'npm30',label:'npm 下载 / 30 天',value:credibilityCount(facts?.signals?.npmDownloads30d)},
|
|
1193
|
+
{id:'installs',label:'DSH 安装 / 30 天',value:credibilityCount(facts?.signals?.storeInstalls30d)},
|
|
1194
|
+
{id:'users',label:'使用人数 / 30 天',value:credibilityCount(facts?.popularity?.users30d)}
|
|
1195
|
+
].map(row=>({...row,reason:row.value===null?'catalog-field-unavailable':null}));}
|
|
1196
|
+
async function readCredibility(source,item,fetcher=globalThis.fetch){
|
|
1197
|
+
const link=catalogWebsiteLink(source,item),empty={itemId:item?.id,signals:null,popularity:null,checkedAt:null,asOf:null};
|
|
1198
|
+
if(!link||typeof fetcher!=='function')return empty;
|
|
1199
|
+
const origin=new URL(link).origin,id=encodeURIComponent(item.id),controller=new AbortController(),timer=setTimeout(()=>controller.abort(),8000);
|
|
1200
|
+
const read=async path=>{const response=await fetcher(origin+path,{method:'GET',credentials:'omit',redirect:'error',cache:'no-store',signal:controller.signal,headers:{accept:'application/json'}});if(!response.ok)throw Error('CREDIBILITY_UNAVAILABLE');const value=await response.json();if(value?.hanaId!==item.id)throw Error('CREDIBILITY_ID_MISMATCH');return value;};
|
|
1201
|
+
try{const [detail,popularity]=await Promise.allSettled([read('/api/v2/hanas/'+id),read('/hm/v1/custody/hanas/'+id+'/popularity')]);
|
|
1202
|
+
const signals=detail.status==='fulfilled'?detail.value.signals:null,pop=popularity.status==='fulfilled'?popularity.value:null;
|
|
1203
|
+
return {...empty,signals,popularity:pop,checkedAt:credibilityTime(signals?.checkedAt),asOf:credibilityTime(pop?.asOf)};
|
|
1204
|
+
}finally{clearTimeout(timer);}
|
|
1205
|
+
}
|
|
1206
|
+
function catalogWebsiteLink(source,item){
|
|
1207
|
+
if(source?.kind==='fixture'||typeof source?.manifestUrl!=='string'||typeof item?.id!=='string')return null;
|
|
1208
|
+
try{const url=new URL(source.manifestUrl);if(url.pathname!=='/catalog-source.json'||url.searchParams.get('view')!=='hanamesh')return null;return new URL('/hana/'+encodeURIComponent(item.id),url.origin).href;}catch{return null;}
|
|
1209
|
+
}
|
|
1210
|
+
function ConnectionTabs({selected,navigate}){return h('nav',{className:'hm-connection-tabs','aria-label':t('设置与连接')},...[["hanamesh-account","账号与设备"],["hanamesh-providers","模型连接"],["hanamesh-environment","环境与来源"]].map(([id,label])=>h('button',{type:'button',key:id,'aria-current':id===selected?'page':undefined,onClick:()=>navigate(id)},t(label))));}
|
|
1211
|
+
function ProvidersPage({navigate}){useLocale();const[error,setError]=React.useState(false);const go=id=>{try{navigate(id);setError(false);}catch{setError(true);}};return h('div',{className:'hm-apphost-page'},h('div',{className:'hm-apphost-breadcrumb'},t('HanaMesh / 模型连接')),h('header',null,h('div',{className:'hm-apphost-eyebrow'},'MODELS / PROVIDERS'),h('h1',null,t('连接模型,保持掌控。')),h('p',null,t('登录账号不提供模型凭据。沿用宿主连接,按应用分别查看授权。'))),h(ConnectionTabs,{selected:'hanamesh-providers',navigate:go}),error?h('p',{role:'alert'},t('对应页面暂不可用,请确认 HanaMesh Core 已启用。')):null,h('div',{className:'hm-apphost-card'},h(ProvidersSection)),h('footer',null,'HanaMesh · Let every use bloom.'));}
|
|
1212
|
+
function EnvironmentContent(){useLocale();const[expanded,setExpanded]=React.useState({});return h('div',{className:'hm-apphost-environment'},h('section',{className:'hm-apphost-card'},h(NodeRuntimeSection)),...[["sources","目录来源",LibrarySourcesSection],["suite","套件安装包来源",SuiteSourceSettingsSection],["handoff","网站安装交接",HandoffSettingsSection]].map(([id,label,Component])=>h('details',{key:id,className:'hm-apphost-card',onToggle:event=>{const open=event.currentTarget.open;setExpanded(previous=>({...previous,[id]:open}));}},h('summary',null,t(label)),expanded[id]?h(Component):h('p',null,t('展开后读取当前设置;不会自动保存或安装。')))));}
|
|
1213
|
+
function registerAppHostPages(ctx){
|
|
1214
|
+
const previous=pageLayout;pageLayout=ctx.layout;const releases=[];
|
|
1215
|
+
releases.push(ctx.slots.inject('main',()=>ctx.slots.register({name:'main',key:'hanamesh-library'},()=>h(LibraryOverlay,{embedded:true,mainPage:true}))));
|
|
1216
|
+
releases.push(ctx.slots.inject('main',()=>ctx.slots.register({name:'main',key:'hanamesh-providers'},()=>h(ProvidersPage,{navigate:id=>ctx.layout.selectPanel(id)}))));
|
|
1217
|
+
releases.push(ctx.slots.inject('sidebar.panellist',()=>ctx.slots.register({name:'sidebar.panellist',id:'hanamesh-library',order:-20,label:t('应用库')},AppLibraryIcon)));
|
|
1218
|
+
return()=>{for(const dispose of releases.reverse())dispose();if(pageLayout===ctx.layout)pageLayout=previous;};
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
function AppLibraryIcon({size=20}){return h('span',{'aria-hidden':true,style:{display:'block',width:size,height:size,background:'currentColor',maskImage:'url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMCIgaGVpZ2h0PSIyMCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGNsYXNzPSJsdWNpZGUgbHVjaWRlLWdyaWQyeDIgbHVjaWRlLWdyaWQtMngyIiBhcmlhLWhpZGRlbj0idHJ1ZSI+PHBhdGggZD0iTTEyIDN2MTgiPjwvcGF0aD48cGF0aCBkPSJNMyAxMmgxOCI+PC9wYXRoPjxyZWN0IHg9IjMiIHk9IjMiIHdpZHRoPSIxOCIgaGVpZ2h0PSIxOCIgcng9IjIiPjwvcmVjdD48L3N2Zz4=")',maskSize:'contain',maskRepeat:'no-repeat'}});}
|
|
1222
|
+
|
|
1223
|
+
const appHostPageStyles=`
|
|
1224
|
+
.hm-apphost-page{height:100%;overflow:auto;box-sizing:border-box;padding:36px 38px;max-width:1360px;margin:auto;color:var(--dsw-alias-label-primary,CanvasText);background:var(--dsw-alias-bg-base,Canvas);font:14px/1.55 var(--dsw-font-family,system-ui)}.hm-apphost-page *{box-sizing:border-box}.hm-apphost-page h1{font-size:32px;line-height:1.25;letter-spacing:-.025em;margin:0}.hm-apphost-page h2{font-size:17px}.hm-apphost-page p{color:var(--dsw-alias-label-secondary);line-height:1.7}.hm-apphost-breadcrumb{font-size:12px;color:var(--dsw-alias-label-tertiary);margin-bottom:28px}.hm-apphost-eyebrow{font-size:11px;font-weight:700;letter-spacing:.16em;color:var(--dsw-alias-brand-primary);margin-bottom:10px}.hm-apphost-page>header{margin-bottom:28px}.hm-apphost-page button,.hm-apphost-page select,.hm-apphost-page input{font:inherit;color:inherit;min-height:44px;padding:10px 14px;border:1px solid var(--dsw-alias-border-l2);border-radius:10px;background:var(--dsw-alias-bg-layer-3);max-width:100%}.hm-apphost-page button{cursor:pointer}.hm-apphost-page button:disabled{opacity:.5;cursor:default}.hm-apphost-page :focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:3px}.hm-connection-tabs{display:flex;gap:8px;flex-wrap:wrap;margin:28px 0 24px}.hm-connection-tabs [aria-current=page]{color:var(--dsw-alias-brand-primary);border-color:var(--dsw-alias-brand-primary)}.hm-apphost-card{padding:22px;border:1px solid var(--dsw-alias-border-l1);border-radius:18px;background:var(--dsw-alias-bg-layer-2);min-width:0}.hm-apphost-environment{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:20px;align-items:start}.hm-apphost-environment>details summary{cursor:pointer;font-weight:600;min-height:28px}.hm-apphost-page .hm-library-grid{grid-template-columns:repeat(3,minmax(0,1fr));gap:16px}.hm-apphost-page .hm-library-grid article{padding:22px;border-radius:18px;background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-border-l1);display:flex;flex-direction:column;gap:16px}.hm-apphost-page .hm-library-grid h3{margin:0;font-size:16px}.hm-apphost-page .hm-market-actions{margin-top:auto}.hm-apphost-page .hm-library-toolbar{gap:12px;margin:24px 0}.hm-apphost-page .hm-app-detail{padding:22px;border:1px solid var(--dsw-alias-border-l1);border-radius:18px;background:var(--dsw-alias-bg-layer-2);margin:20px 0;overflow-wrap:anywhere}.hm-apphost-page .hm-app-detail h3{font-size:28px;margin:0 0 12px}.hm-credibility{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:12px}.hm-credibility>div{padding:16px;border:1px solid var(--dsw-alias-border-l1);border-radius:14px;background:var(--dsw-alias-bg-layer-1)}.hm-credibility span,.hm-credibility small{display:block;font-size:12px;color:var(--dsw-alias-label-tertiary)}.hm-credibility strong{display:block;font:26px/1.8 var(--dsh-font-family-code-default,monospace)}.hm-apphost-page .hm-install-review{padding:22px;border-radius:18px;background:var(--dsw-alias-bg-layer-2);margin:24px 0}.hm-install-ack{display:flex;align-items:center;gap:10px;margin:18px 0}.hm-install-ack input{width:20px;min-height:20px;flex:none;accent-color:var(--dsw-alias-brand-primary)}.hm-apphost-page .hm-website-link{display:inline-block;min-height:44px;color:var(--dsw-alias-brand-primary);padding:10px 0}.hm-library-tabs{display:flex;gap:5px;padding:4px;border:1px solid var(--dsw-alias-border-l1);border-radius:11px;width:max-content;margin:20px 0}.hm-library-tabs [aria-selected=true]{background:var(--dsw-alias-bg-layer-3);border-color:var(--dsw-alias-brand-primary)}.hm-apphost-page footer{font-size:11px;color:var(--dsw-alias-label-tertiary);margin-top:26px}
|
|
1225
|
+
@media(max-width:1000px){.hm-apphost-page .hm-library-grid{grid-template-columns:repeat(2,minmax(0,1fr))}}@media(max-width:640px){.hm-apphost-page>header{display:block}.hm-apphost-page>header button{margin-top:16px}.hm-apphost-page{padding:24px 18px}.hm-apphost-page h1{font-size:26px}.hm-apphost-page .hm-library-grid,.hm-apphost-environment,.hm-credibility{grid-template-columns:minmax(0,1fr)}.hm-apphost-card{padding:18px}}
|
|
1226
|
+
`;
|
|
1227
|
+
|
|
1135
1228
|
const name='hanamesh-app-host-client',inject=['slots','locale'];
|
|
1136
1229
|
// No cross-plugin reads here: the browser-side cordis context never carries host services such as hanameshCore
|
|
1137
1230
|
// (dsh-client-modules boots its own root context); Core status lives in Core's own Settings section.
|
|
1138
1231
|
function localizedSection(ctx,id,order,label,component){return ctx.slots.inject('settings.section',()=>{let dispose;const refresh=()=>{dispose?.();dispose=ctx.slots.register({name:'settings.section',id,order,label:t(label)},component);};refresh();const unsubscribe=localeService?.subscribe(refresh);return()=>{unsubscribe?.();dispose?.();};});}
|
|
1139
|
-
function apply(ctx,{verification=false}={}){localeService=ctx.locale??null;if(localeService)ctx.effect(()=>localeService.register('hanamesh.apphost',COPY),'hanamesh-app-host:locale');ctx.effect(()=>{const style=document.createElement('style');style.textContent='.hm-node-runtime{display:grid;gap:12px;max-width:900px;color:inherit}.hm-node-fields{display:grid;gap:10px}.hm-node-fields label{display:grid;gap:6px}.hm-node-runtime input,.hm-node-runtime select,.hm-node-runtime button{font:inherit;color:inherit;background:transparent;border:1px solid color-mix(in srgb,currentColor 30%,transparent);border-radius:8px;min-height:44px;padding:8px 12px;box-sizing:border-box;max-width:100%}.hm-node-runtime button{cursor:pointer}.hm-node-runtime button:hover:not(:disabled){background:color-mix(in srgb,currentColor 9%,transparent)}.hm-node-runtime button:focus-visible{outline:2px solid currentColor;outline-offset:2px}.hm-node-runtime button:disabled{opacity:.5;cursor:default}.hm-node-confirm,.hm-node-progress{border:1px solid color-mix(in srgb,currentColor 25%,transparent);border-radius:12px;padding:16px}.hm-node-confirm dl{display:grid;grid-template-columns:100px minmax(0,1fr);gap:8px}.hm-node-runtime dd{margin:0}.hm-node-runtime code,.hm-node-runtime dd{overflow-wrap:anywhere}.hm-node-runtime progress{width:100%}.hm-node-runtime [role=alert]{border-left:3px solid currentColor;padding-left:10px}'+'.hm-providers,.hm-library-sources{display:grid;gap:16px}.hm-providers table{width:100%;border-collapse:collapse}.hm-providers th,.hm-providers td{padding:8px;text-align:left;border-bottom:1px solid color-mix(in srgb,currentColor 14%,transparent)}.hm-provider-card{padding:16px;border:1px solid color-mix(in srgb,currentColor 18%,transparent);border-radius:12px}.hm-provider-row{display:grid;grid-template-columns:minmax(120px,1fr) auto minmax(180px,1fr) auto;gap:8px;align-items:center;margin:8px 0}.hm-source-row{display:grid;grid-template-columns:minmax(0,1fr) repeat(4,auto);gap:8px}.hm-source-row code{overflow-wrap:anywhere}.hm-source-add{display:flex;gap:8px}.hm-source-add input{flex:1}.hm-library-overlay{position:absolute;inset:0;z-index:30;pointer-events:auto;overflow:auto;padding:24px;background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary)}.hm-library-overlay button,.hm-library-overlay input,.hm-library-overlay select,.hm-market-embedded button,.hm-market-embedded input,.hm-market-embedded select{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-border-l2)}.hm-library-overlay>header{display:flex;justify-content:space-between;align-items:center}.hm-library-toolbar{display:flex;gap:8px;align-items:center;margin:8px 0}.hm-library-toolbar input[type=search]{flex:1;padding:6px 10px}.hm-library-more{margin:12px auto;display:block}.hm-library-failed{color:var(--dsw-alias-state-error-primary);margin:0}.hm-library-ok{color:var(--dsw-alias-state-success-primary);margin:0}.hm-restart-notice{padding:10px 12px;border:1px solid var(--dsw-alias-state-warn-primary);border-radius:8px;background:var(--dsw-alias-state-warn-tertiary);margin:8px 0}.hm-restart-link{margin-left:4px;font-weight:600}.hm-market-state{font-size:12px;color:var(--dsw-alias-label-secondary)}.hm-market-actions{display:flex;gap:8px;flex-wrap:wrap}.hm-market-embedded{display:block}.hm-market-embedded>header{display:flex;justify-content:space-between;align-items:center}.hm-market-conflict{padding:10px 12px;border:1px solid var(--dsw-alias-state-business-primary);border-radius:8px;background:color-mix(in srgb,var(--dsw-alias-state-business-primary) 8%,transparent);margin:8px 0}.hm-install-target{display:grid;gap:8px;padding:16px;border:1px solid var(--dsw-alias-border-l2);border-radius:12px;margin:12px 0}.hm-install-target form{display:flex;gap:8px;flex-wrap:wrap}.hm-install-target input{flex:1;min-width:180px;padding:6px 10px}.hm-install-target dl{display:grid;grid-template-columns:80px 1fr;gap:6px}.hm-install-target dd{margin:0;overflow-wrap:anywhere}.hm-market-installed{margin-top:24px}.hm-market-installed ul{padding-left:18px;display:grid;gap:6px}.hm-library-toolbar select{padding:6px 10px}.hm-library-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:16px}.hm-library-grid article{display:grid;gap:10px;padding:16px;border:1px solid color-mix(in srgb,currentColor 16%,transparent);border-radius:12px}.hm-app-frame{display:grid;grid-template-rows:auto 1fr;height:calc(100vh - 130px)}.hm-market-embedded>.hm-app-frame{position:fixed;inset:0;z-index:1001;height:100vh;box-sizing:border-box;background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);padding:12px 16px}.hm-app-frame>div{display:flex;justify-content:space-between}.hm-app-frame iframe{width:100%;height:100%;border:0}';style.textContent+='/* Only AppHost surfaces: never resize the host settings shell. */\n.hm-node-runtime,.hm-library-sources,.hm-providers,.hm-market{min-width:0;width:100%;max-width:100%;box-sizing:border-box;overflow-wrap:anywhere}\n.hm-node-runtime>*,.hm-library-sources>*,.hm-node-fields>*,.hm-handoff-settings>*,.hm-library-toolbar>*{min-width:0;max-width:100%;box-sizing:border-box}\n.hm-library-sources input,.hm-library-sources textarea,.hm-library-sources select,.hm-node-runtime input,.hm-node-runtime select{min-width:0;width:100%;max-width:100%;box-sizing:border-box;font:inherit;color:inherit;background:var(--dsw-alias-bg-layer-2)}\n.hm-library-sources button,.hm-market button{min-height:44px;max-width:100%;white-space:normal;font:inherit;color:inherit;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:8px 12px}\n.hm-library-sources button:focus-visible,.hm-market button:focus-visible{outline:2px solid currentColor;outline-offset:2px}\n.hm-handoff-settings{display:grid;gap:12px}.hm-handoff-settings label{display:grid;gap:6px}.hm-handoff-settings input[type=checkbox]{width:auto}\n.hm-node-confirm,.hm-node-progress{min-width:0;max-width:100%;box-sizing:border-box}\n.hm-failure pre{max-width:100%;white-space:pre-wrap;overflow-wrap:anywhere;font-size:12px}.hm-failure summary{cursor:pointer}.hm-failure p{margin:8px 0}\n.hm-library-toolbar,.hm-source-add{flex-wrap:wrap}.hm-library-toolbar input[type=search]{min-width:0;width:100%;flex:1 1 180px}\n.hm-library-grid{grid-template-columns:repeat(auto-fill,minmax(min(240px,100%),1fr))}\n.hm-providers{overflow-x:auto}.hm-source-row{grid-template-columns:minmax(0,1fr);gap:6px}\n@media(max-width:640px){.hm-library-overlay{padding:12px}.hm-node-confirm{padding:10px}.hm-node-confirm dl,.hm-install-target dl{grid-template-columns:minmax(0,1fr)}.hm-node-confirm dt{font-weight:600}.hm-node-confirm dd{margin-bottom:6px}.hm-library-toolbar select{width:100%}}\n';style.textContent+='\n.hm-provider-records td{overflow-wrap:anywhere}.hm-providers select,.hm-providers input{min-width:0;max-width:100%;width:100%;box-sizing:border-box}.hm-provider-records details>summary{cursor:pointer;min-height:44px;display:flex;align-items:center}.hm-provider-records pre{white-space:pre-wrap;overflow-wrap:anywhere;font-size:12px}\n@media(max-width:640px){.hm-providers{overflow:visible}.hm-provider-records,.hm-provider-records tbody{display:block}.hm-provider-records thead{display:none}.hm-provider-records tr{display:grid;grid-template-columns:minmax(0,1fr);gap:12px;padding:12px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;margin:10px 0}.hm-provider-records td{display:block;min-width:0;padding:0;border:0}.hm-provider-records td::before{content:attr(data-label);display:block;font-weight:600;margin-bottom:4px}.hm-provider-records td:empty{display:none}}\n';style.textContent+="\n.hm-providers,.hm-library-sources,.hm-node-runtime{overflow-wrap:normal;word-break:normal}\n.hm-providers h2,.hm-library-sources h2,.hm-node-runtime h2{margin:0;font-weight:600;line-height:1.5}\n.hm-providers p,.hm-library-sources p,.hm-node-runtime p{margin:0;line-height:1.6}\n.hm-suite-fields,.hm-suite-fields>label,.hm-handoff-settings,.hm-source-add{display:grid;grid-template-columns:minmax(0,1fr);gap:10px;min-width:0}\n.hm-suite-fields>*,.hm-source-add>*,.hm-providers>*,.hm-library-sources details,.hm-node-runtime details{min-width:0;max-width:100%;box-sizing:border-box}\n.hm-library-sources input,.hm-library-sources textarea,.hm-library-sources select,.hm-suite-fields select,.hm-providers select,.hm-providers input{display:block;width:100%;min-width:0;max-width:100%;box-sizing:border-box;min-height:44px;padding:8px;font:inherit;border:1px solid var(--dsw-alias-border-l2);border-radius:8px}\n.hm-library-sources input[type=checkbox]{display:inline-block;width:20px;min-height:20px;max-width:20px}\n.hm-library-sources summary,.hm-providers summary,.hm-node-runtime summary{min-height:44px;display:flex;align-items:center;cursor:pointer}\n.hm-library-sources code,.hm-library-sources dd,.hm-providers pre{overflow-wrap:anywhere;white-space:pre-wrap}\n.hm-handoff-settings>button,.hm-suite-fields>button{width:100%}\n@media(max-width:640px){.hm-provider-records tr{padding:8px}.hm-node-fields{grid-template-columns:minmax(0,1fr)}}\n";document.head.append(style);return()=>style.remove();},'hanamesh-app-host:style');
|
|
1232
|
+
function apply(ctx,{verification=false}={}){localeService=ctx.locale??null;if(localeService)ctx.effect(()=>localeService.register('hanamesh.apphost',COPY),'hanamesh-app-host:locale');ctx.effect(()=>{const style=document.createElement('style');style.textContent='.hm-node-runtime{display:grid;gap:12px;max-width:900px;color:inherit}.hm-node-fields{display:grid;gap:10px}.hm-node-fields label{display:grid;gap:6px}.hm-node-runtime input,.hm-node-runtime select,.hm-node-runtime button{font:inherit;color:inherit;background:transparent;border:1px solid color-mix(in srgb,currentColor 30%,transparent);border-radius:8px;min-height:44px;padding:8px 12px;box-sizing:border-box;max-width:100%}.hm-node-runtime button{cursor:pointer}.hm-node-runtime button:hover:not(:disabled){background:color-mix(in srgb,currentColor 9%,transparent)}.hm-node-runtime button:focus-visible{outline:2px solid currentColor;outline-offset:2px}.hm-node-runtime button:disabled{opacity:.5;cursor:default}.hm-node-confirm,.hm-node-progress{border:1px solid color-mix(in srgb,currentColor 25%,transparent);border-radius:12px;padding:16px}.hm-node-confirm dl{display:grid;grid-template-columns:100px minmax(0,1fr);gap:8px}.hm-node-runtime dd{margin:0}.hm-node-runtime code,.hm-node-runtime dd{overflow-wrap:anywhere}.hm-node-runtime progress{width:100%}.hm-node-runtime [role=alert]{border-left:3px solid currentColor;padding-left:10px}'+'.hm-providers,.hm-library-sources{display:grid;gap:16px}.hm-providers table{width:100%;border-collapse:collapse}.hm-providers th,.hm-providers td{padding:8px;text-align:left;border-bottom:1px solid color-mix(in srgb,currentColor 14%,transparent)}.hm-provider-card{padding:16px;border:1px solid color-mix(in srgb,currentColor 18%,transparent);border-radius:12px}.hm-provider-row{display:grid;grid-template-columns:minmax(120px,1fr) auto minmax(180px,1fr) auto;gap:8px;align-items:center;margin:8px 0}.hm-source-row{display:grid;grid-template-columns:minmax(0,1fr) repeat(4,auto);gap:8px}.hm-source-row code{overflow-wrap:anywhere}.hm-source-add{display:flex;gap:8px}.hm-source-add input{flex:1}.hm-library-overlay{position:absolute;inset:0;z-index:30;pointer-events:auto;overflow:auto;padding:24px;background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary)}.hm-library-overlay button,.hm-library-overlay input,.hm-library-overlay select,.hm-market-embedded button,.hm-market-embedded input,.hm-market-embedded select{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-border-l2)}.hm-library-overlay>header{display:flex;justify-content:space-between;align-items:center}.hm-library-toolbar{display:flex;gap:8px;align-items:center;margin:8px 0}.hm-library-toolbar input[type=search]{flex:1;padding:6px 10px}.hm-library-more{margin:12px auto;display:block}.hm-library-failed{color:var(--dsw-alias-state-error-primary);margin:0}.hm-library-ok{color:var(--dsw-alias-state-success-primary);margin:0}.hm-restart-notice{padding:10px 12px;border:1px solid var(--dsw-alias-state-warn-primary);border-radius:8px;background:var(--dsw-alias-state-warn-tertiary);margin:8px 0}.hm-restart-link{margin-left:4px;font-weight:600}.hm-market-state{font-size:12px;color:var(--dsw-alias-label-secondary)}.hm-market-actions{display:flex;gap:8px;flex-wrap:wrap}.hm-market-embedded{display:block}.hm-market-embedded>header{display:flex;justify-content:space-between;align-items:center}.hm-market-conflict{padding:10px 12px;border:1px solid var(--dsw-alias-state-business-primary);border-radius:8px;background:color-mix(in srgb,var(--dsw-alias-state-business-primary) 8%,transparent);margin:8px 0}.hm-install-target{display:grid;gap:8px;padding:16px;border:1px solid var(--dsw-alias-border-l2);border-radius:12px;margin:12px 0}.hm-install-target form{display:flex;gap:8px;flex-wrap:wrap}.hm-install-target input{flex:1;min-width:180px;padding:6px 10px}.hm-install-target dl{display:grid;grid-template-columns:80px 1fr;gap:6px}.hm-install-target dd{margin:0;overflow-wrap:anywhere}.hm-market-installed{margin-top:24px}.hm-market-installed ul{padding-left:18px;display:grid;gap:6px}.hm-library-toolbar select{padding:6px 10px}.hm-library-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:16px}.hm-library-grid article{display:grid;gap:10px;padding:16px;border:1px solid color-mix(in srgb,currentColor 16%,transparent);border-radius:12px}.hm-app-frame{display:grid;grid-template-rows:auto 1fr;height:calc(100vh - 130px)}.hm-market-embedded>.hm-app-frame{position:fixed;inset:0;z-index:1001;height:100vh;box-sizing:border-box;background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);padding:12px 16px}.hm-app-frame>div{display:flex;justify-content:space-between}.hm-app-frame iframe{width:100%;height:100%;border:0}';style.textContent+='/* Only AppHost surfaces: never resize the host settings shell. */\n.hm-node-runtime,.hm-library-sources,.hm-providers,.hm-market{min-width:0;width:100%;max-width:100%;box-sizing:border-box;overflow-wrap:anywhere}\n.hm-node-runtime>*,.hm-library-sources>*,.hm-node-fields>*,.hm-handoff-settings>*,.hm-library-toolbar>*{min-width:0;max-width:100%;box-sizing:border-box}\n.hm-library-sources input,.hm-library-sources textarea,.hm-library-sources select,.hm-node-runtime input,.hm-node-runtime select{min-width:0;width:100%;max-width:100%;box-sizing:border-box;font:inherit;color:inherit;background:var(--dsw-alias-bg-layer-2)}\n.hm-library-sources button,.hm-market button{min-height:44px;max-width:100%;white-space:normal;font:inherit;color:inherit;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:8px 12px}\n.hm-library-sources button:focus-visible,.hm-market button:focus-visible{outline:2px solid currentColor;outline-offset:2px}\n.hm-handoff-settings{display:grid;gap:12px}.hm-handoff-settings label{display:grid;gap:6px}.hm-handoff-settings input[type=checkbox]{width:auto}\n.hm-node-confirm,.hm-node-progress{min-width:0;max-width:100%;box-sizing:border-box}\n.hm-failure pre{max-width:100%;white-space:pre-wrap;overflow-wrap:anywhere;font-size:12px}.hm-failure summary{cursor:pointer}.hm-failure p{margin:8px 0}\n.hm-library-toolbar,.hm-source-add{flex-wrap:wrap}.hm-library-toolbar input[type=search]{min-width:0;width:100%;flex:1 1 180px}\n.hm-library-grid{grid-template-columns:repeat(auto-fill,minmax(min(240px,100%),1fr))}\n.hm-providers{overflow-x:auto}.hm-source-row{grid-template-columns:minmax(0,1fr);gap:6px}\n@media(max-width:640px){.hm-library-overlay{padding:12px}.hm-node-confirm{padding:10px}.hm-node-confirm dl,.hm-install-target dl{grid-template-columns:minmax(0,1fr)}.hm-node-confirm dt{font-weight:600}.hm-node-confirm dd{margin-bottom:6px}.hm-library-toolbar select{width:100%}}\n';style.textContent+='\n.hm-provider-records td{overflow-wrap:anywhere}.hm-providers select,.hm-providers input{min-width:0;max-width:100%;width:100%;box-sizing:border-box}.hm-provider-records details>summary{cursor:pointer;min-height:44px;display:flex;align-items:center}.hm-provider-records pre{white-space:pre-wrap;overflow-wrap:anywhere;font-size:12px}\n@media(max-width:640px){.hm-providers{overflow:visible}.hm-provider-records,.hm-provider-records tbody{display:block}.hm-provider-records thead{display:none}.hm-provider-records tr{display:grid;grid-template-columns:minmax(0,1fr);gap:12px;padding:12px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;margin:10px 0}.hm-provider-records td{display:block;min-width:0;padding:0;border:0}.hm-provider-records td::before{content:attr(data-label);display:block;font-weight:600;margin-bottom:4px}.hm-provider-records td:empty{display:none}}\n';style.textContent+="\n.hm-providers,.hm-library-sources,.hm-node-runtime{overflow-wrap:normal;word-break:normal}\n.hm-providers h2,.hm-library-sources h2,.hm-node-runtime h2{margin:0;font-weight:600;line-height:1.5}\n.hm-providers p,.hm-library-sources p,.hm-node-runtime p{margin:0;line-height:1.6}\n.hm-suite-fields,.hm-suite-fields>label,.hm-handoff-settings,.hm-source-add{display:grid;grid-template-columns:minmax(0,1fr);gap:10px;min-width:0}\n.hm-suite-fields>*,.hm-source-add>*,.hm-providers>*,.hm-library-sources details,.hm-node-runtime details{min-width:0;max-width:100%;box-sizing:border-box}\n.hm-library-sources input,.hm-library-sources textarea,.hm-library-sources select,.hm-suite-fields select,.hm-providers select,.hm-providers input{display:block;width:100%;min-width:0;max-width:100%;box-sizing:border-box;min-height:44px;padding:8px;font:inherit;border:1px solid var(--dsw-alias-border-l2);border-radius:8px}\n.hm-library-sources input[type=checkbox]{display:inline-block;width:20px;min-height:20px;max-width:20px}\n.hm-library-sources summary,.hm-providers summary,.hm-node-runtime summary{min-height:44px;display:flex;align-items:center;cursor:pointer}\n.hm-library-sources code,.hm-library-sources dd,.hm-providers pre{overflow-wrap:anywhere;white-space:pre-wrap}\n.hm-handoff-settings>button,.hm-suite-fields>button{width:100%}\n@media(max-width:640px){.hm-provider-records tr{padding:8px}.hm-node-fields{grid-template-columns:minmax(0,1fr)}}\n";style.textContent+=appHostPageStyles;document.head.append(style);return()=>style.remove();},'hanamesh-app-host:style');
|
|
1233
|
+
ctx.inject?.(['layout'],page=>{page.effect(()=>registerAppHostPages(page),'hanamesh-app-host:pages');});
|
|
1234
|
+
ctx.effect(()=>ctx.slots.inject('hanamesh.environment.content',()=>ctx.slots.register({name:'hanamesh.environment.content',id:'apphost',order:10},EnvironmentContent)),'hanamesh-app-host:environment-content');
|
|
1140
1235
|
ctx.effect(()=>localizedSection(ctx,'hanamesh-providers',18,"供应商",ProvidersSection),'hanamesh-app-host:providers');
|
|
1141
1236
|
ctx.effect(()=>localizedSection(ctx,'hanamesh-library-sources',19,"市场目录源",LibrarySourcesSection),'hanamesh-app-host:library-sources');
|
|
1142
1237
|
ctx.effect(()=>localizedSection(ctx,'hanamesh-suite-source',19.5,"套件安装包来源",SuiteSourceSettingsSection),'hanamesh-app-host:suite-source');
|
|
@@ -1148,5 +1243,5 @@ h('div',{className:'hm-source-add'},h('input',{value:draft,placeholder:'https://
|
|
|
1148
1243
|
ctx.effect(()=>{let stopped=false,timer;const tick=async()=>{await pollHandoffsVisibly();if(!stopped)timer=setTimeout(()=>void tick(),handoffPollMs);};void tick();return()=>{stopped=true;clearTimeout(timer);};},'hanamesh-app-host:website-handoff');
|
|
1149
1244
|
ctx.effect(()=>{globalThis.addEventListener?.('hashchange',consumeInstallTargetHash);consumeInstallTargetHash();return()=>globalThis.removeEventListener?.('hashchange',consumeInstallTargetHash);},'hanamesh-app-host:install-target-navigation');}
|
|
1150
1245
|
return{name,inject,apply,ProvidersSection,SuiteSourceSettingsSection,LibrarySourcesSection,HandoffSettingsSection,NodeRuntimeSection,LibraryAction,LibraryOverlay,MarketConflictNotice,createMarketSeat,openInstallTarget,
|
|
1151
|
-
MARKET_SEAT,MARKET_CONFLICT_TEXT,INSTALL_TARGET_HASH,INSTALL_TARGET_CONTRACT_VERSION,marketState:()=>({conflict:marketConflict,entryVisible})};
|
|
1246
|
+
registerAppHostPages,EnvironmentContent,credibilityFacts,readCredibility,catalogWebsiteLink,MARKET_SEAT,MARKET_CONFLICT_TEXT,INSTALL_TARGET_HASH,INSTALL_TARGET_CONTRACT_VERSION,marketState:()=>({conflict:marketConflict,entryVisible})};
|
|
1152
1247
|
}});
|
package/dist/dsh-compat.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import semver from 'semver';
|
|
2
2
|
|
|
3
3
|
/** Public host compatibility; exact dependency versions in devDependencies remain reproducible test inputs. */
|
|
4
|
-
export const DSH_RANGE='>=0.2.0-rc.2 <0.3.0-0 || >=0.2.1-0 <0.3.0-0';
|
|
4
|
+
export const DSH_RANGE='>=0.2.0-rc.2 <0.3.0-0 || >=0.2.1-0 <0.3.0-0 || >=0.2.2-0 <0.3.0-0';
|
|
5
5
|
/** saveExact entered the public PluginManager contract in 0.2.1-alpha.2. Unknown versions fail closed. */
|
|
6
6
|
export function managerSupportsExactSave(version){return typeof version==='string'&&semver.satisfies(version,'>=0.2.1-alpha.2 <0.3.0-0',{includePrerelease:true});}
|
package/dist/dsh.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type {InstallationReaderConfig} from './installation-status.js';
|
|
2
1
|
import type { AppHost, AppDefinition, AuthenticatedSubject, Awaitable, DshStorageBinding } from './index.js';
|
|
3
2
|
export const name: 'hanamesh-app-host';
|
|
4
3
|
export const DSH_TARGET: '>=0.2.0-rc.2 <0.3.0-0 || >=0.2.1-0 <0.3.0-0';
|
|
@@ -13,7 +12,7 @@ export interface DshPluginConfig {
|
|
|
13
12
|
/** `sources` undefined → the HanaMesh catalog source; explicit `[]` → none. `profileDir`/`profileName`/`dshBin` (and
|
|
14
13
|
* `nodeBinary`) are inferred from the proven Desktop layout or plain DSH when undefined — see docs/CONTRACT.md 「应用库配置」; explicit values win. */
|
|
15
14
|
/** rc.28: the same keys serve the HanaMesh market (plugins + applications; routes `/hanamesh/library*`, see docs/CONTRACT.md「市场」). */
|
|
16
|
-
library?:{fixture?:string;sources?:Array<{manifestUrl:string;enabled?:boolean}>;profileDir?:string;profileName?:string;dshBin?:string;registry?:string;eventRetention?:number
|
|
15
|
+
library?:{fixture?:string;sources?:Array<{manifestUrl:string;enabled?:boolean}>;profileDir?:string;profileName?:string;dshBin?:string;registry?:string;eventRetention?:number};
|
|
17
16
|
applications?: AppDefinition[]; leaseTtlMs?: number; sweepIntervalMs?: number;
|
|
18
17
|
}
|
|
19
18
|
/** Adapt an open `ctx.storageDomain` handle (single layout, global snapshot) to the store binding. */
|
package/dist/dsh.js
CHANGED
|
@@ -76,8 +76,6 @@ export const Config = z.object({
|
|
|
76
76
|
registry:z.string(),
|
|
77
77
|
/** Publicly named event history capacity; a lost cursor reports resetRequired. */
|
|
78
78
|
eventRetention:z.number().default(512),
|
|
79
|
-
/** Explicit official CLI readers; unset means unknown, never inferred from PATH or private files. */
|
|
80
|
-
installationReaders:z.union([z.any(),z.const(undefined)]),
|
|
81
79
|
/** Website → client handoff listener (library/handoff.js). Unset fields use the defaults shown in the library
|
|
82
80
|
* settings page, where they can also be changed (saved values win over this config). */
|
|
83
81
|
handoff:z.object({enabled:z.boolean(),ports:z.union([z.array(z.number()),z.const(undefined)]),
|
|
@@ -260,7 +258,7 @@ export async function apply(ctx, config) {
|
|
|
260
258
|
};
|
|
261
259
|
const identity=await detectClient({desktop});
|
|
262
260
|
if(typeof ctx.logger?.info==='function')ctx.logger.info('hanamesh-app-host library: client %s (%s)',identity.client,identity.evidence);
|
|
263
|
-
library=createLibraryService({domain:{global:libraryDomain.global,close:closeLibraryDomain},host,config:libraryConfig,dataRoot,ledgerReader:provisionApi?.ledger??(async()=>({schema:1,items:{}})),installer,client:identity.client,unavailable,suiteSourceService:()=>suiteSourceService,installationStatus:nodeStatusBinding(()=>nodeRuntime.executable,workerNode=>createInstallationStatusReader({client:identity.client,core:()=>coreService,workerNode
|
|
261
|
+
library=createLibraryService({domain:{global:libraryDomain.global,close:closeLibraryDomain},host,config:libraryConfig,dataRoot,ledgerReader:provisionApi?.ledger??(async()=>({schema:1,items:{}})),installer,client:identity.client,unavailable,suiteSourceService:()=>suiteSourceService,installationStatus:nodeStatusBinding(()=>nodeRuntime.executable,workerNode=>createInstallationStatusReader({client:identity.client,core:()=>coreService,workerNode})),logger:ctx.logger});
|
|
264
262
|
own('library', () => library.close());
|
|
265
263
|
await library.init();
|
|
266
264
|
// rc.27: app bundles register plain `app.json` (no package name); the installed scan knows which npm package ships
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Live Windows tree anchor; its reaper closes the tree before removing trace files.
|
|
2
|
+
import {spawn,spawnSync} from 'node:child_process';
|
|
3
|
+
import {join} from 'node:path';
|
|
4
|
+
const stop=()=>{spawnSync(join(process.env.SystemRoot,'System32','taskkill.exe'),['/PID',String(process.pid),'/T','/F'],{stdio:'ignore',windowsHide:true});process.exit(1);};
|
|
5
|
+
process.once('disconnect',stop);
|
|
6
|
+
process.once('message',({command,args,options})=>{
|
|
7
|
+
const {input,...spawnOptions}=options;
|
|
8
|
+
const child=spawn(command,args,{...spawnOptions,stdio:[input===undefined?'ignore':'pipe','inherit','inherit'],detached:false});
|
|
9
|
+
if(input!==undefined){child.stdin.on('error',()=>{});child.stdin.end(input);}
|
|
10
|
+
child.once('error',error=>{if(process.connected)process.send({error:{code:error.code??'SPAWN_FAILED'}});});
|
|
11
|
+
child.once('close',(code,signal)=>{if(process.connected)process.send({result:{code,status:code,signal}});});
|
|
12
|
+
});
|
package/dist/git-process.js
CHANGED
|
@@ -7,6 +7,11 @@ const policy=Object.freeze({GIT_CONFIG_NOSYSTEM:'1',GIT_TERMINAL_PROMPT:'0',GIT_
|
|
|
7
7
|
GIT_CONFIG_KEY_1:'credential.helper',GIT_CONFIG_VALUE_1:'!gh auth git-credential'});
|
|
8
8
|
export function safeGitEnv(base=process.env){
|
|
9
9
|
const env={...base};
|
|
10
|
+
// Nested guards share ownership only within the same explicitly selected temp root.
|
|
11
|
+
// A caller choosing a different scratch root starts an independent trace owner.
|
|
12
|
+
if(!env.HM_GIT_GUARD_PARENT_ROOT&&base.TMPDIR===process.env.TMPDIR&&process.env.HM_GIT_GUARD_PARENT_ROOT){
|
|
13
|
+
env.HM_GIT_GUARD_PARENT_ROOT=process.env.HM_GIT_GUARD_PARENT_ROOT;env.HM_GIT_GUARD_TMP_ROOT=process.env.HM_GIT_GUARD_TMP_ROOT;
|
|
14
|
+
}
|
|
10
15
|
for(const key of Object.keys(env))if(/^GIT_CONFIG_(?:COUNT|KEY_\d+|VALUE_\d+|PARAMETERS)$/.test(key)||/^GIT_TRACE/.test(key)||['GIT_ASKPASS','SSH_ASKPASS'].includes(key))delete env[key];
|
|
11
16
|
return {...env,...policy,npm_config_manage_package_manager_versions:'false',pnpm_config_manage_package_manager_versions:'false'};
|
|
12
17
|
}
|
|
@@ -27,7 +32,7 @@ export function observeGitTrace(directory){
|
|
|
27
32
|
let event;try{event=JSON.parse(line);}catch{throw Object.assign(new Error('Invalid Git credential trace.'),{code:'GIT_TRACE_INVALID'});}
|
|
28
33
|
if(['child_start','exec'].includes(event.event)&&forbidden.test((event.argv??[]).join(' '))){failure=Object.assign(new Error('Forbidden macOS keychain helper was invoked; operation failed.'),{code:'GIT_KEYCHAIN_FORBIDDEN'});throw failure;}
|
|
29
34
|
}
|
|
30
|
-
},close(){if(closed)return;closed=true;try{closeSync(fd);}finally{rmSync(directory,{recursive:true,force:true});}}};
|
|
35
|
+
},close(){if(closed)return;closed=true;try{closeSync(fd);}finally{rmSync(directory,{recursive:true,force:true,maxRetries:4,retryDelay:25});}}};
|
|
31
36
|
}
|
|
32
37
|
export function guardNodeBinary({workerNode,versions=process.versions,execPath=process.execPath}={}){
|
|
33
38
|
if(workerNode!==undefined)return typeof workerNode==='string'&&isAbsolute(workerNode)?workerNode:undefined;
|
|
@@ -43,7 +48,7 @@ export async function spawnGitSafe(command,args,options={}){
|
|
|
43
48
|
try{
|
|
44
49
|
if(signal?.aborted)throw Object.assign(new Error('Guarded command aborted.'),{name:'AbortError',code:'ABORT_ERR'});
|
|
45
50
|
const stdio=options.stdio==='inherit'?['ignore','inherit','inherit','ipc']:['ignore','pipe','pipe','ipc'];
|
|
46
|
-
worker=fork(new URL('./git-worker.js',import.meta.url),[],{cwd:options.cwd,env:safeGitEnv(options.env??process.env),execArgv:[],execPath:nodeRuntime,detached:
|
|
51
|
+
worker=fork(new URL('./git-worker.js',import.meta.url),[],{cwd:options.cwd,env:safeGitEnv(options.env??process.env),execArgv:[],execPath:nodeRuntime,detached:true,windowsHide:true,stdio});
|
|
47
52
|
worker.stdout?.on('data',chunk=>{stdout=(stdout+chunk.toString(encoding)).slice(-4*1024*1024);});
|
|
48
53
|
worker.stderr?.on('data',chunk=>{stderr=(stderr+chunk.toString(encoding)).slice(-4*1024*1024);});
|
|
49
54
|
signal?.addEventListener('abort',abort,{once:true});
|
|
@@ -59,6 +64,6 @@ export async function spawnGitSafe(command,args,options={}){
|
|
|
59
64
|
}finally{
|
|
60
65
|
clearTimeout(deadline);clearTimeout(abortFallback);signal?.removeEventListener('abort',abort);
|
|
61
66
|
if(worker?.connected)worker.disconnect();
|
|
62
|
-
if(directory)rmSync(directory,{recursive:true,force:true});
|
|
67
|
+
if(directory)rmSync(directory,{recursive:true,force:true,maxRetries:4,retryDelay:25});
|
|
63
68
|
}
|
|
64
69
|
}
|