@hanamesh/dsh-app-host 0.6.18 → 0.6.21
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 +3 -1
- package/dist/client-ui.d.ts +4 -1
- package/dist/client-ui.js +37 -31
- package/dist/install-target/fixtures/catalog.json +99 -8
- package/dist/install-target/fixtures/install-failure-cases.json +385 -35
- package/dist/install-target/fixtures/reference-consumer.js +1 -1
- package/dist/install-target/suite.d.ts +2 -1
- package/dist/install-target/suite.js +26 -6
- package/dist/installation-status.d.ts +1 -1
- package/dist/library/catalog.js +24 -7
- package/dist/library/github-target.js +42 -0
- package/dist/library/handoff.js +5 -2
- package/dist/library/install.js +27 -17
- package/dist/library/installation-status.js +3 -3
- package/dist/library/recovery.js +5 -3
- package/dist/library/routes.js +1 -0
- package/dist/library/service.js +37 -21
- package/dist/library/suite.js +24 -11
- package/docs/CONTRACT.md +2 -2
- package/docs/INSTALLATION_STATUS.md +4 -2
- package/docs/INSTALL_TARGET.md +20 -4
- package/docs/LIBRARY.md +3 -1
- package/docs/LICENSES.json +1 -1
- package/docs/SECURITY.md +2 -0
- package/package.json +1 -1
- package/schemas/catalog-provider-page.schema.json +97 -10
- package/schemas/install-target.schema.json +252 -38
package/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
0.6.21 候选:普通市场和网站交接消费 Indexer 的固定 GitHub SHA/子目录/包名/构建名单。客户端沿原确认页展示来源、完整 commit 与本机构建提示;确认回传同一目标,目录更新不得换 SHA。执行仍走受控官方 DSH CLI / pnpm,读回精确来源与启用项后报告需完全退出重开;没有改上游或另写安装器。详见 [安装目标](docs/INSTALL_TARGET.md)。初轮已完成隔离 DSH、网站及真实 DTO 验收;本轮 R25 另修未匹配固定目标时的拒绝与退休目录版本字段,仍待复审,不宣称已发布。
|
|
2
|
+
|
|
1
3
|
0.6.16 候选:普通市场通过注入的宿主 pluginManager.registries() 读取源事实,并按宿主锚点/版本守卫加载公开 ./registry 规则模块;AppHost 不声明、安装或捆绑 manager。发布依赖/peer闭包与0.6.15一致。pnpm自有源(null)不显式传--registry,配置镜像则传同一源;查询30秒具名超时,脱敏原诊断同进UI和日志。
|
|
2
4
|
|
|
3
|
-
0.6.15 候选:市场保留官方 CLI 的 stdout/stderr 真实失败原因并脱敏,在卡片与安装确认的技术详情中显示。正文显示 pnpm
|
|
5
|
+
0.6.15 候选:市场保留官方 CLI 的 stdout/stderr 真实失败原因并脱敏,在卡片与安装确认的技术详情中显示。正文显示 pnpm 真实错误类别;宿主发布时间检查失败时列出受影响包与实际 cutoff;HanaMesh 总包安装/升级使用精确发布版本,发布后即可验收;不修改 profile、pnpm 供应链策略或安装执行路径。
|
|
4
6
|
|
|
5
7
|
官方桌面 0.2.0-rc.2 自带 pnpm 11.7.0 对同一包的多条精确 `minimumReleaseAgeExclude` 只采用首条,旧版条目会遮住后列新版,整份 lockfile 检查因而可阻断无关插件安装。本机桌面 0.2.1-rc.3 自带的 pnpm 11.28.5 已合并这些条目,相同样本通过;未推定首次修复版本。请通过官方渠道升级 DSH,保留失败的技术详情;AppHost 不绕过该检查。见 [兼容说明](docs/DSH_COMPATIBILITY.md)。
|
|
6
8
|
|
package/dist/client-ui.d.ts
CHANGED
|
@@ -17,7 +17,10 @@ export function LibraryOverlay(props?:{embedded?:boolean,preferredSubsectionId?:
|
|
|
17
17
|
export function MarketConflictNotice():unknown;
|
|
18
18
|
/** Identifiers only; metadata and confirmation are owned by the client's market. */
|
|
19
19
|
/** rc.8: `contractVersion` optionally declares the install-target contract the caller speaks; absent means v1, any other value is refused with CONTRACT_VERSION_UNSUPPORTED. */
|
|
20
|
-
export
|
|
20
|
+
export type PackageInstallTarget =
|
|
21
|
+
| {source:'npm';name:string;version:string;allowBuild:string[]}
|
|
22
|
+
| {source:'github';repo:string;ref:string;path:string|null;packageName:string;allowBuild:string[]};
|
|
23
|
+
export interface InstallTarget {itemId?:string;packageName?:string;contractVersion?:'1';installTarget?:PackageInstallTarget;}
|
|
21
24
|
export interface InstallTargetReceipt {status:'confirmation-required'|'installing'|'already-installed'|'superseded';itemId?:string;packageName?:string;}
|
|
22
25
|
/** Show/resolve a target without installing. Resolves once confirmation is ready; rejects with a visible market error. */
|
|
23
26
|
export function openInstallTarget(input:InstallTarget):Promise<InstallTargetReceipt>;
|
package/dist/client-ui.js
CHANGED
|
@@ -53,7 +53,7 @@ window.__ModuleLoader__.load({id:'@hanamesh/dsh-app-host',factory:function(requi
|
|
|
53
53
|
"copy45": "删除",
|
|
54
54
|
"copy46": "添加并启用",
|
|
55
55
|
"copy47": "套件安装包来源",
|
|
56
|
-
"copy48": "
|
|
56
|
+
"copy48": "选择 HanaMesh 总包的 npm 安装包 registry。市场目录源提供浏览清单,在「市场目录源」中单独设置。",
|
|
57
57
|
"copy49": "安装包来源",
|
|
58
58
|
"copy50": "现有配置(默认)",
|
|
59
59
|
"copy51": "Core 当前本机来源",
|
|
@@ -147,6 +147,7 @@ window.__ModuleLoader__.load({id:'@hanamesh/dsh-app-host',factory:function(requi
|
|
|
147
147
|
"copy135": "检测不到",
|
|
148
148
|
"copy136": "(未启用)",
|
|
149
149
|
"copy137": "。请完全退出客户端,使用补齐命令(仅 macOS / Linux 终端;Windows 安全命令暂未提供)修复并重新打开:",
|
|
150
|
+
"copy302": "。请完全退出客户端,重新读取总包发布版本后修复并重新打开。",
|
|
150
151
|
"copy138": ";但应用库没能读回 profile(",
|
|
151
152
|
"copy139": "),已安装列表可能仍显示「需重启」",
|
|
152
153
|
"copy140": ";安装前状态未被改动(已逐项读回)",
|
|
@@ -187,7 +188,7 @@ window.__ModuleLoader__.load({id:'@hanamesh/dsh-app-host',factory:function(requi
|
|
|
187
188
|
"copy175": "打开",
|
|
188
189
|
"copy176": "仅收录,不可安装",
|
|
189
190
|
"copy177": "HanaMesh 套件",
|
|
190
|
-
"copy178": "已安装 ·
|
|
191
|
+
"copy178": "已安装 · HanaMesh ",
|
|
191
192
|
"copy179": "套件不完整,请查看组件状态",
|
|
192
193
|
"copy180": "套件状态未知,请查看 Core 声明与阻断原因",
|
|
193
194
|
"copy181": "Core 完整性:",
|
|
@@ -197,7 +198,6 @@ window.__ModuleLoader__.load({id:'@hanamesh/dsh-app-host',factory:function(requi
|
|
|
197
198
|
"copy185": "检查兼容升级",
|
|
198
199
|
"copy186": "重试安装",
|
|
199
200
|
"copy187": "升级",
|
|
200
|
-
"copy188": "升级到 ",
|
|
201
201
|
"copy189": "补齐运行时",
|
|
202
202
|
"copy190": "卸载",
|
|
203
203
|
"copy191": "已卸载,重启 DSH 后生效",
|
|
@@ -223,11 +223,9 @@ window.__ModuleLoader__.load({id:'@hanamesh/dsh-app-host',factory:function(requi
|
|
|
223
223
|
"copy211": "查看安装目标",
|
|
224
224
|
"copy212": "市场安装确认",
|
|
225
225
|
"copy213": "插件名",
|
|
226
|
-
"copy214": "版本",
|
|
227
|
-
"copy215": "目录未提供版本",
|
|
228
226
|
"copy216": "测试目录(fixture)",
|
|
229
227
|
"copy217": "当前市场目录",
|
|
230
|
-
"copy218": "
|
|
228
|
+
"copy218": "确认后升级 HanaMesh 总包及其锁定组件;需要完全退出并重新打开客户端。",
|
|
231
229
|
"copy219": "确认安装",
|
|
232
230
|
"copy220": "取消",
|
|
233
231
|
"copy221": "正在查询市场目录…",
|
|
@@ -280,7 +278,7 @@ window.__ModuleLoader__.load({id:'@hanamesh/dsh-app-host',factory:function(requi
|
|
|
280
278
|
"copy268": "交接状态只给发起它的那个网站看",
|
|
281
279
|
"copy269": "一个网站不能读另一个网站的交接",
|
|
282
280
|
"copy270": "交接请求体不超过 4096 字节",
|
|
283
|
-
"copy271": "
|
|
281
|
+
"copy271": "交接只包含目录身份、合约版本及已审核的结构化安装目标;正文上限为 4096 字节,不接受来源命令",
|
|
284
282
|
"copy272": "端口在 1024–65535 之间、互不重复,至少一个",
|
|
285
283
|
"copy273": "1024 以下要管理员权限;没有端口就没有地方让网站找到",
|
|
286
284
|
"copy274": "网站必须写成来源(协议 + 域名 [+ 端口]),http 只允许 127.0.0.1 / localhost",
|
|
@@ -360,7 +358,7 @@ window.__ModuleLoader__.load({id:'@hanamesh/dsh-app-host',factory:function(requi
|
|
|
360
358
|
"copy45": "Remove",
|
|
361
359
|
"copy46": "Add and enable",
|
|
362
360
|
"copy47": "Suite package source",
|
|
363
|
-
"copy48": "Choose the npm registry for the
|
|
361
|
+
"copy48": "Choose the npm registry for the HanaMesh bundle. Browseable catalogs are configured separately in Market catalog sources.",
|
|
364
362
|
"copy49": "Package source",
|
|
365
363
|
"copy50": "Existing configuration (default)",
|
|
366
364
|
"copy51": "Core’s current local source",
|
|
@@ -454,6 +452,7 @@ window.__ModuleLoader__.load({id:'@hanamesh/dsh-app-host',factory:function(requi
|
|
|
454
452
|
"copy135": "Not available",
|
|
455
453
|
"copy136": " (disabled)",
|
|
456
454
|
"copy137": ". Fully quit the client and run the repair command (macOS / Linux terminal only; no safe Windows command is supplied), then reopen: ",
|
|
455
|
+
"copy302": ". Fully quit the client, read the published bundle version again, repair and reopen.",
|
|
457
456
|
"copy138": "; the market could not read the profile (",
|
|
458
457
|
"copy139": "), so the installed list may still show Restart required",
|
|
459
458
|
"copy140": "; the pre-install state is unchanged (each item read back)",
|
|
@@ -494,7 +493,7 @@ window.__ModuleLoader__.load({id:'@hanamesh/dsh-app-host',factory:function(requi
|
|
|
494
493
|
"copy175": "Open",
|
|
495
494
|
"copy176": "Listed only; not installable",
|
|
496
495
|
"copy177": "HanaMesh suite",
|
|
497
|
-
"copy178": "Installed ·
|
|
496
|
+
"copy178": "Installed · HanaMesh ",
|
|
498
497
|
"copy179": "Suite incomplete. Check component status",
|
|
499
498
|
"copy180": "Suite status unknown. Check the Core declaration and blocking reasons",
|
|
500
499
|
"copy181": "Core completeness: ",
|
|
@@ -504,7 +503,6 @@ window.__ModuleLoader__.load({id:'@hanamesh/dsh-app-host',factory:function(requi
|
|
|
504
503
|
"copy185": "Check compatible upgrade",
|
|
505
504
|
"copy186": "Retry installation",
|
|
506
505
|
"copy187": "Upgrade",
|
|
507
|
-
"copy188": "Upgrade to ",
|
|
508
506
|
"copy189": "Prepare runtime",
|
|
509
507
|
"copy190": "Uninstall",
|
|
510
508
|
"copy191": "Uninstalled; restart DSH to apply",
|
|
@@ -530,11 +528,9 @@ window.__ModuleLoader__.load({id:'@hanamesh/dsh-app-host',factory:function(requi
|
|
|
530
528
|
"copy211": "Review installation target",
|
|
531
529
|
"copy212": "Confirm market installation",
|
|
532
530
|
"copy213": "Plugin name",
|
|
533
|
-
"copy214": "Version",
|
|
534
|
-
"copy215": "No version provided by catalog",
|
|
535
531
|
"copy216": "Test catalog (fixture)",
|
|
536
532
|
"copy217": "Current market catalog",
|
|
537
|
-
"copy218": "Confirmation upgrades
|
|
533
|
+
"copy218": "Confirmation upgrades the HanaMesh bundle and its locked components. Fully quit and reopen the client afterwards.",
|
|
538
534
|
"copy219": "Confirm installation",
|
|
539
535
|
"copy220": "Cancel",
|
|
540
536
|
"copy221": "Reading market catalog…",
|
|
@@ -587,7 +583,7 @@ window.__ModuleLoader__.load({id:'@hanamesh/dsh-app-host',factory:function(requi
|
|
|
587
583
|
"copy268": "Handoff status is visible only to the website that created it.",
|
|
588
584
|
"copy269": "One website cannot read another website’s handoffs.",
|
|
589
585
|
"copy270": "Handoff request body must not exceed 4096 bytes.",
|
|
590
|
-
"copy271": "A
|
|
586
|
+
"copy271": "A handoff contains only catalog identity, contract version and the reviewed package target. The body limit is 4096 bytes; source commands are not accepted.",
|
|
591
587
|
"copy272": "Provide at least one distinct port between 1024 and 65535.",
|
|
592
588
|
"copy273": "Lower ports need administrator permissions. Without a port the website cannot find this client.",
|
|
593
589
|
"copy274": "Websites must be origins (scheme + domain [+ port]). HTTP is allowed only for 127.0.0.1 / localhost.",
|
|
@@ -619,6 +615,8 @@ window.__ModuleLoader__.load({id:'@hanamesh/dsh-app-host',factory:function(requi
|
|
|
619
615
|
"copy301": "Supports macOS 13.5+ (Apple Silicon / Intel). Windows installation, guardian and cancellation verification are not connected; this flow does not support Windows."
|
|
620
616
|
}
|
|
621
617
|
};
|
|
618
|
+
Object.assign(COPY.zh,{githubRepository:'来源仓库',githubCommit:'固定 commit',githubPath:'仓库子目录',githubBuild:'会在本机执行构建脚本',githubBuildPolicy:'确认后将按名单允许构建;pnpm 会把许可保存到当前 profile。',githubNoBuild:'未授予构建脚本许可'});
|
|
619
|
+
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'});
|
|
622
620
|
const COPY_KEYS=new Map(Object.entries(COPY.zh).map(([key,text])=>[text,key]));
|
|
623
621
|
let localeService=null;
|
|
624
622
|
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];}
|
|
@@ -664,6 +662,7 @@ window.__ModuleLoader__.load({id:'@hanamesh/dsh-app-host',factory:function(requi
|
|
|
664
662
|
let targetSelection=null,targetSequence=0,pendingOperations={};
|
|
665
663
|
const targetListeners=new Set(),pendingListeners=new Set();
|
|
666
664
|
const INSTALL_TARGET_HASH='#hanamesh-install?',INSTALL_TARGET_CONTRACT_VERSION='1';
|
|
665
|
+
const targetQuery=input=>new URLSearchParams({...input,...(input.installTarget?{installTarget:JSON.stringify(input.installTarget)}:{})});
|
|
667
666
|
const CORE_SUITE_FAILURES={CORE_SUITE_DECLARATION_MISSING:t('Core 未提供套件声明'),CORE_SUITE_DECLARATION_INVALID:t('Core 套件声明无法用于精确安装。')};
|
|
668
667
|
const suiteTargetFailure=target=>target.previewFailed?t('兼容升级检查未通过,尚未执行安装:')+(t(CORE_SUITE_FAILURES[target.error])??target.error??t('检测不到结果')):t(CORE_SUITE_FAILURES[target.error])??(target.details?.stage==='suite-upgrade'?t('套件升级未完成:')+(target.error??t('检测不到结果'))+t('。请核对当前版本;完全退出后可使用 HanaMesh 设置中的补齐命令修复并重新打开。'):t('套件升级未完成或结果尚未确认:')+(target.error??t('检测不到结果'))+t('。请核对已装版本;不会自动重试。'));
|
|
669
668
|
const setTarget=value=>{targetSelection=value;for(const listener of targetListeners)listener(value);};
|
|
@@ -675,11 +674,12 @@ window.__ModuleLoader__.load({id:'@hanamesh/dsh-app-host',factory:function(requi
|
|
|
675
674
|
if(!input||typeof input!=='object'||Array.isArray(input))throw new Error('INVALID_INPUT');
|
|
676
675
|
// rc.8: optional contract declaration; absent keeps v1 semantics, an unsupported version is refused before any request.
|
|
677
676
|
if(input.contractVersion!==undefined&&input.contractVersion!==INSTALL_TARGET_CONTRACT_VERSION)throw new Error('CONTRACT_VERSION_UNSUPPORTED');
|
|
678
|
-
if(Object.keys(input).some(key=>!['itemId','packageName','contractVersion'].includes(key))||(!input.itemId&&!input.packageName)||
|
|
677
|
+
if(Object.keys(input).some(key=>!['itemId','packageName','contractVersion','installTarget'].includes(key))||(!input.itemId&&!input.packageName)||
|
|
678
|
+
(input.installTarget!==undefined&&(!input.itemId||!input.installTarget||typeof input.installTarget!=='object'||Array.isArray(input.installTarget)))||
|
|
679
679
|
(input.itemId!==undefined&&(typeof input.itemId!=='string'||!input.itemId.length||input.itemId.length>160||/[\u0000-\u001f\u007f]/.test(input.itemId)))||
|
|
680
680
|
(input.packageName!==undefined&&(typeof input.packageName!=='string'||input.packageName.length>214||!/^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/.test(input.packageName))))throw new Error('INVALID_INPUT');
|
|
681
681
|
setTarget({status:'resolving',input:{...input}});
|
|
682
|
-
const query=
|
|
682
|
+
const query=targetQuery(input),resolved=await request('/hanamesh/library/target?'+query);
|
|
683
683
|
if(sequence!==targetSequence)return{status:'superseded'};
|
|
684
684
|
const item=resolved.item,op=pendingOperations[item.id];
|
|
685
685
|
const status=op?.busy?'installing':['registered','installed','installed-not-loaded'].includes(item.installed?.state)&&!item.upgradeAvailable?'already-installed':'confirmation-required';
|
|
@@ -705,7 +705,7 @@ window.__ModuleLoader__.load({id:'@hanamesh/dsh-app-host',factory:function(requi
|
|
|
705
705
|
if(handoffIssue?.poll)setHandoffIssue(null);
|
|
706
706
|
for(const row of value.handoffs??[]){
|
|
707
707
|
if(row.state!=='pending'||handled.has(row.handoffId))continue;handled.add(row.handoffId);
|
|
708
|
-
const input={...(row.itemId?{itemId:row.itemId}:{}),...(row.packageName&&row.itemId!=='hanamesh:suite-upgrade'?{packageName:row.packageName}:{}),contractVersion:INSTALL_TARGET_CONTRACT_VERSION};
|
|
708
|
+
const input={...(row.itemId?{itemId:row.itemId}:{}),...(row.packageName&&row.itemId!=='hanamesh:suite-upgrade'?{packageName:row.packageName}:{}),...(row.installTarget?{installTarget:row.installTarget}:{}),contractVersion:INSTALL_TARGET_CONTRACT_VERSION};
|
|
709
709
|
let opened=false;
|
|
710
710
|
try{await openInstallTarget(input,{handoffId:row.handoffId});opened=true;}
|
|
711
711
|
catch(error){noteHandoffIssue(t('确认框打不开'),error);await reportHandoff(row.handoffId,'failed',{code:errorCode(error)});}
|
|
@@ -718,7 +718,9 @@ window.__ModuleLoader__.load({id:'@hanamesh/dsh-app-host',factory:function(requi
|
|
|
718
718
|
const hash=globalThis.location?.hash;if(!hash?.startsWith(INSTALL_TARGET_HASH))return;
|
|
719
719
|
const params=new URLSearchParams(hash.slice(INSTALL_TARGET_HASH.length));
|
|
720
720
|
if(new Set(params.keys()).size!==[...params.keys()].length){targetSequence++;if(entryVisible)setLibraryVisible(true);setTarget({status:'failed',error:'INVALID_INPUT'});return;}
|
|
721
|
-
|
|
721
|
+
const input=Object.fromEntries(params);
|
|
722
|
+
if(input.installTarget!==undefined){try{input.installTarget=JSON.parse(input.installTarget);}catch{setTarget({status:'failed',error:'INVALID_INPUT'});return;}}
|
|
723
|
+
void openInstallTarget(input).catch(()=>{});
|
|
722
724
|
}
|
|
723
725
|
// rc.32 market seat. The shell's Extension Management panel (dsh-tauri-panel-extension) reads the client-side
|
|
724
726
|
// cordis service `market` via `ctx.reflect.get('market')` and shows a 「市场」 tab when it is there; it calls
|
|
@@ -772,7 +774,7 @@ h('div',{className:'hm-source-add'},h('input',{value:draft,placeholder:'https://
|
|
|
772
774
|
h('p',null,t('现有 registry:'),h('code',null,value.configuredRegistry)),
|
|
773
775
|
h('p',null,t('已保存选择:')+(value.mode==='core'?t('Core 当前本机来源'):t('现有配置'))),
|
|
774
776
|
observation?.observable?h('div',null,h('p',{role:'status'},t('Core 来源:')+state.status+' · '+state.selection),state.registry?h('p',null,h('code',null,state.registry)):null,state.lastError?FailureNotice({error:state.lastError}):null):h('div',null,h('p',{role:'status'},t('Core 来源暂不可用。请检查 Core 插件及其来源设置。')),h('details',null,h('summary',null,t('技术详情')),h('code',null,observation?.code??'CORE_SUITE_SOURCE_MISSING'))),
|
|
775
|
-
h('details',null,h('summary',null,t('使用说明')),h('p',null,t('
|
|
777
|
+
h('details',null,h('summary',null,t('使用说明')),h('p',null,t('选择 HanaMesh 总包的 npm 安装包 registry。市场目录源提供浏览清单,在「市场目录源」中单独设置。')),h('p',null,t('保存选择不会安装。检查、确认和执行前都重新读取当前来源与 Core 兼容声明;来源变化后须重新查看并确认。')))):h('p',null,t('正在读取套件安装包来源…')));
|
|
776
778
|
}
|
|
777
779
|
function NodeRuntimeSection(){useLocale();
|
|
778
780
|
const[value,setValue]=React.useState(null),[draft,setDraft]=React.useState(null),[plan,setPlan]=React.useState(null),[error,setError]=React.useState(''),[working,setWorking]=React.useState(false);
|
|
@@ -849,7 +851,7 @@ h('div',{className:'hm-source-add'},h('input',{value:draft,placeholder:'https://
|
|
|
849
851
|
return(pnpmCode?pnpmCode+' · ':'')+(t(FAILURE_REASONS[code])??t('详细原因见宿主日志'));
|
|
850
852
|
};
|
|
851
853
|
const operationNotice=op=>op?h('div',null,h('p',{role:op.failed?'alert':'status',className:op.failed?'hm-library-failed':'hm-library-ok',style:{overflowWrap:'anywhere'}},t(op.text)),op.diagnostic?h('details',null,h('summary',null,t('技术详情')),h('pre',{style:{whiteSpace:'pre-wrap',overflowWrap:'anywhere'}},op.diagnostic)):null):null;
|
|
852
|
-
const suiteRecoveryText=details=>details?.stage==='suite-upgrade'?t(';升级未完成,部分组件可能已改变。当前读回:')+(details.actual?.components?.map(c=>c.label+' '+(c.version??t('检测不到'))+(c.active?'':t('(未启用)'))).join(';')||t('检测不到'))+t('。请完全退出客户端,使用补齐命令(仅 macOS / Linux 终端;Windows 安全命令暂未提供)修复并重新打开:')+details.command:'';
|
|
854
|
+
const suiteRecoveryText=details=>details?.stage==='suite-upgrade'?t(';升级未完成,部分组件可能已改变。当前读回:')+(details.actual?.components?.map(c=>c.label+' '+(c.version??t('检测不到'))+(c.active?'':t('(未启用)'))).join(';')||t('检测不到'))+(details.command?t('。请完全退出客户端,使用补齐命令(仅 macOS / Linux 终端;Windows 安全命令暂未提供)修复并重新打开:')+details.command:t('。请完全退出客户端,重新读取总包发布版本后修复并重新打开。')):'';
|
|
853
855
|
/** pluginManager applied it, but the profile could not be read back: the installed list may still say 「需重启」. */
|
|
854
856
|
const baselineText=result=>result?.restartBaseline?.updated===false?t(';但应用库没能读回 profile(')+result.restartBaseline.code+t('),已安装列表可能仍显示「需重启」'):'';
|
|
855
857
|
/** rc.10 install recovery result: only an item-by-item readback that matched the baseline is shown as restored. */
|
|
@@ -863,7 +865,7 @@ h('div',{className:'hm-source-add'},h('input',{value:draft,placeholder:'https://
|
|
|
863
865
|
const KIND_TEXT={application:t('应用'),plugin:t('插件'),listing:t('仅收录')};
|
|
864
866
|
const STATE_TEXT={registered:t('已安装'),installed:t('已安装'),'installed-not-loaded':t('需重启'),'uninstalled-not-unloaded':t('已卸载 · 需重启'),'runtime-missing':t('缺运行时'),invalid:t('包无效')};
|
|
865
867
|
// Mirrors PROTECTED_PACKAGES on the host: the suite is never offered an uninstall button (the host answers 403 anyway).
|
|
866
|
-
const PROTECTED=new Set(['@hanamesh/
|
|
868
|
+
const PROTECTED=new Set(['@hanamesh/hanamesh','@hanamesh/core','@hanamesh/usage','@hanamesh/dsh-app-host','hanamesh-brand']);
|
|
867
869
|
function RestartNotice({reason}){useLocale();
|
|
868
870
|
return h('p',{role:'status',className:'hm-restart-notice','data-hanamesh-restart':'required'},h('strong',null,t('需重新打开客户端')),' ',reason??t('插件更改需要重新打开客户端后生效。'),' ',h('span',{className:'hm-restart-hint'},t('请完全退出 HanaMesh 客户端或 DSH 后重新打开;关闭窗口不等于退出。')));
|
|
869
871
|
}
|
|
@@ -988,14 +990,15 @@ h('div',{className:'hm-source-add'},h('input',{value:draft,placeholder:'https://
|
|
|
988
990
|
const selected=targetSelection;if(selected?.status!=='confirmation-required'||pendingOperations[selected.item.id]?.busy)return;
|
|
989
991
|
setTarget({...selected,status:'installing'});
|
|
990
992
|
const suite=selected.item.kind==='suite-upgrade';
|
|
991
|
-
const
|
|
993
|
+
const reviewed={itemId:selected.item.id,packageName:selected.item.package.name,...(selected.item.installTarget?{installTarget:selected.item.installTarget}:{})};
|
|
994
|
+
const outcome=await operate(suite?'/hanamesh/library/suite/upgrade':'/hanamesh/library/install',suite?{planId:selected.item.suiteUpgrade.planId}:reviewed,selected.item.id,suite?t('升级套件'):t('安装'),true);
|
|
992
995
|
if(outcome){
|
|
993
996
|
if(!outcome.ok){if(targetSelection?.item?.id===selected.item.id)setTarget({...targetSelection,status:outcome.unknown?'result-unavailable':suite?'suite-upgrade-failed':'failed',error:outcome.code,details:outcome.details});return;}
|
|
994
997
|
if(suite){
|
|
995
998
|
try{const snapshot=await request('/hanamesh/library/installedPlugins');if(!outcome.result.components.every(c=>snapshot.plugins.some(p=>p.packageName===c.name&&p.version===c.to&&p.active)))throw new Error('INSTALL_READBACK_MISMATCH');setPending(p=>({...p,[selected.item.id]:{label:t("升级套件"),text:()=>(t('套件升级已读回,请完全退出并重新打开客户端。'))}}));setChanged(true);setTarget({...selected,status:'installed'});}
|
|
996
999
|
catch(error){setPending(p=>({...p,[selected.item.id]:{label:t("升级套件"),text:()=>(t('套件版本读回失败,请检查三个组件。')),failed:true}}));setTarget({...selected,status:'readback-failed',error:String(error.message??error)});}return;
|
|
997
1000
|
}
|
|
998
|
-
try{const fresh=await request('/hanamesh/library/target?'+
|
|
1001
|
+
try{const fresh=await request('/hanamesh/library/target?'+targetQuery(reviewed));
|
|
999
1002
|
const row=fresh.item?.installed;
|
|
1000
1003
|
if(row?.packageName!==selected.item.package.name||!['registered','installed','installed-not-loaded'].includes(row.state)||(outcome.result?.version&&row.version!==outcome.result.version))throw new Error('INSTALL_READBACK_MISMATCH');
|
|
1001
1004
|
const live=outcome.result?.status==='applied';setPending(p=>({...p,[selected.item.id]:{label:t("安装"),text:()=>(live?t('安装完成,已加载,可以直接使用(已按精确版本读回)')+baselineText(outcome.result):t('安装完成,已按精确版本读回'))}}));if(!live)setChanged(true);
|
|
@@ -1046,20 +1049,21 @@ h('div',{className:'hm-source-add'},h('input',{value:draft,placeholder:'https://
|
|
|
1046
1049
|
const restartRequired=changed||Boolean(snapshot?.restartRequired);
|
|
1047
1050
|
const filters=[...BASE_FILTERS,...seenCategories.filter(c=>c!=='hanamesh-app').map(c=>[c,c])];
|
|
1048
1051
|
const openButton=row=>h('button',{type:'button',disabled:Boolean(openingAppId),onClick:()=>void open(row)},openingAppId===row.appId?t('打开中…'):t('打开'));
|
|
1049
|
-
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}:{})};
|
|
1052
|
+
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}:{})};
|
|
1053
|
+
const reviewOrInstall=label=>item.installTarget?openInstallTarget(installBody).catch(()=>{}):operate('/hanamesh/library/install',installBody,item.id,label);
|
|
1050
1054
|
let action=h('span',null,t('仅收录,不可安装')),state=null;
|
|
1051
1055
|
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:''));
|
|
1052
|
-
if(item.kind==='suite')return h('article',{key:item.id,'data-hanamesh-library-item':item.id,'data-hanamesh-kind':'suite'},h('h3',null,t('HanaMesh 套件')),h('p',null,item.summary),h('p',null,item.suite?.status==='installed'?t('已安装 ·
|
|
1056
|
+
if(item.kind==='suite')return h('article',{key:item.id,'data-hanamesh-library-item':item.id,'data-hanamesh-kind':'suite'},h('h3',null,t('HanaMesh 套件')),h('p',null,item.summary),h('p',null,item.suite?.status==='installed'?t('已安装 · HanaMesh ')+item.suite.version:item.suite?.status==='incomplete'?t('套件不完整,请查看组件状态'):t('套件状态未知,请查看 Core 声明与阻断原因')),item.suite?.completeness?h('p',null,t('Core 完整性:')+item.suite.completeness):null,...(item.suite?.components??[]).map(c=>h('p',{key:c.name},c.requiredRange?c.label+t(' · 要求 ')+c.requiredRange+t(' · 实际 ')+(c.actualVersion??t('检测不到'))+' · '+c.status:c.label+' · '+(c.version??t('检测不到'))+(c.active?'':t(' · 未启用')))),...(item.suite?.blocked??[]).map((row,i)=>h('p',{key:'blocked-'+i,role:'status'},row.code+(row.moduleName?' · '+row.moduleName:'')+(row.bundleVersion?' · HanaMesh '+row.bundleVersion:''))),item.suiteUpgradeUnavailable?h('p',{role:'status'},item.suiteUpgradeUnavailable):null,h('button',{type:'button',disabled:Boolean(item.suiteUpgradeUnavailable),onClick:()=>void openInstallTarget({itemId:'hanamesh:suite-upgrade'}).catch(()=>{})},t('检查兼容升级')));
|
|
1053
1057
|
if(busy)action=h('button',{type:'button',disabled:true},t(op.text));
|
|
1054
|
-
else if(item.kind!=='listing'&&!row)action=h('button',{type:'button',onClick:()=>void
|
|
1055
|
-
else if(row&&item.upgradeAvailable)action=h('span',{className:'hm-market-actions'},h('button',{type:'button',onClick:()=>void
|
|
1058
|
+
else if(item.kind!=='listing'&&!row)action=h('button',{type:'button',onClick:()=>void reviewOrInstall(t('安装'))},op?.failed?t('重试安装'):t('安装'));
|
|
1059
|
+
else if(row&&item.upgradeAvailable)action=h('span',{className:'hm-market-actions'},h('button',{type:'button',onClick:()=>void reviewOrInstall(t('升级'))},item.installTarget?.source==='github'?t('查看安装目标'):t('升级')),row.state==='registered'?openButton(row):null);
|
|
1056
1060
|
else if(row?.state==='runtime-missing')action=h('button',{type:'button',onClick:()=>void operate('/hanamesh/library/provision',{appId:row.appId,packageName:row.packageName,runtimeItem:row.runtimeItem},item.id,t('补齐运行时'))},t('补齐运行时'));
|
|
1057
1061
|
else if(row?.state==='registered')action=h('span',{className:'hm-market-actions'},openButton(row),h('button',{type:'button',onClick:()=>void operate('/hanamesh/library/uninstall',{appId:row.appId,packageName:row.packageName,runtimeItem:row.runtimeItem},item.id,t('卸载'))},t('卸载')));
|
|
1058
1062
|
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('卸载'));
|
|
1059
1063
|
else if(row?.state==='uninstalled-not-unloaded')action=h('span',null,t('已卸载,重启 DSH 后生效'));
|
|
1060
1064
|
else if(row)action=h('span',null,t('已安装,重启 DSH 后生效'));
|
|
1061
1065
|
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),
|
|
1062
|
-
h('small',null,t(KIND_TEXT[item.kind])+' · '+(item.publisher?.name??t('未知发布者'))+
|
|
1066
|
+
h('small',null,t(KIND_TEXT[item.kind])+' · '+(item.publisher?.name??t('未知发布者'))+((item.categories??[]).length?' · '+item.categories.join('、'):'')),
|
|
1063
1067
|
state,action,op&&!busy?operationNotice(op):null);};
|
|
1064
1068
|
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'})):
|
|
1065
1069
|
h('div',{className:'hm-library-grid',key:'catalog'},...items.map(card));
|
|
@@ -1080,10 +1084,12 @@ h('div',{className:'hm-source-add'},h('input',{value:draft,placeholder:'https://
|
|
|
1080
1084
|
h('input',{'aria-label':t('目录条目 ID'),value:targetItemId,placeholder:t('itemId(可选)'),onChange:event=>setTargetItemId(event.target.value)}),
|
|
1081
1085
|
h('input',{'aria-label':t('包名'),value:targetPackageName,placeholder:t('packageName(可选)'),onChange:event=>setTargetPackageName(event.target.value)}),h('button',{type:'submit'},t('查看安装目标')))):null,
|
|
1082
1086
|
target?h('div',{role:target.status==='confirmation-required'?'dialog':'status','aria-label':t('市场安装确认')},
|
|
1083
|
-
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('
|
|
1084
|
-
target.item?.
|
|
1087
|
+
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,
|
|
1088
|
+
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,
|
|
1089
|
+
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,
|
|
1090
|
+
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,
|
|
1085
1091
|
target.item?.suiteUpgrade?.registry?h('p',null,'Registry:'+target.item.suiteUpgrade.registry):null,
|
|
1086
|
-
...(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.
|
|
1092
|
+
...(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:''))),
|
|
1087
1093
|
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('取消'))):
|
|
1088
1094
|
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]),
|
|
1089
1095
|
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,
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
{
|
|
2
|
-
"schemaVersion": "
|
|
3
|
-
"
|
|
2
|
+
"schemaVersion": "hanamesh.catalog.v1",
|
|
3
|
+
"generatedAt": "2026-11-05T00:00:00.000Z",
|
|
4
4
|
"items": [
|
|
5
5
|
{
|
|
6
6
|
"id": "fixture.install-target.plugin",
|
|
7
7
|
"name": "@hanamesh-fixture/install-target-plugin",
|
|
8
8
|
"displayName": "测试目录 · 安装目标插件",
|
|
9
9
|
"summary": "Install-target contract fixture (test catalog, not a real product).",
|
|
10
|
-
"latestVersion": "1.2.3",
|
|
11
10
|
"categories": [
|
|
12
11
|
"utility"
|
|
13
12
|
],
|
|
@@ -17,6 +16,43 @@
|
|
|
17
16
|
},
|
|
18
17
|
"publisher": {
|
|
19
18
|
"name": "HanaMesh fixture"
|
|
19
|
+
},
|
|
20
|
+
"itemId": "fixture.install-target.plugin",
|
|
21
|
+
"packageName": "@hanamesh-fixture/install-target-plugin",
|
|
22
|
+
"catalogKind": "plugin",
|
|
23
|
+
"source": {
|
|
24
|
+
"sourceId": "hanamesh",
|
|
25
|
+
"sourceRef": "fixture.install-target.plugin",
|
|
26
|
+
"sourceLinks": [
|
|
27
|
+
{
|
|
28
|
+
"sourceId": "hanamesh",
|
|
29
|
+
"sourceRef": "fixture.install-target.plugin",
|
|
30
|
+
"sourceUrl": "https://catalog-fixture.example/fixture.install-target.plugin",
|
|
31
|
+
"firstSeenAt": "2026-11-05T00:00:00.000Z",
|
|
32
|
+
"lastSeenAt": "2026-11-05T00:00:00.000Z"
|
|
33
|
+
}
|
|
34
|
+
]
|
|
35
|
+
},
|
|
36
|
+
"installMethods": [
|
|
37
|
+
{
|
|
38
|
+
"channel": "hanamesh",
|
|
39
|
+
"kind": "npm",
|
|
40
|
+
"spec": "@hanamesh-fixture/install-target-plugin",
|
|
41
|
+
"command": null,
|
|
42
|
+
"allowBuild": [],
|
|
43
|
+
"sourceId": "hanamesh",
|
|
44
|
+
"sourceRef": "fixture.install-target.plugin",
|
|
45
|
+
"target": {
|
|
46
|
+
"source": "npm",
|
|
47
|
+
"name": "@hanamesh-fixture/install-target-plugin",
|
|
48
|
+
"version": "1.2.3",
|
|
49
|
+
"allowBuild": []
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
],
|
|
53
|
+
"oneClickInstall": {
|
|
54
|
+
"allowed": true,
|
|
55
|
+
"reason": null
|
|
20
56
|
}
|
|
21
57
|
},
|
|
22
58
|
{
|
|
@@ -24,7 +60,6 @@
|
|
|
24
60
|
"name": "@hanamesh-fixture/install-target-app",
|
|
25
61
|
"displayName": "测试目录 · 安装目标应用",
|
|
26
62
|
"summary": "Install-target contract fixture application (test catalog).",
|
|
27
|
-
"latestVersion": "0.4.0",
|
|
28
63
|
"categories": [
|
|
29
64
|
"hanamesh-app"
|
|
30
65
|
],
|
|
@@ -34,6 +69,43 @@
|
|
|
34
69
|
},
|
|
35
70
|
"publisher": {
|
|
36
71
|
"name": "HanaMesh fixture"
|
|
72
|
+
},
|
|
73
|
+
"itemId": "fixture.install-target.app",
|
|
74
|
+
"packageName": "@hanamesh-fixture/install-target-app",
|
|
75
|
+
"catalogKind": "application",
|
|
76
|
+
"source": {
|
|
77
|
+
"sourceId": "hanamesh",
|
|
78
|
+
"sourceRef": "fixture.install-target.app",
|
|
79
|
+
"sourceLinks": [
|
|
80
|
+
{
|
|
81
|
+
"sourceId": "hanamesh",
|
|
82
|
+
"sourceRef": "fixture.install-target.app",
|
|
83
|
+
"sourceUrl": "https://catalog-fixture.example/fixture.install-target.app",
|
|
84
|
+
"firstSeenAt": "2026-11-05T00:00:00.000Z",
|
|
85
|
+
"lastSeenAt": "2026-11-05T00:00:00.000Z"
|
|
86
|
+
}
|
|
87
|
+
]
|
|
88
|
+
},
|
|
89
|
+
"installMethods": [
|
|
90
|
+
{
|
|
91
|
+
"channel": "hanamesh",
|
|
92
|
+
"kind": "npm",
|
|
93
|
+
"spec": "@hanamesh-fixture/install-target-app",
|
|
94
|
+
"command": null,
|
|
95
|
+
"allowBuild": [],
|
|
96
|
+
"sourceId": "hanamesh",
|
|
97
|
+
"sourceRef": "fixture.install-target.app",
|
|
98
|
+
"target": {
|
|
99
|
+
"source": "npm",
|
|
100
|
+
"name": "@hanamesh-fixture/install-target-app",
|
|
101
|
+
"version": "0.4.0",
|
|
102
|
+
"allowBuild": []
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
],
|
|
106
|
+
"oneClickInstall": {
|
|
107
|
+
"allowed": true,
|
|
108
|
+
"reason": null
|
|
37
109
|
}
|
|
38
110
|
},
|
|
39
111
|
{
|
|
@@ -45,14 +117,33 @@
|
|
|
45
117
|
"utility"
|
|
46
118
|
],
|
|
47
119
|
"repository": {
|
|
48
|
-
"url": "https://
|
|
120
|
+
"url": "https://github.com/fixture/repository-only"
|
|
49
121
|
},
|
|
50
122
|
"publisher": {
|
|
51
123
|
"name": "HanaMesh fixture"
|
|
124
|
+
},
|
|
125
|
+
"itemId": "fixture.install-target.repository-only",
|
|
126
|
+
"packageName": null,
|
|
127
|
+
"catalogKind": "entry",
|
|
128
|
+
"source": {
|
|
129
|
+
"sourceId": "hanamesh",
|
|
130
|
+
"sourceRef": "fixture.install-target.repository-only",
|
|
131
|
+
"sourceLinks": [
|
|
132
|
+
{
|
|
133
|
+
"sourceId": "hanamesh",
|
|
134
|
+
"sourceRef": "fixture.install-target.repository-only",
|
|
135
|
+
"sourceUrl": "https://catalog-fixture.example/fixture.install-target.repository-only",
|
|
136
|
+
"firstSeenAt": "2026-11-05T00:00:00.000Z",
|
|
137
|
+
"lastSeenAt": "2026-11-05T00:00:00.000Z"
|
|
138
|
+
}
|
|
139
|
+
]
|
|
140
|
+
},
|
|
141
|
+
"installMethods": [],
|
|
142
|
+
"oneClickInstall": {
|
|
143
|
+
"allowed": false,
|
|
144
|
+
"reason": "no-install-method"
|
|
52
145
|
}
|
|
53
146
|
}
|
|
54
147
|
],
|
|
55
|
-
"page": {
|
|
56
|
-
"total": 3
|
|
57
|
-
}
|
|
148
|
+
"page": {}
|
|
58
149
|
}
|