@hanamesh/dsh-app-host 0.6.12 → 0.6.15
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 +8 -0
- package/dist/client-ui.js +21 -9
- package/dist/library/catalog.js +42 -13
- package/dist/library/install.js +9 -5
- package/dist/library/service.js +7 -3
- package/dist/library/suite.js +4 -4
- package/docs/CONTRACT.md +7 -1
- package/docs/DSH_COMPATIBILITY.md +6 -0
- package/docs/DSH_INTEGRATION.md +1 -1
- package/docs/LIBRARY.md +1 -1
- package/docs/LICENSES.json +1 -1
- package/docs/PROVENANCE.json +1 -1
- package/node_modules/@hanamesh/lib-provision/bin/hanamesh-provision.js +0 -0
- package/package.json +23 -26
- package/schemas/catalog-provider-page.schema.json +881 -6
package/README.md
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
|
+
0.6.15 候选:市场保留官方 CLI 的 stdout/stderr 真实失败原因并脱敏,在卡片与安装确认的技术详情中显示。正文显示 pnpm 真实错误类别;宿主发布时间检查失败时列出受影响包,说明默认 24 小时与实际 cutoff,并提示到 DSH 设置检查版本、升级 DSH 或等待后手动重试;不修改 profile、pnpm 供应链策略或安装执行路径。
|
|
2
|
+
|
|
3
|
+
官方桌面 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)。
|
|
4
|
+
|
|
5
|
+
0.6.14:严格读取标准分发目录的精确 npm 三目标与 agentPlugins,保留历史固定 GitHub DTO;目录事实仅展示,不决定兼容或升级。R1-COMBO-01 原始 26 条目响应已纳入回归。
|
|
6
|
+
|
|
1
7
|
> I51 R1 返修(待复审):供应商指引跟随宿主中英菜单,默认正文只写用途和结果;实现解释、交接固定规则及具名设置默认值保留在技术详情。交接的保存操作与套件来源选择前置,自有字段按单列排列;设置值、默认值、权限、读写协议均不变。
|
|
8
|
+
|
|
9
|
+
0.6.13 候选只消费新 npm 三包身份;插件 id、服务与存储不变。新旧 Core/Usage/Brand 均受保护,Recommender 不属于套件。0.6.12 原件保持原行为。
|
|
2
10
|
>
|
|
3
11
|
> 已知限制 / Known limitations:官方 DSH rc.2 设置页在 390 CSS px 下仍保留固定侧栏,插件内容可用宽度受宿主挤压;本插件不改变客户端或全局 CSS,不能保证该宿主窄屏下标题、原生下拉选项及输入值完整可见。完整设置操作建议使用宽窗口。At 390 CSS px, the official DSH rc.2 settings sidebar leaves limited room for plugin content; titles, native select options and input values may remain constrained. Use a wider window for full settings operations. AppHost only controls its own content and does not modify the client or global CSS. 此限制按编排本次裁定披露,不报告整页390通过。Brand M01既有错误可见性限制仍开;静态profile peers check红流不改写为通过。
|
|
4
12
|
|
package/dist/client-ui.js
CHANGED
|
@@ -130,7 +130,9 @@ window.__ModuleLoader__.load({id:'@hanamesh/dsh-app-host',factory:function(requi
|
|
|
130
130
|
"copy122": "为什么:",
|
|
131
131
|
"copy123": "安装命令返回成功,但 profile 读回的版本与固定版本不一致",
|
|
132
132
|
"copy124": "包不在当前 registry 上——尚未发布到 npm,或客户端 registry 设置不对",
|
|
133
|
-
"copy125": "DSH
|
|
133
|
+
"copy125": "DSH 安装命令失败,请展开技术详情查看原始原因",
|
|
134
|
+
"installAgeRejected": "宿主的依赖发布时间检查未通过(默认需发布满 24 小时,以技术详情中的 cutoff 为准)。已知 DSH 0.2.0-rc.2 内置 pnpm 11.7.0 对同名多条白名单只认第一条。请在 DSH 设置中检查当前版本并升级 DSH,或等待受影响版本满足规则后手动重试。",
|
|
135
|
+
"installAffectedPackages": "受影响包:",
|
|
134
136
|
"copy126": "当前宿主未提供安装参数(profile / node 路径)",
|
|
135
137
|
"copy127": "目录里已没有这一项,刷新后重试",
|
|
136
138
|
"copy128": "registry 上的最新版是预发布版(rc),客户端未允许安装预发布版",
|
|
@@ -433,7 +435,9 @@ window.__ModuleLoader__.load({id:'@hanamesh/dsh-app-host',factory:function(requi
|
|
|
433
435
|
"copy122": "Reason: ",
|
|
434
436
|
"copy123": "Installation command succeeded, but the version read from the profile differs from the confirmed version",
|
|
435
437
|
"copy124": "Package not found in the current registry. It may not be published, or the registry setting may be incorrect",
|
|
436
|
-
"copy125": "DSH installation
|
|
438
|
+
"copy125": "DSH installation failed. Expand Technical details for the original cause",
|
|
439
|
+
"installAgeRejected": "The host rejected a dependency's release age (the default requires 24 hours since publication; see the actual cutoff in Technical details). Known issue: pnpm 11.7.0 bundled with DSH 0.2.0-rc.2 only recognizes the first allowlist entry for a package name. Check your version in DSH Settings and upgrade DSH, or wait until the affected versions meet the rule before trying again manually.",
|
|
440
|
+
"installAffectedPackages": "Affected packages: ",
|
|
437
441
|
"copy126": "This host has not provided installation parameters (profile / Node path)",
|
|
438
442
|
"copy127": "This item is no longer in the catalog. Refresh before trying again",
|
|
439
443
|
"copy128": "The latest registry version is a prerelease, which this client has not allowed",
|
|
@@ -831,8 +835,16 @@ h('div',{className:'hm-source-add'},h('input',{value:draft,placeholder:'https://
|
|
|
831
835
|
defaults&&names?h('div',null,h('h3',null,t('设置名称与默认值')),h('dl',null,...['enabled','ports','allowedOrigins','ttlMs','pollMs'].flatMap(key=>[h('dt',{key:key+'-name'},names[key]),h('dd',{key:key+'-value'},Array.isArray(defaults[key])?defaults[key].join(', '):typeof defaults[key]==='boolean'?(defaults[key]?t('开'):t('关')):String(defaults[key]))]))):null,
|
|
832
836
|
value?.rules?h('div',null,h('h3',null,t('固定规则(不能关)')),h('ul',null,...value.rules.map(row=>h('li',{key:row.rule},t(row.rule),h('br',null),h('small',null,t('为什么:')+t(row.why)))))):null));
|
|
833
837
|
}
|
|
834
|
-
const FAILURE_REASONS={...CORE_SUITE_FAILURES,INSTALL_READBACK_MISMATCH:t('安装命令返回成功,但 profile 读回的版本与固定版本不一致'),REGISTRY_LOOKUP_FAILED:t('包不在当前 registry 上——尚未发布到 npm,或客户端 registry 设置不对'),DSH_PLUGIN_FAILED:t('DSH
|
|
835
|
-
const failureReason=
|
|
838
|
+
const FAILURE_REASONS={...CORE_SUITE_FAILURES,INSTALL_READBACK_MISMATCH:t('安装命令返回成功,但 profile 读回的版本与固定版本不一致'),REGISTRY_LOOKUP_FAILED:t('包不在当前 registry 上——尚未发布到 npm,或客户端 registry 设置不对'),DSH_PLUGIN_FAILED:t('DSH 安装命令失败,请展开技术详情查看原始原因'),LIBRARY_INSTALL_UNAVAILABLE:t('当前宿主未提供安装参数(profile / node 路径)'),CATALOG_ITEM_MISSING:t('目录里已没有这一项,刷新后重试'),UNSTABLE_VERSION:t('registry 上的最新版是预发布版(rc),客户端未允许安装预发布版'),RUNTIME_ARCHIVE_UNAVAILABLE:t('运行时归档下载失败,检查网络或来源'),OPERATION_TIMEOUT:t('10 分钟内没有结果,可稍后重试'),PACKAGE_DENIED:t('HanaMesh 套件自身不能从市场安装或卸载'),INSTANCE_IN_USE:t('先关闭该应用的所有视图再卸载')};
|
|
839
|
+
const failureReason=(code,details)=>{
|
|
840
|
+
const diagnostic=code==='DSH_PLUGIN_FAILED'?details?.diagnostic??'':'',pnpmCode=diagnostic.match(/\bERR_PNPM_[A-Z0-9_]+\b/)?.[0];
|
|
841
|
+
if(pnpmCode==='ERR_PNPM_MINIMUM_RELEASE_AGE_VIOLATION'){
|
|
842
|
+
const packages=[...new Set([...diagnostic.matchAll(/((?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*@\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?)\s+was published\b/g)].map(match=>match[1]))];
|
|
843
|
+
return pnpmCode+' · '+t('宿主的依赖发布时间检查未通过(默认需发布满 24 小时,以技术详情中的 cutoff 为准)。已知 DSH 0.2.0-rc.2 内置 pnpm 11.7.0 对同名多条白名单只认第一条。请在 DSH 设置中检查当前版本并升级 DSH,或等待受影响版本满足规则后手动重试。')+(packages.length?' '+t('受影响包:')+packages.join(', '):'');
|
|
844
|
+
}
|
|
845
|
+
return(pnpmCode?pnpmCode+' · ':'')+(t(FAILURE_REASONS[code])??t('详细原因见宿主日志'));
|
|
846
|
+
};
|
|
847
|
+
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;
|
|
836
848
|
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:'';
|
|
837
849
|
/** pluginManager applied it, but the profile could not be read back: the installed list may still say 「需重启」. */
|
|
838
850
|
const baselineText=result=>result?.restartBaseline?.updated===false?t(';但应用库没能读回 profile(')+result.restartBaseline.code+t('),已安装列表可能仍显示「需重启」'):'';
|
|
@@ -847,7 +859,7 @@ h('div',{className:'hm-source-add'},h('input',{value:draft,placeholder:'https://
|
|
|
847
859
|
const KIND_TEXT={application:t('应用'),plugin:t('插件'),listing:t('仅收录')};
|
|
848
860
|
const STATE_TEXT={registered:t('已安装'),installed:t('已安装'),'installed-not-loaded':t('需重启'),'uninstalled-not-unloaded':t('已卸载 · 需重启'),'runtime-missing':t('缺运行时'),invalid:t('包无效')};
|
|
849
861
|
// Mirrors PROTECTED_PACKAGES on the host: the suite is never offered an uninstall button (the host answers 403 anyway).
|
|
850
|
-
const PROTECTED=new Set(['@hanamesh/dsh-app-host','hanamesh-core','hanamesh-usage','hanamesh-brand','@hanamesh/dsh-core','@hanamesh/dsh-usage']);
|
|
862
|
+
const PROTECTED=new Set(['@hanamesh/core','@hanamesh/usage','@hanamesh/brand','@hanamesh/dsh-app-host','hanamesh-core','hanamesh-usage','hanamesh-brand','@hanamesh/dsh-core','@hanamesh/dsh-usage']);
|
|
851
863
|
function RestartNotice({reason}){useLocale();
|
|
852
864
|
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 后重新打开;关闭窗口不等于退出。')));
|
|
853
865
|
}
|
|
@@ -966,7 +978,7 @@ h('div',{className:'hm-source-add'},h('input',{value:draft,placeholder:'https://
|
|
|
966
978
|
if(!outcome)outcome={ok:false,code:'OPERATION_TIMEOUT'};
|
|
967
979
|
const liveLoaded=outcome.ok&&outcome.result?.status==='applied';
|
|
968
980
|
if(outcome.ok&&!awaitReadback&&!liveLoaded)setChanged(true);
|
|
969
|
-
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)+')'+recoveryText(outcome.recovery)+suiteRecoveryText(outcome.details)),failed:true}}));
|
|
981
|
+
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}}));
|
|
970
982
|
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};}};
|
|
971
983
|
const confirmTarget=async()=>{
|
|
972
984
|
const selected=targetSelection;if(selected?.status!=='confirmation-required'||pendingOperations[selected.item.id]?.busy)return;
|
|
@@ -1044,14 +1056,14 @@ h('div',{className:'hm-source-add'},h('input',{value:draft,placeholder:'https://
|
|
|
1044
1056
|
else if(row)action=h('span',null,t('已安装,重启 DSH 后生效'));
|
|
1045
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),
|
|
1046
1058
|
h('small',null,t(KIND_TEXT[item.kind])+' · '+(item.publisher?.name??t('未知发布者'))+' · '+(item.latestVersion??'—')+((item.categories??[]).length?' · '+item.categories.join('、'):'')),
|
|
1047
|
-
state,action,op&&!busy?
|
|
1059
|
+
state,action,op&&!busy?operationNotice(op):null);};
|
|
1048
1060
|
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'})):
|
|
1049
1061
|
h('div',{className:'hm-library-grid',key:'catalog'},...items.map(card));
|
|
1050
1062
|
const moreButton=nextCursor&&!receipt?h('button',{type:'button',className:'hm-library-more',key:'more',onClick:()=>void more()},t('更多')):null;
|
|
1051
1063
|
const installedSection=!receipt&&(installed.length||plugins.length)?h('section',{className:'hm-market-installed',key:'installed'},h('h2',null,t('已安装')),h('ul',null,
|
|
1052
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)))),
|
|
1053
1065
|
...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 层)'):''),
|
|
1054
|
-
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]?
|
|
1066
|
+
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;
|
|
1055
1067
|
// The seat owner may ask for a subsection to come first; `installed` is the only one it names today.
|
|
1056
1068
|
const body=preferredSubsectionId==='installed'?[installedSection,catalogSection,moreButton]:[catalogSection,moreButton,installedSection];
|
|
1057
1069
|
return h('section',{className:embedded?'hm-market hm-market-embedded':'hm-library-overlay hm-market'},
|
|
@@ -1071,7 +1083,7 @@ h('div',{className:'hm-source-add'},h('input',{value:draft,placeholder:'https://
|
|
|
1071
1083
|
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('取消'))):
|
|
1072
1084
|
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]),
|
|
1073
1085
|
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,
|
|
1074
|
-
target.item
|
|
1086
|
+
target.item?operationNotice(pending[target.item.id]):null,
|
|
1075
1087
|
['installed','already-installed'].includes(target.status)&&target.item?.installed?.state==='registered'?openButton(target.item.installed):null):null,
|
|
1076
1088
|
verification===true?h('section',{'aria-label':t('C-MARKET 合约验证')},h('h2',null,t('C-MARKET 合约验证')),
|
|
1077
1089
|
h('p',null,t('测试版;输入与输出仅供当前客户端验证。安装仍须在正常确认框确认。')),
|
package/dist/library/catalog.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import semver from 'semver';
|
|
1
2
|
import { readFile } from 'node:fs/promises';
|
|
2
3
|
import { AppHostError, requireCondition } from '../errors.js';
|
|
3
4
|
|
|
@@ -62,26 +63,54 @@ function timestamp(value,utc=false){
|
|
|
62
63
|
return Number.isFinite(date.getTime())&&date.toISOString().slice(0,10)===value.slice(0,10);
|
|
63
64
|
}
|
|
64
65
|
// Display-only C-INDEXER-CATALOG SuitePresentationDTO; never an install/upgrade decision.
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
66
|
+
const suiteIdentities={
|
|
67
|
+
core:{names:['@hanamesh/core','hanamesh-core'],repo:'yzsnstotz/hanamesh-core'},
|
|
68
|
+
usage:{names:['@hanamesh/usage','hanamesh-usage'],repo:'yzsnstotz/hanamesh-usage'},
|
|
69
|
+
'app-host':{names:['@hanamesh/dsh-app-host'],repo:'yzsnstotz/hanamesh-plugin-app-host'},
|
|
70
|
+
brand:{names:['@hanamesh/brand','hanamesh-brand'],repo:'yzsnstotz/hanamesh-brand'},
|
|
71
|
+
};
|
|
72
|
+
const suiteComponent=value=>Object.hasOwn(suiteIdentities,value);
|
|
73
|
+
const exactVersion=value=>textWithin(value,128,true)&&!value.startsWith('v')&&Boolean(semver.valid(value))&&value.trim()===value;
|
|
74
|
+
const packageFact=fact=>fact&&suiteComponent(fact.component)&&suiteIdentities[fact.component].names.includes(fact.npmPackageName)&&suiteIdentities[fact.component].repo===fact.githubRepo;
|
|
75
|
+
const distinctComponents=(facts,count)=>Array.isArray(facts)&&facts.length===count&&new Set(facts.map(fact=>fact?.component)).size===count;
|
|
76
|
+
function suitePresentation(value,item){
|
|
77
|
+
if(!(own(value,['schemaVersion','id','role','component','canonicalHanaId','installable','installTargets','components','distributionVersion','agentPlugins'])&&
|
|
69
78
|
value.schemaVersion===1&&value.id==='hanamesh'&&['suite','component','client-only'].includes(value.role)&&suiteComponent(value.component)&&
|
|
70
|
-
(value.canonicalHanaId===null||plain(value.canonicalHanaId))&&typeof value.installable==='boolean'
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
79
|
+
(value.canonicalHanaId===null||plain(value.canonicalHanaId))&&typeof value.installable==='boolean'))return false;
|
|
80
|
+
const canonical=value.role==='suite'&&value.component==='usage'&&value.installable&&value.canonicalHanaId===item.id;
|
|
81
|
+
if(value.role!==(value.component==='usage'?'suite':value.component==='brand'?'client-only':'component')||value.installable!==canonical)return false;
|
|
82
|
+
if(value.distributionVersion!==undefined&&(!canonical||!exactVersion(value.distributionVersion)))return false;
|
|
83
|
+
if(value.components!==undefined&&(!canonical||!distinctComponents(value.components,4)||!value.components.every(fact=>
|
|
84
|
+
own(fact,['component','npmPackageName','githubRepo','version'])&&packageFact(fact)&&exactVersion(fact.version))))return false;
|
|
85
|
+
if(value.installTargets!==undefined){
|
|
86
|
+
if(!canonical||!distinctComponents(value.installTargets,3)||!value.installTargets.every(target=>{
|
|
87
|
+
if(!(own(target,['component','npmPackageName','githubRepo','version','source'])&&target.component!=='brand'&&packageFact(target)&&
|
|
88
|
+
(target.version===undefined||exactVersion(target.version))))return false;
|
|
89
|
+
const fact=value.components?.find(fact=>fact.component===target.component),source=target.source;
|
|
90
|
+
if(fact&&(fact.npmPackageName!==target.npmPackageName||fact.githubRepo!==target.githubRepo||
|
|
91
|
+
(target.version!==undefined&&fact.version!==target.version)))return false;
|
|
92
|
+
if(source?.source==='npm')return own(source,['source','name','version'])&&source.name===target.npmPackageName&&
|
|
93
|
+
exactVersion(source.version)&&source.version===target.version&&fact?.version===target.version;
|
|
94
|
+
// Approved standard distribution retains immutable historical Git DTOs, without choosing a fallback source.
|
|
95
|
+
return own(source,['source','repo','ref'])&&source.source==='github'&&source.repo===target.githubRepo&&
|
|
96
|
+
typeof source.ref==='string'&&/^[0-9a-f]{40}$/i.test(source.ref);
|
|
97
|
+
}))return false;
|
|
98
|
+
}
|
|
99
|
+
if(value.agentPlugins!==undefined){
|
|
100
|
+
const agents=value.agentPlugins;
|
|
101
|
+
if(!canonical||!Array.isArray(agents)||agents.length<1||agents.length>2||new Set(agents.map(agent=>agent?.host)).size!==agents.length||
|
|
102
|
+
!agents.every(agent=>own(agent,['host','pluginName','pluginId','marketplaceUrl','version'])&&['claude','codex'].includes(agent.host)&&
|
|
103
|
+
agent.pluginName==='hanamesh'&&agent.pluginId==='hanamesh@hanamesh'&&agent.marketplaceUrl==='https://github.com/yzsnstotz/hanamesh-plugins'&&
|
|
104
|
+
exactVersion(agent.version)&&agent.version===value.distributionVersion))return false;
|
|
105
|
+
}
|
|
106
|
+
return true;
|
|
78
107
|
}
|
|
79
108
|
|
|
80
109
|
function validateHanaItem(item,maxSourceLinks){
|
|
81
110
|
const optionalText={description:5000,latestVersion:64,license:80};
|
|
82
111
|
const source=item?.source;
|
|
83
112
|
requireCondition(own(item,['id','itemId','packageName','name','displayName','summary','catalogKind','source','description','homepage','latestVersion','license','categories','keywords','repository','package','publisher','updatedAt','official','suite'])&&
|
|
84
|
-
(item.official===undefined||typeof item.official==='boolean')&&(item.suite===undefined||suitePresentation(item.suite))&&
|
|
113
|
+
(item.official===undefined||typeof item.official==='boolean')&&(item.suite===undefined||suitePresentation(item.suite,item))&&
|
|
85
114
|
plain(item.id)&&item.id===item.itemId&&
|
|
86
115
|
textWithin(item.name,160,true)&&textWithin(item.displayName,120,true)&&textWithin(item.summary,1000)&&
|
|
87
116
|
['application','plugin','entry'].includes(item.catalogKind)&&
|
package/dist/library/install.js
CHANGED
|
@@ -3,6 +3,8 @@ import { join, resolve, dirname, sep, delimiter } from 'node:path';
|
|
|
3
3
|
import { fileURLToPath } from 'node:url';
|
|
4
4
|
import {safeGitEnv,guardNodeBinary,spawnGitSafe} from '../git-process.js';
|
|
5
5
|
import { randomUUID } from 'node:crypto';
|
|
6
|
+
import { stripVTControlCharacters } from 'node:util';
|
|
7
|
+
import { redact } from '../runtime.js';
|
|
6
8
|
import { AppHostError, requireCondition, SerialQueue } from '../errors.js';
|
|
7
9
|
import { validateDefinition } from '../descriptor.js';
|
|
8
10
|
import { itemKind } from './catalog.js';
|
|
@@ -10,7 +12,7 @@ import { profileSnapshot, runtimeSnapshot, profileChecks, runtimeChecks, package
|
|
|
10
12
|
import {prepareSuiteUpgrade,readSuiteStatus,createCoreSuiteAdapter,SUITE_INSTALL_COMMAND} from './suite.js';
|
|
11
13
|
|
|
12
14
|
/** The HanaMesh suite itself (real names + the historical scoped spellings) is never installed or removed through the market. */
|
|
13
|
-
export const PROTECTED_PACKAGES=Object.freeze(['@hanamesh/dsh-app-host','hanamesh-core','hanamesh-usage','hanamesh-brand','@hanamesh/dsh-core','@hanamesh/dsh-usage']);
|
|
15
|
+
export const PROTECTED_PACKAGES=Object.freeze(['@hanamesh/core','@hanamesh/usage','@hanamesh/brand','@hanamesh/dsh-app-host','hanamesh-core','hanamesh-usage','hanamesh-brand','@hanamesh/dsh-core','@hanamesh/dsh-usage']);
|
|
14
16
|
const blocked=new Set(PROTECTED_PACKAGES);
|
|
15
17
|
const packageName=value=>typeof value==='string'&&/^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/.test(value);
|
|
16
18
|
const versionText=value=>typeof value==='string'&&/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(value);
|
|
@@ -66,7 +68,9 @@ export function createLibraryInstaller({profileDir,profileName,dataRoot,nodeBina
|
|
|
66
68
|
const options={cwd:profileDir,workerNode:supervisorNode,env:safeGitEnv({HOME:process.env.HOME,DSH_HOME:process.env.DSH_HOME,LANG:process.env.LANG??'C.UTF-8',PATH:commandPath(nodeBinary??desktopLauncher.command)})},tail=['plugin','--profile',profileName,...args];
|
|
67
69
|
if(launcherAvailable)options.env=safeGitEnv({...options.env,DSH_HOME:desktopLauncher.home,PATH:commandPath(desktopLauncher.command)});
|
|
68
70
|
const result=await spawn(launcherAvailable?desktopLauncher.command:nodeBinary,launcherAvailable?tail:[...(desktopCli?['--expose-internals',fileURLToPath(new URL('./desktop-cli.js',import.meta.url)),desktopCli.entry,desktopCli.runtimeDir,desktopCli.supportDir]:[dshBin]),...tail],options);
|
|
69
|
-
|
|
71
|
+
const clean=text=>redact(stripVTControlCharacters(String(text??''))).replace(/(https?:\/\/)[^/\s@]+@/g,'$1[REDACTED]@').slice(-2000);
|
|
72
|
+
const stdout=clean(result.stdout),stderr=clean(result.stderr);
|
|
73
|
+
requireCondition(result.code===0,'DSH_PLUGIN_FAILED','DSH plugin operation failed.',{code:result.code,signal:result.signal,stderr,diagnostic:[stdout,stderr].filter(Boolean).join('\n')||`DSH plugin exited with code ${result.code}${result.signal?' ('+result.signal+')':''}.`},502);return result;}
|
|
70
74
|
/** The exact version `dsh plugin add` will pin: the registry's `latest` dist-tag, re-checked here (the catalog's `latestVersion` is a hint only). */
|
|
71
75
|
async function latest(name){
|
|
72
76
|
const base=new URL(registry);requireCondition(['https:','http:'].includes(base.protocol),'REGISTRY_DENIED','Registry URL is invalid.');
|
|
@@ -161,16 +165,16 @@ export function createLibraryInstaller({profileDir,profileName,dataRoot,nodeBina
|
|
|
161
165
|
requireCondition(cliAvailable,'DSH_CLI_UNAVAILABLE','套件升级需客户端随附的官方 CLI;请恢复后重试。',{},503);
|
|
162
166
|
const executing=await prepareSuiteUpgrade({coreSuitePlan:suiteProvider});
|
|
163
167
|
requireCondition(identity(executing)===identity(confirmed),'SUITE_PLAN_CHANGED','执行前套件安装包来源或声明已变化,请重新查看并确认。',{},409);
|
|
164
|
-
emit({type:'library.install-started',itemId:'hanamesh:suite-upgrade',packageName:'hanamesh
|
|
168
|
+
emit({type:'library.install-started',itemId:'hanamesh:suite-upgrade',packageName:'@hanamesh/core',kind:'suite-upgrade'});
|
|
165
169
|
try{
|
|
166
170
|
await run(['add','--save-exact','--strict-peer-dependencies',`--registry=${fresh.registry??registry}`,...fresh.components.map(c=>`${c.name}@${c.to}`)]);
|
|
167
171
|
const actual=await readSuiteStatus(profileDir);
|
|
168
172
|
const saved=await json(join(profileDir,'package.json'));
|
|
169
173
|
requireCondition(actual.status==='installed'&&fresh.components.every((c,i)=>actual.components[i]?.version===c.to&&saved.dependencies?.[c.name]===c.to),'INSTALL_READBACK_MISMATCH','套件升级后的版本或启用状态不符,请完全退出后按补齐命令修复。',{},502);
|
|
170
|
-
return{status:'restart-required',kind:'suite-upgrade',packageName:'hanamesh
|
|
174
|
+
return{status:'restart-required',kind:'suite-upgrade',packageName:'@hanamesh/core',components:fresh.components,version:actual.version};
|
|
171
175
|
}catch(error){
|
|
172
176
|
error.details={...error.details,stage:'suite-upgrade',actual:await readSuiteStatus(profileDir),command:SUITE_INSTALL_COMMAND};
|
|
173
|
-
emit({type:'library.install-failed',packageName:'hanamesh
|
|
177
|
+
emit({type:'library.install-failed',packageName:'@hanamesh/core',code:error.code??'SUITE_UPGRADE_FAILED'});throw error;
|
|
174
178
|
}
|
|
175
179
|
});},
|
|
176
180
|
async install(item,context){
|
package/dist/library/service.js
CHANGED
|
@@ -36,7 +36,11 @@ export function createLibraryService({domain,host,config={},dataRoot,ledgerReade
|
|
|
36
36
|
async function installedPlugins(){return config.profileDir?await scanInstalledPlugins({profileDir:config.profileDir,bootDependencies}):[];}
|
|
37
37
|
const settingsWriter=new SerialQueue();
|
|
38
38
|
function persist(update){return settingsWriter.run(async()=>{const next={...update(state),revision:state.revision+1};await domain.global.set(next);state=next;});}
|
|
39
|
-
async function operation(type,task){const operationId=randomUUID();emit({type:`library.${type}-started`,operationId});const work=Promise.resolve().then(()=>task(operationId)).then(result=>{emit({type:`library.${type}-done`,operationId,result});return result;},error=>{
|
|
39
|
+
async function operation(type,task){const operationId=randomUUID();emit({type:`library.${type}-started`,operationId});const work=Promise.resolve().then(()=>task(operationId)).then(result=>{emit({type:`library.${type}-done`,operationId,result});return result;},error=>{
|
|
40
|
+
const details=error.details?.stage==='suite-upgrade'?{stage:'suite-upgrade',actual:error.details.actual,command:SUITE_INSTALL_COMMAND}:{};
|
|
41
|
+
if(error instanceof AppHostError&&error.code==='DSH_PLUGIN_FAILED'&&typeof error.details?.diagnostic==='string')details.diagnostic=error.details.diagnostic;
|
|
42
|
+
emit({type:`library.${type}-failed`,operationId,code:error.code??'LIBRARY_OPERATION_FAILED',...(error.recovery?{recovery:error.recovery}:{}),...(Object.keys(details).length?{details}:{})});throw error;
|
|
43
|
+
}).finally(()=>running.delete(operationId));running.set(operationId,work);work.catch(()=>{});return{operationId,status:'started'};}
|
|
40
44
|
const requireInstaller=(message)=>requireCondition(ready(),'LIBRARY_INSTALL_UNAVAILABLE',message,{need:unavailable()},503);
|
|
41
45
|
/**
|
|
42
46
|
* The host itself loaded/unloaded a plugin (pluginManager `applied`): it no longer waits for a restart. When the profile
|
|
@@ -62,7 +66,7 @@ export function createLibraryService({domain,host,config={},dataRoot,ledgerReade
|
|
|
62
66
|
requireCondition(packageName===undefined,'INVALID_INPUT','套件升级不接受包名或任意替换参数。');
|
|
63
67
|
requireCondition(typeof installer?.prepareSuiteUpgrade==='function','SUITE_UPGRADE_UNAVAILABLE','当前客户端还不支持安全套件升级,请使用补齐命令。',{},503);
|
|
64
68
|
const plan=await installer.prepareSuiteUpgrade();
|
|
65
|
-
return{contractVersion:'1',source:{kind:'suite',manifestUrl:'https://github.com/yzsnstotz'},item:{id:SUITE_TARGET,displayName:'HanaMesh 套件',kind:'suite-upgrade',package:{registry:'npm',name:'hanamesh
|
|
69
|
+
return{contractVersion:'1',source:{kind:'suite',manifestUrl:'https://github.com/yzsnstotz'},item:{id:SUITE_TARGET,displayName:'HanaMesh 套件',kind:'suite-upgrade',package:{registry:'npm',name:'@hanamesh/core'},latestVersion:plan.components[0].to,installed:{state:'installed',version:suiteStatus.version},upgradeAvailable:plan.available,suiteUpgrade:plan}};
|
|
66
70
|
}
|
|
67
71
|
requireCondition(itemId!==undefined||packageName!==undefined,'INVALID_INPUT','itemId or packageName is required.');
|
|
68
72
|
requireCondition(itemId===undefined||(text(itemId,160)&&!/[\u0000-\u001f\u007f]/.test(itemId)),'INVALID_INPUT','itemId is invalid.');
|
|
@@ -144,7 +148,7 @@ export function createLibraryService({domain,host,config={},dataRoot,ledgerReade
|
|
|
144
148
|
get live(){return Boolean(installer?.live);},
|
|
145
149
|
async upgradeSuite(input){requireInstaller('Suite upgrade is unavailable.');
|
|
146
150
|
requireCondition(input&&Object.keys(input).length===1&&typeof input.planId==='string','INVALID_INPUT','Only the current planId is accepted.');
|
|
147
|
-
return await operation('install',async()=>{try{const result=await installer.upgradeSuite(input);suiteStatus={...await suiteFacts(),restartRequired:true};return result;}catch(error){if(error.details?.stage!=='suite-upgrade')throw error;const actual=await readSuiteStatus(config.profileDir);suiteStatus={...await suiteFacts(),restartRequired:true,upgradeError:error.code??'SUITE_UPGRADE_FAILED'};error.details={...error.details,stage:'suite-upgrade',actual,command:SUITE_INSTALL_COMMAND};emit({type:'library.install-failed',packageName:'hanamesh
|
|
151
|
+
return await operation('install',async()=>{try{const result=await installer.upgradeSuite(input);suiteStatus={...await suiteFacts(),restartRequired:true};return result;}catch(error){if(error.details?.stage!=='suite-upgrade')throw error;const actual=await readSuiteStatus(config.profileDir);suiteStatus={...await suiteFacts(),restartRequired:true,upgradeError:error.code??'SUITE_UPGRADE_FAILED'};error.details={...error.details,stage:'suite-upgrade',actual,command:SUITE_INSTALL_COMMAND};emit({type:'library.install-failed',packageName:'@hanamesh/core',code:error.code??'SUITE_UPGRADE_FAILED',details:{stage:'suite-upgrade',actual,command:SUITE_INSTALL_COMMAND}});throw error;}});
|
|
148
152
|
},
|
|
149
153
|
async install(input){requireCondition(input?.itemId!==SUITE_TARGET,'SUITE_CONFIRMATION_REQUIRED','请在客户端确认完整升级计划。',{},403);requireInstaller('Install requires absolute profileDir, nodeBinary, dshBin and profileName.');const{item}=await resolveTarget(input??{});if(present(item.installed)&&!item.upgradeAvailable)return{status:'already-installed',itemId:item.id,packageName:item.package.name};return await installItem(item);},
|
|
150
154
|
async provision(input){requireInstaller('Runtime provision is unavailable.');
|
package/dist/library/suite.js
CHANGED
|
@@ -6,11 +6,11 @@ import {requireCondition} from '../errors.js';
|
|
|
6
6
|
|
|
7
7
|
export const SUITE_TARGET='hanamesh:suite-upgrade';
|
|
8
8
|
export const SUITE_PACKAGES=Object.freeze([
|
|
9
|
-
{name:'hanamesh
|
|
10
|
-
{name:'hanamesh
|
|
9
|
+
{name:'@hanamesh/core',label:'Core',repo:'hanamesh-core'},
|
|
10
|
+
{name:'@hanamesh/usage',label:'使用记录',repo:'hanamesh-usage'},
|
|
11
11
|
{name:'@hanamesh/dsh-app-host',label:'应用库',repo:'hanamesh-plugin-app-host'},
|
|
12
12
|
].map(Object.freeze));
|
|
13
|
-
export const SUITE_INSTALL_COMMAND="
|
|
13
|
+
export const SUITE_INSTALL_COMMAND="dsh plugin --profile desktop add --save-exact '@hanamesh/core@0.3.21' '@hanamesh/usage@0.3.0-rc.6' '@hanamesh/dsh-app-host@0.6.14'";
|
|
14
14
|
const json=async path=>JSON.parse(await readFile(path,'utf8'));
|
|
15
15
|
/** Physical inventory only, never a compatibility declaration or permission to upgrade. */
|
|
16
16
|
export async function readSuiteStatus(profileDir){
|
|
@@ -104,7 +104,7 @@ export function createCoreSuiteAdapter({core=()=>undefined,registry,suiteSourceM
|
|
|
104
104
|
Object.defineProperty(get,'unavailable',{get:()=>current()?null:CORE_SUITE_DECLARATION_MISSING});
|
|
105
105
|
get.readFacts=async()=>{
|
|
106
106
|
const declaration=await read();
|
|
107
|
-
return{status:declaration.completeness==='complete'?'installed':declaration.completeness,completeness:declaration.completeness,version:declaration.installed.find(row=>row.moduleName==='hanamesh
|
|
107
|
+
return{status:declaration.completeness==='complete'?'installed':declaration.completeness,completeness:declaration.completeness,version:declaration.installed.find(row=>row.moduleName==='@hanamesh/core').actualVersion,
|
|
108
108
|
components:declaration.installed.map(row=>({...row,name:row.moduleName,label:SUITE_PACKAGES.find(p=>p.name===row.moduleName).label,version:row.actualVersion})),blocked:declaration.blocked};
|
|
109
109
|
};
|
|
110
110
|
return get;
|
package/docs/CONTRACT.md
CHANGED
|
@@ -157,7 +157,9 @@ DSH 绑定必须提供同一个 `single` domain 的一次完整镜像 publish
|
|
|
157
157
|
| `/hanamesh/library/provision` | POST | `{appId, packageName, runtimeItem}` 三者必填 | 202;缺字段/非法 → 400 `INVALID_INPUT`(`LIB-PROVISION-INPUT`) |
|
|
158
158
|
| `/hanamesh/library/uninstall` | POST | `{appId, packageName, runtimeItem?}` | 202;缺字段/非法 → 400 `INVALID_INPUT` |
|
|
159
159
|
|
|
160
|
-
事件流 `library.install-*` 增加 `kind`;`install-done.result` 对插件为 `{status:'restart-required', kind:'plugin', packageName, version}
|
|
160
|
+
事件流 `library.install-*` 增加 `kind`;`install-done.result` 对插件为 `{status:'restart-required', kind:'plugin', packageName, version}`。受保护包(`@hanamesh/core`、`@hanamesh/usage`、`@hanamesh/brand`、`hanamesh-brand`、`hanamesh-core`、`hanamesh-usage`、`@hanamesh/dsh-app-host` 及旧写法 `@hanamesh/dsh-core`、`@hanamesh/dsh-usage`)任何路径都 `PACKAGE_DENIED`:`plugins/*` 在启动操作前同步答 403,目录条目路径在操作内以 `-failed` 事件给出。「需重启」判定只来自本进程启动时的 profile 依赖快照(钉版本内核不暴露已加载插件集合);快照不可读时一切已装计为已加载。客户端在壳内(`window.self !== window.top`)给 `hanamesh://restart` 深链,官方 DSH 直开时只提示文字;本包不重启 DSH。
|
|
161
|
+
|
|
162
|
+
0.6.13 候选:套件 npm 身份为 `@hanamesh/core`、`@hanamesh/usage`、`@hanamesh/dsh-app-host`;Core 声明的 `core.name`、`moduleName`、升级目标和返回的 `packageName` 按这些新名精确比较,旧名声明不当作新三包。插件 id、`hanameshMarket` 服务、路由与存储键保持;普通 Recommender 不受保护、不加入套件。首装/补齐精确命令见 LIBRARY.md;已发布 0.6.12 原件不包含该适配。
|
|
161
163
|
|
|
162
164
|
## 市场席位(rc.32,设计定案 2026-09-22 §4,T5)
|
|
163
165
|
|
|
@@ -246,3 +248,7 @@ B17 review repair(同0.6.8候选):`AppHost` 未注入 `runtimeLedgerReader
|
|
|
246
248
|
|
|
247
249
|
|
|
248
250
|
0.6.12:目录条目的展示/跳转链接(`sourceLinks.sourceUrl`、`homepage`、`repository.url`、`publisher.url`)接受 fragment 并原样保留;目录自身 attribution 展示链接亦保留 fragment。manifest 来源和 `transport.endpoint` 等请求 URL 校验不变。单条非法仍拒绝整个 provider page,不返回部分结果。
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
0.6.14 / R1-COMBO-01:HanaMesh view 的可选 suite.agentPlugins 接受 claude/codex 唯一渠道、hanamesh / hanamesh@hanamesh、公开 hanamesh-plugins HTTPS 市场及与 distributionVersion 相同的精确 SemVer。npm source 严格为 {source:'npm',name,version},name=目标 npmPackageName,source.version=目标 version=对应 components.version;三个不同目标限 Core/Usage/AppHost,Brand 仅组件事实。公开组件的新旧 npm 名均绑定原 GitHub 仓库,不猜发布版本。未知字段、范围/tag、缺件、重复件及不一致整页拒绝 INVALID_CATALOG_PAGE。
|
|
254
|
+
历史 GitHub DTO 按已审标准分发提议继续可读:source 只许 source/repo/ref,repo=目标 githubRepo,ref 为40位hex;可缺历史版本事实,不自动转换或选备用源。目录发布事实不作为 Core 兼容声明、安装授权或本机已装观察。随包 catalog/schema/page.json 同时描述默认和 HanaMesh 两视图,跨字段相等性由运行解码器检查。
|
|
@@ -33,6 +33,12 @@ Internal connection HTTPS/bind-address handling changed upstream; the local HTTP
|
|
|
33
33
|
|
|
34
34
|
## Installation behavior
|
|
35
35
|
|
|
36
|
+
0.6.15 retains the monitored public CLI. CLI failures carry bounded, redacted stdout and stderr through the asynchronous operation's diagnostic details; the market shows the original cause in an expandable panel. The main failure message includes the exact pnpm category. A minimum-release-age rejection lists affected package versions, explains the default 24-hour requirement while deferring to the actual cutoff, explains the known first-entry-only defect in pnpm 11.7.0 bundled with DSH 0.2.0-rc.2, and directs the person to DSH Settings to check the version and upgrade DSH, or wait until the rule is met before a manual retry. No profile configuration, dependency exclusion or supply-chain policy is changed.
|
|
37
|
+
|
|
38
|
+
The official 0.2.0-rc.2 bundled pnpm 11.7.0 uses only the first matching package-name rule in `minimumReleaseAgeExclude`: an earlier exact old-version entry shadows a later exact new-version entry. The existing lockfile verifier can therefore reject an unrelated installation even though the new version is visibly listed. The 11.7.0 package obtained from npm has the same entry and core bytes as the supplied Desktop copy. Official Desktop 0.2.1-rc.3's pnpm 11.28.5 unions all matching exact versions and passes the same diagnostic profile. This establishes two tested endpoints, not the first fixed version. Both Desktop CLI and in-process manager use this pnpm path and set `ELECTRON_RUN_AS_NODE=1`; switching to the manager does not correct the 11.7.0 rule evaluation.
|
|
39
|
+
|
|
40
|
+
The rc.2 manager lacks the explicit `saveExact` option, but that alone does not prove exact installation unavailable: normal pnpm 10.33.0 and 11.7.0 both saved an explicitly supplied `name@version` as an exact spec in diagnostic runs. The retained CLI requirement below comes from the scoped environment and Git monitor contract; no manager path is added by 0.6.15.
|
|
41
|
+
|
|
36
42
|
AppHost reads public package metadata resolved from the proven Desktop runtime, or the CLI/profile package anchor.
|
|
37
43
|
It never infers capability from the presence of a method alone or from function source text. Unknown versions do not
|
|
38
44
|
enable live exact installation. Compatible manager versions at least `0.2.1-alpha.2` support `saveExact`,
|
package/docs/DSH_INTEGRATION.md
CHANGED
|
@@ -20,7 +20,7 @@ Review fixes. The install-target probe on the handoff listener answers only orig
|
|
|
20
20
|
plutil timeout); the remaining fixed rules are served as `rules` by `/hanamesh/library/handoff-settings` and listed on
|
|
21
21
|
the settings page. Expiry codes: `HANDOFF_NOT_PICKED_UP` (window never fetched it / fetched but no outcome) and
|
|
22
22
|
`HANDOFF_EXPIRED` (shown or confirmed, no answer); the client reports `failed` with `error.code` when the confirmation
|
|
23
|
-
cannot open (`HANDOFF_OPEN_FAILED`, `cause`). Live install results carry `restartBaseline`. `hanamesh-brand`
|
|
23
|
+
cannot open (`HANDOFF_OPEN_FAILED`, `cause`). Live install results carry `restartBaseline`. `@hanamesh/brand` and legacy `hanamesh-brand` are protected.
|
|
24
24
|
|
|
25
25
|
## 0.6.0 (2026-10-15)
|
|
26
26
|
|
package/docs/LIBRARY.md
CHANGED
|
@@ -59,7 +59,7 @@ Both SOURCE fixture sets and suites ship as `@hanamesh/dsh-app-host/catalog/suit
|
|
|
59
59
|
DSH 首次安装或缺组件时,完全退出客户端后执行同一行,再重新打开:
|
|
60
60
|
|
|
61
61
|
```sh
|
|
62
|
-
dsh plugin --profile desktop add --save-exact '
|
|
62
|
+
dsh plugin --profile desktop add --save-exact '@hanamesh/core@0.3.21' '@hanamesh/usage@0.3.0-rc.6' '@hanamesh/dsh-app-host@0.6.14'
|
|
63
63
|
```
|
|
64
64
|
|
|
65
65
|
套件版本范围、是否完整、候选来源及兼容/升级规则唯一属于 Core。App-host 只读物理安装版本,不能用它们推导兼容或安装许可;不自行读取 npm latest、计算组件组合、校验候选宿主 peers 或持有 Core 的版本范围副本。普通安装/卸载接口继续拒绝套件包。
|
package/docs/LICENSES.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"package": {
|
|
3
3
|
"name": "@hanamesh/dsh-app-host",
|
|
4
|
-
"version": "0.6.
|
|
4
|
+
"version": "0.6.15",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"source": "git+https://github.com/yzsnstotz/hanamesh-plugin-app-host.git",
|
|
7
7
|
"purpose": "Application ownership, view leases, Router forwarding, and application library for official DSH"
|
package/docs/PROVENANCE.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"currentCandidate": {
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.15",
|
|
4
4
|
"dshTarget": ">=0.2.0-rc.2 <0.3.0-0 || >=0.2.1-0 <0.3.0-0",
|
|
5
5
|
"vendorSha256VerifiedAtBuild": false,
|
|
6
6
|
"libProvisionDistribution": "随包分发、源仓不变; bundleDependencies; original LICENSE retained, no relicensing",
|
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hanamesh/dsh-app-host",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.15",
|
|
4
4
|
"description": "HanaMesh application ownership, durable view leases, and application library for official DSH 0.2.x",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE",
|
|
@@ -68,28 +68,6 @@
|
|
|
68
68
|
"docs/DSH_COMPATIBILITY.md",
|
|
69
69
|
"docs/INSTALLATION_STATUS.md"
|
|
70
70
|
],
|
|
71
|
-
"scripts": {
|
|
72
|
-
"build": "node scripts/build.mjs",
|
|
73
|
-
"test": "node scripts/safe-git.mjs node --test --test-reporter=tap --test-concurrency=1 tests/*.test.mjs",
|
|
74
|
-
"test:contract": "pnpm run build && node --test --test-reporter=tap --test-concurrency=1 tests/install-target-contract.test.mjs tests/catalog-major-compat.test.mjs",
|
|
75
|
-
"test:mutation": "node scripts/safe-git.mjs node scripts/mutations.mjs",
|
|
76
|
-
"test:types": "tsc --noEmit --strict --target ES2022 --module NodeNext --moduleResolution NodeNext tests/consumer.mts",
|
|
77
|
-
"test:native-driver": "node scripts/safe-git.mjs node --test --test-reporter=tap tests/native/driver.test.mjs",
|
|
78
|
-
"check": "node scripts/safe-git.mjs node scripts/check.mjs",
|
|
79
|
-
"prepack": "npm run build",
|
|
80
|
-
"demo": "node examples/demo.mjs",
|
|
81
|
-
"preflight:dsh": "node scripts/preflight-dsh.mjs",
|
|
82
|
-
"check:consistency": "node scripts/check-consistency.mjs",
|
|
83
|
-
"check:toolchain": "node scripts/check-toolchain.mjs",
|
|
84
|
-
"preflight": "node scripts/preflight.mjs",
|
|
85
|
-
"verify:pack": "node scripts/safe-git.mjs node scripts/verify-package.mjs",
|
|
86
|
-
"check:devkit-supply": "node scripts/check-devkit-consumption.mjs",
|
|
87
|
-
"check:provision-supply": "node scripts/check-provision-consumption.mjs",
|
|
88
|
-
"install:safe": "node scripts/safe-git.mjs pnpm install --frozen-lockfile",
|
|
89
|
-
"release:check": "hanamesh-release check --lockfile pnpm-lock.yaml",
|
|
90
|
-
"release:publish": "hanamesh-release publish --lockfile pnpm-lock.yaml",
|
|
91
|
-
"prepublishOnly": "hanamesh-release source-guard"
|
|
92
|
-
},
|
|
93
71
|
"dsh": {
|
|
94
72
|
"bundle": {
|
|
95
73
|
"patch": "./profile/cordis.patch.yml"
|
|
@@ -153,8 +131,27 @@
|
|
|
153
131
|
"@deepseek-ai/dsh-client-ui-renderer": "0.2.1-alpha.2",
|
|
154
132
|
"@hanamesh/release-platform-gate": "file:vendor/hanamesh-release-platform-gate-0.1.9.tgz"
|
|
155
133
|
},
|
|
156
|
-
"packageManager": "pnpm@10.33.0",
|
|
157
134
|
"bundleDependencies": [
|
|
158
135
|
"@hanamesh/lib-provision"
|
|
159
|
-
]
|
|
160
|
-
|
|
136
|
+
],
|
|
137
|
+
"scripts": {
|
|
138
|
+
"build": "node scripts/build.mjs",
|
|
139
|
+
"test": "node scripts/safe-git.mjs node --test --test-reporter=tap --test-concurrency=1 tests/*.test.mjs",
|
|
140
|
+
"test:contract": "pnpm run build && node --test --test-reporter=tap --test-concurrency=1 tests/install-target-contract.test.mjs tests/catalog-major-compat.test.mjs",
|
|
141
|
+
"test:mutation": "node scripts/safe-git.mjs node scripts/mutations.mjs",
|
|
142
|
+
"test:types": "tsc --noEmit --strict --target ES2022 --module NodeNext --moduleResolution NodeNext tests/consumer.mts",
|
|
143
|
+
"test:native-driver": "node scripts/safe-git.mjs node --test --test-reporter=tap tests/native/driver.test.mjs",
|
|
144
|
+
"check": "node scripts/safe-git.mjs node scripts/check.mjs",
|
|
145
|
+
"demo": "node examples/demo.mjs",
|
|
146
|
+
"preflight:dsh": "node scripts/preflight-dsh.mjs",
|
|
147
|
+
"check:consistency": "node scripts/check-consistency.mjs",
|
|
148
|
+
"check:toolchain": "node scripts/check-toolchain.mjs",
|
|
149
|
+
"preflight": "node scripts/preflight.mjs",
|
|
150
|
+
"verify:pack": "node scripts/safe-git.mjs node scripts/verify-package.mjs",
|
|
151
|
+
"check:devkit-supply": "node scripts/check-devkit-consumption.mjs",
|
|
152
|
+
"check:provision-supply": "node scripts/check-provision-consumption.mjs",
|
|
153
|
+
"install:safe": "node scripts/safe-git.mjs pnpm install --frozen-lockfile",
|
|
154
|
+
"release:check": "hanamesh-release check --lockfile pnpm-lock.yaml",
|
|
155
|
+
"release:publish": "hanamesh-release publish --lockfile pnpm-lock.yaml"
|
|
156
|
+
}
|
|
157
|
+
}
|