@hanamesh/dsh-app-host 0.6.15 → 0.6.16
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 +4 -2
- package/dist/client-ui.js +7 -3
- package/dist/dsh.js +2 -1
- package/dist/install-target/suite.js +2 -1
- package/dist/library/diagnostics.js +37 -0
- package/dist/library/install.js +52 -14
- package/dist/library/locate.js +21 -7
- package/dist/library/service.js +6 -2
- package/docs/CONTRACT.md +7 -0
- package/docs/LIBRARY.md +10 -0
- package/docs/LICENSES.json +8 -7
- package/docs/SECURITY.md +6 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
0.6.16 候选:普通市场通过注入的宿主 pluginManager.registries() 读取源事实,并按宿主锚点/版本守卫加载公开 ./registry 规则模块;AppHost 不声明、安装或捆绑 manager。发布依赖/peer闭包与0.6.15一致。pnpm自有源(null)不显式传--registry,配置镜像则传同一源;查询30秒具名超时,脱敏原诊断同进UI和日志。
|
|
2
|
+
|
|
1
3
|
0.6.15 候选:市场保留官方 CLI 的 stdout/stderr 真实失败原因并脱敏,在卡片与安装确认的技术详情中显示。正文显示 pnpm 真实错误类别;宿主发布时间检查失败时列出受影响包,说明默认 24 小时与实际 cutoff,并提示到 DSH 设置检查版本、升级 DSH 或等待后手动重试;不修改 profile、pnpm 供应链策略或安装执行路径。
|
|
2
4
|
|
|
3
5
|
官方桌面 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)。
|
|
@@ -224,7 +226,7 @@ Actions runs on the standard public/free `macos-15` arm64 runner because the exi
|
|
|
224
226
|
|
|
225
227
|
运行 `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。
|
|
226
228
|
|
|
227
|
-
共享门已升级到 R2 PASS 的 0.1.
|
|
229
|
+
共享门已升级到 R2 PASS 的 0.1.10,原正常工具 tar SHA256 为 `c72010c96a5f22e36d65f6c1fd850e583dead7e843644eba1657b4416d9cbd0d`;最终 main 来源见 `vendor/release-platform-gate.json`。本仓接线仍须 R1 独审,不等于 npm 发布放行,本次不发布。
|
|
228
230
|
|
|
229
231
|
`verify:pack` 原有 tarball 参数保持;正常消费者验证之后通过共享公开 checkRelease 核对同一个归档,CI沿该入口必经硬门,不另打一个包代替原包。接线没有复制扫描实现。
|
|
230
232
|
|
|
@@ -232,4 +234,4 @@ Actions runs on the standard public/free `macos-15` arm64 runner because the exi
|
|
|
232
234
|
|
|
233
235
|
发布说明:DSH 内置 pnpm 11.7.0 处理不适用平台 optional 子包时可能安装后不退出,使 DSH 一直等安装结束;因此本门是正确性不变量(`disableable:false`),没有关闭开关。所有 OS/CPU/libc 都必须具备平台中立的完整安装依赖闭包,不能用本机已装、omit-optional、ignore-scripts、超时、重试、放宽断言或改客户端代替。每次 PASS/FAIL 的 JSON/receipt 都包含触发背景和不能关闭的 reason;release:check 正常 pack,release:publish 只发布同一已检查且再核 SHA 的 tar。源码目录发布由 prepublishOnly 拒绝,CI 的 verify:pack/preflight 必经该门;无可失败 job 或旁路入口。
|
|
234
236
|
|
|
235
|
-
固定提供方 main 提交 `13ec0db3bc6aa6b6235f2105d3f0a6b998855ff5`,与 R2 审过的 0.1.
|
|
237
|
+
固定提供方 main 提交 `13ec0db3bc6aa6b6235f2105d3f0a6b998855ff5`,与 R2 审过的 0.1.10 正常包6载荷逐字一致;来源/归档摘要见 `vendor/release-platform-gate.json`。
|
package/dist/client-ui.js
CHANGED
|
@@ -129,7 +129,9 @@ window.__ModuleLoader__.load({id:'@hanamesh/dsh-app-host',factory:function(requi
|
|
|
129
129
|
"copy121": "固定规则(不能关)",
|
|
130
130
|
"copy122": "为什么:",
|
|
131
131
|
"copy123": "安装命令返回成功,但 profile 读回的版本与固定版本不一致",
|
|
132
|
-
"copy124": "
|
|
132
|
+
"copy124": "无法查询 DSH 配置的 registry;检查 DSH 源设置与网络,展开技术详情后手动重试",
|
|
133
|
+
"registryLookupTimeout": "registry 查询超过 30 秒;检查 DSH 源设置与网络后手动重试",
|
|
134
|
+
"registryConfigUnavailable": "DSH 未提供可读的源配置;检查客户端插件管理器,展开技术详情",
|
|
133
135
|
"copy125": "DSH 安装命令失败,请展开技术详情查看原始原因",
|
|
134
136
|
"installAgeRejected": "宿主的依赖发布时间检查未通过(默认需发布满 24 小时,以技术详情中的 cutoff 为准)。已知 DSH 0.2.0-rc.2 内置 pnpm 11.7.0 对同名多条白名单只认第一条。请在 DSH 设置中检查当前版本并升级 DSH,或等待受影响版本满足规则后手动重试。",
|
|
135
137
|
"installAffectedPackages": "受影响包:",
|
|
@@ -434,7 +436,9 @@ window.__ModuleLoader__.load({id:'@hanamesh/dsh-app-host',factory:function(requi
|
|
|
434
436
|
"copy121": "Required rules",
|
|
435
437
|
"copy122": "Reason: ",
|
|
436
438
|
"copy123": "Installation command succeeded, but the version read from the profile differs from the confirmed version",
|
|
437
|
-
"copy124": "
|
|
439
|
+
"copy124": "Cannot query the registry configured in DSH. Check DSH source settings and network, expand technical details, then retry manually",
|
|
440
|
+
"registryLookupTimeout": "Registry lookup exceeded 30 seconds. Check DSH source settings and network, then retry manually",
|
|
441
|
+
"registryConfigUnavailable": "DSH did not expose readable registry configuration. Check the client plugin manager and expand technical details",
|
|
438
442
|
"copy125": "DSH installation failed. Expand Technical details for the original cause",
|
|
439
443
|
"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
444
|
"installAffectedPackages": "Affected packages: ",
|
|
@@ -835,7 +839,7 @@ h('div',{className:'hm-source-add'},h('input',{value:draft,placeholder:'https://
|
|
|
835
839
|
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,
|
|
836
840
|
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));
|
|
837
841
|
}
|
|
838
|
-
const FAILURE_REASONS={...CORE_SUITE_FAILURES,INSTALL_READBACK_MISMATCH:t('安装命令返回成功,但 profile 读回的版本与固定版本不一致'),REGISTRY_LOOKUP_FAILED:t('
|
|
842
|
+
const FAILURE_REASONS={...CORE_SUITE_FAILURES,INSTALL_READBACK_MISMATCH:t('安装命令返回成功,但 profile 读回的版本与固定版本不一致'),REGISTRY_LOOKUP_FAILED:t('无法查询 DSH 配置的 registry;检查 DSH 源设置与网络,展开技术详情后手动重试'),REGISTRY_LOOKUP_TIMEOUT:t('registry 查询超过 30 秒;检查 DSH 源设置与网络后手动重试'),REGISTRY_CONFIG_UNAVAILABLE:t('DSH 未提供可读的源配置;检查客户端插件管理器,展开技术详情'),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
843
|
const failureReason=(code,details)=>{
|
|
840
844
|
const diagnostic=code==='DSH_PLUGIN_FAILED'?details?.diagnostic??'':'',pnpmCode=diagnostic.match(/\bERR_PNPM_[A-Z0-9_]+\b/)?.[0];
|
|
841
845
|
if(pnpmCode==='ERR_PNPM_MINIMUM_RELEASE_AGE_VIOLATION'){
|
package/dist/dsh.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import {pathToFileURL} from 'node:url';
|
|
1
2
|
import { createInstallationStatusReader } from './library/installation-status.js';
|
|
2
3
|
/**
|
|
3
4
|
* DSH plugin entry for @hanamesh/dsh-app-host, using the public APIs audited in
|
|
@@ -244,7 +245,7 @@ export async function apply(ctx, config) {
|
|
|
244
245
|
if(locations.profileDir&&locations.profileName){
|
|
245
246
|
provisionApi=await import('@hanamesh/lib-provision');
|
|
246
247
|
installer=nodeBinding(()=>nodeRuntime.executable,nodeBinary=>createLibraryInstaller({profileDir:locations.profileDir,profileName:locations.profileName,dataRoot,
|
|
247
|
-
...(cliUsable?{nodeBinary,dshBin,desktopCli:locations.desktopCli,desktopLauncher:locations.desktopLauncher}:{}),pluginManagerVersion:locations.pluginManagerVersion,profileWriter,
|
|
248
|
+
...(cliUsable?{nodeBinary,dshBin,desktopCli:locations.desktopCli,desktopLauncher:locations.desktopLauncher}:{}),pluginManagerVersion:locations.pluginManagerVersion,profileWriter,registryRules:locations.pluginManagerRegistry?()=>import(pathToFileURL(locations.pluginManagerRegistry).href):undefined,
|
|
248
249
|
registry:libraryConfig.registry,allowPrerelease:libraryConfig.allowPrerelease,provision:provisionApi.provision,remove:provisionApi.remove,ledger:provisionApi.ledger,
|
|
249
250
|
coreService:()=>coreService,suiteSourceService:()=>suiteSourceService,suiteSourceMode:()=>library?.suiteSourceSettings().mode??'configured',pluginManager:()=>pluginManager,emit:event=>library?.emit(event)}));
|
|
250
251
|
}
|
|
@@ -93,7 +93,8 @@ export async function sourceRecoveryProfile({recovery},item={}){
|
|
|
93
93
|
if(item.mode==='upgrade')await profile.seed(target.packageName,app.previous);
|
|
94
94
|
for(const [command,phase,skip] of item.inject??[])profile.fail(command,phase,skip);
|
|
95
95
|
if(item.concurrent)profile.concurrentAdd('@fixture/concurrent','1.0.0');
|
|
96
|
-
|
|
96
|
+
// Named single-source contract fixture; not production registry policy.
|
|
97
|
+
const installer=emit=>createLibraryInstaller({registryRules:async()=>({registryPlan:(_requested,configured)=>[configured.registry],attributeFailure:()=>'other'}),pluginManager:()=>({registries:async()=>({registry:'https://source-fixture.invalid/',fallbackRegistries:[],resolved:'https://source-fixture.invalid/'})}),profileDir:profile.profileDir,profileName:profile.profileName,dataRoot:profile.dataRoot,nodeBinary:'/source-fixture/bin/node',dshBin:'/source-fixture/dsh/lib/bin.js',
|
|
97
98
|
fetchImpl:profile.fetchImpl(item.registryMissing?{}:{[app.packageName]:app.latest,[plugin.packageName]:plugin.latest}),spawn:profile.spawn,provision:profile.provision,remove:profile.remove,ledger:profile.ledger,emit});
|
|
98
99
|
return{profile,target,installer};
|
|
99
100
|
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import {stripVTControlCharacters} from 'node:util';
|
|
2
|
+
import {redact} from '../runtime.js';
|
|
3
|
+
import {AppHostError} from '../errors.js';
|
|
4
|
+
|
|
5
|
+
// Diagnostic context only; the registry boundary retains native exceptions as Error.cause.
|
|
6
|
+
const phases=new WeakMap();
|
|
7
|
+
export function noteFailurePhase(error,phase){
|
|
8
|
+
if(error&&typeof error==='object'&&!phases.has(error))phases.set(error,phase);
|
|
9
|
+
}
|
|
10
|
+
const field=(value,key)=>{try{return value?.[key];}catch{return undefined;}};
|
|
11
|
+
function clean(value,limit=1000){
|
|
12
|
+
const text=typeof value==='string'?value:typeof value==='number'?String(value):'';
|
|
13
|
+
return redact(stripVTControlCharacters(text))
|
|
14
|
+
.replace(/(https?:\/\/)[^/\s@]+@/gi,'$1[REDACTED]@')
|
|
15
|
+
.replace(/([?&][^\s&=]+)=([^\s&"'<>]*)/g,'$1=[REDACTED]')
|
|
16
|
+
.replace(/((?:authorization|cookie|set-cookie)\s*["']?\s*[:=]\s*)[^\r\n]+/gi,'$1[REDACTED]')
|
|
17
|
+
.slice(0,limit);
|
|
18
|
+
}
|
|
19
|
+
export function installationDiagnostic(error){
|
|
20
|
+
const lines=[`Phase: ${phases.get(error)??'library operation'}`];
|
|
21
|
+
let cause=error;
|
|
22
|
+
for(let depth=0;cause&&depth<3;depth++){
|
|
23
|
+
const name=clean(field(cause,'name'),80)||'Error';
|
|
24
|
+
const message=clean(field(cause,'message')??(typeof cause==='string'?cause:''));
|
|
25
|
+
const code=clean(field(cause,'code'),100);
|
|
26
|
+
lines.push(`${depth?'Cause: ':''}${name}${code?` [${code}]`:''}: ${message}`);
|
|
27
|
+
cause=field(cause,'cause');
|
|
28
|
+
}
|
|
29
|
+
// Only known installer fields, never arbitrary details/stack/response objects.
|
|
30
|
+
if(error instanceof AppHostError&&error.code==='REGISTRY_LOOKUP_FAILED'&&Number.isInteger(error.details?.status))lines.push(`Registry HTTP status: ${error.details.status}`);
|
|
31
|
+
if(error instanceof AppHostError&&error.code==='DSH_PLUGIN_FAILED'){
|
|
32
|
+
lines.push(`CLI exit: ${Number.isInteger(error.details?.code)?error.details.code:'unknown'}; signal: ${clean(error.details?.signal,80)||'none'}`);
|
|
33
|
+
lines.push(`stderr: ${clean(error.details?.stderr)||'<empty>'}`);
|
|
34
|
+
if(typeof error.details?.diagnostic==='string')lines.push(`CLI output: ${clean(error.details.diagnostic,2000)}`);
|
|
35
|
+
}
|
|
36
|
+
return lines.join('\n').slice(0,4001);
|
|
37
|
+
}
|
package/dist/library/install.js
CHANGED
|
@@ -6,6 +6,7 @@ import { randomUUID } from 'node:crypto';
|
|
|
6
6
|
import { stripVTControlCharacters } from 'node:util';
|
|
7
7
|
import { redact } from '../runtime.js';
|
|
8
8
|
import { AppHostError, requireCondition, SerialQueue } from '../errors.js';
|
|
9
|
+
import {noteFailurePhase} from './diagnostics.js';
|
|
9
10
|
import { validateDefinition } from '../descriptor.js';
|
|
10
11
|
import { itemKind } from './catalog.js';
|
|
11
12
|
import { profileSnapshot, runtimeSnapshot, profileChecks, runtimeChecks, packageRestoreArgs, keepOriginals } from './recovery.js';
|
|
@@ -44,7 +45,7 @@ async function clearGeneratedRuntimeCache(result,runtimeRoot){
|
|
|
44
45
|
}
|
|
45
46
|
|
|
46
47
|
/** Package mutations use the public CLI in an owned monitored process; upstream's in-process manager has no scoped env/monitor port. */
|
|
47
|
-
export function createLibraryInstaller({profileDir,profileName,dataRoot,nodeBinary,dshBin,desktopCli,desktopLauncher,pluginManagerVersion,registry
|
|
48
|
+
export function createLibraryInstaller({profileDir,profileName,dataRoot,nodeBinary,dshBin,desktopCli,desktopLauncher,pluginManagerVersion,registryRules,registry,allowPrerelease=false,
|
|
48
49
|
fetchImpl=globalThis.fetch,spawn=defaultSpawn,provision,remove,ledger,coreSuitePlan,coreService=()=>undefined,suiteSourceMode=()=>'configured',suiteSourceService=()=>undefined,pluginManager=()=>undefined,emit=()=>{},profileWriter=new SerialQueue()}){
|
|
49
50
|
requireCondition(typeof profileDir==='string'&&resolve(profileDir)===profileDir,'PROFILE_DIR_REQUIRED','library.profileDir must be an absolute path.');
|
|
50
51
|
requireCondition(typeof dataRoot==='string'&&resolve(dataRoot)===dataRoot,'INVALID_ROOT','dataRoot must be absolute.');
|
|
@@ -64,20 +65,57 @@ export function createLibraryInstaller({profileDir,profileName,dataRoot,nodeBina
|
|
|
64
65
|
const runtimeApi=async name=>injected[name]??(await defaults())[name];
|
|
65
66
|
async function installed(packageNameValue){const root=join(profileDir,'node_modules',...packageNameValue.split('/')),pkg=await json(join(root,'package.json'));const appFile=typeof pkg.hanamesh?.app==='string'?pkg.hanamesh.app:'app.json';return{pkg,definition:validateDefinition(await json(join(root,appFile)))};}
|
|
66
67
|
const commandPath=command=>[dirname(command),...(process.platform==='win32'?(process.env.PATH??process.env.Path??'').split(delimiter):['/opt/homebrew/bin','/usr/local/bin','/usr/bin','/bin'])].join(delimiter);
|
|
67
|
-
async function run(args){requireCondition(cliAvailable,'DSH_CLI_UNAVAILABLE','The official dsh plugin CLI is unavailable in this host.',{need:['nodeBinary','dshBin']},503);
|
|
68
|
+
async function run(args){try{requireCondition(cliAvailable,'DSH_CLI_UNAVAILABLE','The official dsh plugin CLI is unavailable in this host.',{need:['nodeBinary','dshBin']},503);
|
|
68
69
|
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];
|
|
69
70
|
if(launcherAvailable)options.env=safeGitEnv({...options.env,DSH_HOME:desktopLauncher.home,PATH:commandPath(desktopLauncher.command)});
|
|
70
71
|
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);
|
|
71
72
|
const clean=text=>redact(stripVTControlCharacters(String(text??''))).replace(/(https?:\/\/)[^/\s@]+@/g,'$1[REDACTED]@').slice(-2000);
|
|
72
73
|
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;
|
|
74
|
-
|
|
74
|
+
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;
|
|
75
|
+
}catch(error){noteFailurePhase(error,'CLI run');throw error;}}
|
|
76
|
+
/** Bound the entire pre-install lookup, including public source discovery and metadata readback. */
|
|
75
77
|
async function latest(name){
|
|
76
|
-
const
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
78
|
+
const controller=new AbortController();let timer,phase='registry configuration';
|
|
79
|
+
const timeout=new Promise((_,reject)=>{timer=setTimeout(()=>{
|
|
80
|
+
const error=new AppHostError('REGISTRY_LOOKUP_TIMEOUT','Registry lookup timed out after 30000ms. Check DSH registry settings and network, then retry explicitly.',{},504);
|
|
81
|
+
noteFailurePhase(error,phase);controller.abort(error);reject(error);
|
|
82
|
+
},30000);});
|
|
83
|
+
async function lookup(){
|
|
84
|
+
const manager=pluginManager();
|
|
85
|
+
requireCondition(typeof manager?.registries==='function','REGISTRY_CONFIG_UNAVAILABLE','DSH does not expose registry configuration. Restore the public plugin manager before installing.',{},503);
|
|
86
|
+
// Public DSH ports own ordering, normalization, de-duplication and private-registry isolation.
|
|
87
|
+
const ruleMessage='DSH does not expose its registry rules. Restore the public plugin manager before installing.';
|
|
88
|
+
requireCondition(typeof registryRules==='function','REGISTRY_CONFIG_UNAVAILABLE',ruleMessage,{},503);
|
|
89
|
+
let rules;try{rules=await registryRules();}catch(cause){const error=new AppHostError('REGISTRY_CONFIG_UNAVAILABLE',ruleMessage,{},503);error.cause=cause;throw error;}
|
|
90
|
+
const {registryPlan,attributeFailure}=rules??{};
|
|
91
|
+
requireCondition(typeof registryPlan==='function'&&typeof attributeFailure==='function','REGISTRY_CONFIG_UNAVAILABLE',ruleMessage,{},503);
|
|
92
|
+
controller.signal.throwIfAborted();const configured=await manager.registries();controller.signal.throwIfAborted();
|
|
93
|
+
const plan=registryPlan(registry,configured);
|
|
94
|
+
for(let index=0;index<plan.length;index++){
|
|
95
|
+
const selected=plan[index],source=selected??configured.resolved;let failureKind='unknown';
|
|
96
|
+
controller.signal.throwIfAborted();phase='registry configuration';
|
|
97
|
+
requireCondition(typeof source==='string','REGISTRY_CONFIG_UNAVAILABLE','DSH could not resolve its configured registry. Check DSH registry settings.',{},503);
|
|
98
|
+
const base=new URL(source);requireCondition(['https:','http:'].includes(base.protocol),'REGISTRY_DENIED','Registry URL is invalid.');
|
|
99
|
+
try{
|
|
100
|
+
phase='registry fetch';let response;
|
|
101
|
+
try{response=await fetchImpl(new URL(`${base.href.replace(/\/$/,'')}/${encodeURIComponent(name)}/latest`),{headers:{accept:'application/json','accept-encoding':'identity'},signal:controller.signal});}
|
|
102
|
+
catch(error){failureKind='network';throw error;}
|
|
103
|
+
controller.signal.throwIfAborted();if(!response.ok){failureKind=response.status===404?'not-found':response.status>=500?'network':'unknown';throw new AppHostError('REGISTRY_LOOKUP_FAILED','Registry latest lookup failed. Check DSH registry settings and network, then retry explicitly.',{status:response.status},502);}
|
|
104
|
+
phase='registry readback';const metadata=await response.json();controller.signal.throwIfAborted();const version=metadata.version;
|
|
105
|
+
requireCondition(versionText(version)&&(allowPrerelease||!version.includes('-')),'UNSTABLE_VERSION','Registry latest must be an exact stable version.');
|
|
106
|
+
return{version,registry:selected};
|
|
107
|
+
}catch(error){
|
|
108
|
+
// Ask only fallbacks configured by DSH, and only when its public rule permits another source.
|
|
109
|
+
if(!controller.signal.aborted&&index+1<plan.length&&attributeFailure(failureKind,'',{kind:'registry',name,spec:name})==='registry')continue;
|
|
110
|
+
throw error;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
try{return await Promise.race([lookup(),timeout]);}
|
|
115
|
+
catch(cause){
|
|
116
|
+
const error=cause instanceof AppHostError?cause:new AppHostError('REGISTRY_LOOKUP_FAILED','Registry lookup failed. Check DSH registry settings and network, then retry explicitly.',{},502);
|
|
117
|
+
if(error!==cause)error.cause=cause;noteFailurePhase(error,phase);throw error;
|
|
118
|
+
}finally{clearTimeout(timer);}
|
|
81
119
|
}
|
|
82
120
|
/**
|
|
83
121
|
* rc.10 compensation after a failed install: the package goes back to its baseline spec through the official CLI, runtime
|
|
@@ -122,9 +160,9 @@ export function createLibraryInstaller({profileDir,profileName,dataRoot,nodeBina
|
|
|
122
160
|
const name=item.package.name,operationId=/^[A-Za-z0-9-]{1,64}$/.test(context?.operationId??'')?context.operationId:randomUUID();let stage='baseline',base,runtime;
|
|
123
161
|
try{
|
|
124
162
|
base=await profileSnapshot(profileDir,name);
|
|
125
|
-
stage='before-add';const version=await latest(name);
|
|
163
|
+
stage='before-add';const {version,registry:installRegistry}=await latest(name);
|
|
126
164
|
const status='restart-required';
|
|
127
|
-
stage='add';await run(['add','--save-exact',`${name}@${version}`]);
|
|
165
|
+
stage='add';await run(['add','--save-exact',...(installRegistry===null?[]:[`--registry=${installRegistry}`]),`${name}@${version}`]);
|
|
128
166
|
stage='readback';const added=await profileSnapshot(profileDir,name);
|
|
129
167
|
requireCondition(added.spec===version&&added.module===version,'INSTALL_READBACK_MISMATCH','The profile does not resolve the exact version that was added.',{expected:version,spec:added.spec,module:added.module},502);
|
|
130
168
|
if(kind==='plugin')return{status,kind,packageName:name,version};
|
|
@@ -132,7 +170,7 @@ export function createLibraryInstaller({profileDir,profileName,dataRoot,nodeBina
|
|
|
132
170
|
if(declared){stage='provision';const root=runtimeRoot(record.definition.id);runtime={root,base:await runtimeSnapshot(await runtimeApi('ledger'),root)};
|
|
133
171
|
await (await runtimeApi('provision'))(declared.manifest,{root,only:[declared.item],onProgress:event=>emit({type:'library.provision-progress',appId:record.definition.id,...event})});}
|
|
134
172
|
return{status:'restart-required',kind,appId:record.definition.id,packageName:name,version};
|
|
135
|
-
}catch(error){error.recovery=await recover({name,stage,base,runtime,operationId});throw error;}
|
|
173
|
+
}catch(error){noteFailurePhase(error,{baseline:'profile baseline','before-add':'registry fetch',add:'CLI run',readback:'profile readback',definition:'application readback',provision:'runtime provision'}[stage]);error.recovery=await recover({name,stage,base,runtime,operationId});throw error;}
|
|
136
174
|
}
|
|
137
175
|
async function installAs(item,kind,context){
|
|
138
176
|
permitted(item.package.name);const name=item.package.name;
|
|
@@ -141,7 +179,7 @@ export function createLibraryInstaller({profileDir,profileName,dataRoot,nodeBina
|
|
|
141
179
|
catch(error){emit({type:'library.install-failed',...(item.id?{itemId:item.id}:{}),packageName:name,kind,code:error.code??'INSTALL_FAILED',...(error.recovery?{recovery:error.recovery}:{})});throw error;}
|
|
142
180
|
}
|
|
143
181
|
let suitePlan;
|
|
144
|
-
const suiteProvider=coreSuitePlan??createCoreSuiteAdapter({core:coreService,registry,suiteSourceMode,suiteSourceService});
|
|
182
|
+
const suiteProvider=coreSuitePlan??createCoreSuiteAdapter({core:coreService,registry:registry??'https://registry.npmjs.org',suiteSourceMode,suiteSourceService});
|
|
145
183
|
async function previewSuite(){
|
|
146
184
|
const next=await prepareSuiteUpgrade({coreSuitePlan:suiteProvider});
|
|
147
185
|
suitePlan={...next,planId:randomUUID()};return structuredClone(suitePlan);
|
|
@@ -167,7 +205,7 @@ export function createLibraryInstaller({profileDir,profileName,dataRoot,nodeBina
|
|
|
167
205
|
requireCondition(identity(executing)===identity(confirmed),'SUITE_PLAN_CHANGED','执行前套件安装包来源或声明已变化,请重新查看并确认。',{},409);
|
|
168
206
|
emit({type:'library.install-started',itemId:'hanamesh:suite-upgrade',packageName:'@hanamesh/core',kind:'suite-upgrade'});
|
|
169
207
|
try{
|
|
170
|
-
await run(['add','--save-exact','--strict-peer-dependencies',`--registry=${fresh.registry??registry}`,...fresh.components.map(c=>`${c.name}@${c.to}`)]);
|
|
208
|
+
await run(['add','--save-exact','--strict-peer-dependencies',`--registry=${fresh.registry??registry??'https://registry.npmjs.org'}`,...fresh.components.map(c=>`${c.name}@${c.to}`)]);
|
|
171
209
|
const actual=await readSuiteStatus(profileDir);
|
|
172
210
|
const saved=await json(join(profileDir,'package.json'));
|
|
173
211
|
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);
|
package/dist/library/locate.js
CHANGED
|
@@ -13,7 +13,7 @@ import { constants } from 'node:fs';
|
|
|
13
13
|
import { execFile } from 'node:child_process';
|
|
14
14
|
import { promisify } from 'node:util';
|
|
15
15
|
import { createRequire } from 'node:module';
|
|
16
|
-
import { basename, dirname, isAbsolute, join, sep } from 'node:path';
|
|
16
|
+
import { basename, dirname, isAbsolute, join, resolve, sep } from 'node:path';
|
|
17
17
|
import { fileURLToPath } from 'node:url';
|
|
18
18
|
|
|
19
19
|
export const PACKAGE_NAME='@hanamesh/dsh-app-host';
|
|
@@ -23,9 +23,22 @@ const DSH_BIN_SUFFIX='/@deepseek-ai/dsh/lib/bin.js';
|
|
|
23
23
|
|
|
24
24
|
async function json(path){try{return JSON.parse(await readFile(path,'utf8'));}catch{return undefined;}}
|
|
25
25
|
/** The public package manifest resolved by the host's installation/profile anchor. No service flags are inferred. */
|
|
26
|
-
|
|
27
|
-
if(typeof anchor!=='string'||!isAbsolute(anchor))return
|
|
28
|
-
try{
|
|
26
|
+
async function pluginManagerFacts(anchor){
|
|
27
|
+
if(typeof anchor!=='string'||!isAbsolute(anchor))return {};
|
|
28
|
+
try{
|
|
29
|
+
const require=createRequire(anchor),manifestPath=require.resolve('@deepseek-ai/dsh-plugin-manager/package.json'),pkg=await json(manifestPath);
|
|
30
|
+
if(pkg?.name!=='@deepseek-ai/dsh-plugin-manager'||typeof pkg.version!=='string')return {};
|
|
31
|
+
let pluginManagerRegistry;
|
|
32
|
+
try{const path=require.resolve('@deepseek-ai/dsh-plugin-manager/registry');if(path.startsWith(dirname(manifestPath)+sep))pluginManagerRegistry=path;}catch{}
|
|
33
|
+
return{pluginManagerVersion:pkg.version,...(pluginManagerRegistry?{pluginManagerRegistry}:{})};
|
|
34
|
+
}catch{return {};}
|
|
35
|
+
}
|
|
36
|
+
export async function inferPluginManagerVersion(anchor){return(await pluginManagerFacts(anchor)).pluginManagerVersion;}
|
|
37
|
+
async function desktopRegistryPath(root,pkg){
|
|
38
|
+
const entry=pkg.exports?.['./registry'],target=typeof entry==='string'?entry:entry?.import??entry?.default;
|
|
39
|
+
if(typeof target!=='string'||!target.startsWith('./'))return undefined;
|
|
40
|
+
const path=resolve(root,target);if(!path.startsWith(root+sep))return undefined;
|
|
41
|
+
try{await access(path);return path;}catch{return undefined;}
|
|
29
42
|
}
|
|
30
43
|
/** A DSH profile manifest that carries HanaMesh: `dsh.profile` (written by `dsh plugin`) or a hanamesh dependency. */
|
|
31
44
|
export function isHanameshProfileManifest(manifest){
|
|
@@ -96,6 +109,7 @@ export async function inferDesktopLayout({moduleUrl=import.meta.url,argv=process
|
|
|
96
109
|
}
|
|
97
110
|
const managerManifest=await json(join(runtimeDir,'node_modules/@deepseek-ai/dsh-plugin-manager/package.json'));
|
|
98
111
|
const pluginManagerVersion=managerManifest?.name==='@deepseek-ai/dsh-plugin-manager'&&managerManifest.version===hostManifest.version?managerManifest.version:undefined;
|
|
112
|
+
const pluginManagerRegistry=pluginManagerVersion?await desktopRegistryPath(join(runtimeDir,'node_modules/@deepseek-ai/dsh-plugin-manager'),managerManifest):undefined;
|
|
99
113
|
// Current canonical runtime manifest only. No legacy descriptor inference or PATH search.
|
|
100
114
|
if(metadata?.platform===process.platform&&metadata?.arch===process.arch&&
|
|
101
115
|
metadata.desktopVersion===(await json(join(hostRoot,'package.json')))?.version&&/^24\.\d+\.\d+$/.test(metadata.node??'')) {
|
|
@@ -107,7 +121,7 @@ export async function inferDesktopLayout({moduleUrl=import.meta.url,argv=process
|
|
|
107
121
|
if(actual.node===metadata.node&&actual.electron===undefined)nodeBinary=await realpath(node);
|
|
108
122
|
} catch { /* Unavailable/unproven payload Node stays unavailable; never use Electron. */ }
|
|
109
123
|
}
|
|
110
|
-
return {profileDir:await realpath(profileDir),profileName:'desktop',dshBin,parentOrigin:'dsh-app://app',frameAncestors:[],desktopCli,desktopLauncher,pluginManagerVersion,nodeBinary,runtimeDir};
|
|
124
|
+
return {profileDir:await realpath(profileDir),profileName:'desktop',dshBin,parentOrigin:'dsh-app://app',frameAncestors:[],desktopCli,desktopLauncher,pluginManagerVersion,pluginManagerRegistry,nodeBinary,runtimeDir};
|
|
111
125
|
}
|
|
112
126
|
|
|
113
127
|
/** The DSH CLI that started this process — `argv[1]` only when it IS `@deepseek-ai/dsh/lib/bin.js`. */
|
|
@@ -135,6 +149,6 @@ export async function resolveLibraryLocations(config={},{moduleUrl=import.meta.u
|
|
|
135
149
|
let dshBin=config.dshBin;if(dshBin===undefined){dshBin=desktop?.dshBin??inferDshBin(argv);if(dshBin)inferred.push('dshBin');}
|
|
136
150
|
let node=nodeBinary;if(node===undefined){node=desktop?.nodeBinary??inferNodeBinary({execPath,versions});if(node)inferred.push('nodeBinary');}
|
|
137
151
|
let sources=config.sources;if(sources===undefined){sources=structuredClone(DEFAULT_CATALOG_SOURCES);inferred.push('sources');}
|
|
138
|
-
const pluginManagerVersion=desktop
|
|
139
|
-
return{profileDir,profileName,dshBin,nodeBinary:node,sources,inferred,...(pluginManagerVersion?{pluginManagerVersion}:{}),...(desktop&&desktop.profileDir===profileDir&&config.dshBin===undefined?{desktopCli:desktop.desktopCli,desktopLauncher:desktop.desktopLauncher}:{})};
|
|
152
|
+
const {pluginManagerVersion,pluginManagerRegistry}=desktop??await pluginManagerFacts(inferDshBin(argv)??(profileDir?join(profileDir,'package.json'):undefined));
|
|
153
|
+
return{profileDir,profileName,dshBin,nodeBinary:node,sources,inferred,...(pluginManagerVersion?{pluginManagerVersion}:{}),...(pluginManagerRegistry?{pluginManagerRegistry}:{}),...(desktop&&desktop.profileDir===profileDir&&config.dshBin===undefined?{desktopCli:desktop.desktopCli,desktopLauncher:desktop.desktopLauncher}:{})};
|
|
140
154
|
}
|
package/dist/library/service.js
CHANGED
|
@@ -6,6 +6,7 @@ import {SUITE_TARGET,SUITE_PACKAGES,readSuiteStatus,SUITE_INSTALL_COMMAND,CORE_S
|
|
|
6
6
|
import { PROTECTED_PACKAGES } from './install.js';
|
|
7
7
|
import { handoffSettings } from './handoff.js';
|
|
8
8
|
import { INSTALLATION_STATUS_CAPABILITY } from '../installation-status.js';
|
|
9
|
+
import {installationDiagnostic} from './diagnostics.js';
|
|
9
10
|
|
|
10
11
|
/** Install-target contract version this market implements (schemas/install-target.schema.json). */
|
|
11
12
|
export const INSTALL_TARGET_CONTRACT_VERSION='1';
|
|
@@ -38,8 +39,11 @@ export function createLibraryService({domain,host,config={},dataRoot,ledgerReade
|
|
|
38
39
|
function persist(update){return settingsWriter.run(async()=>{const next={...update(state),revision:state.revision+1};await domain.global.set(next);state=next;});}
|
|
39
40
|
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
41
|
const details=error.details?.stage==='suite-upgrade'?{stage:'suite-upgrade',actual:error.details.actual,command:SUITE_INSTALL_COMMAND}:{};
|
|
41
|
-
|
|
42
|
-
emit({type:`library.${type}-failed`,operationId,code:error.code??'LIBRARY_OPERATION_FAILED',...(error.recovery?{recovery:error.recovery}:{})
|
|
42
|
+
details.diagnostic=installationDiagnostic(error);
|
|
43
|
+
emit({type:`library.${type}-failed`,operationId,code:error.code??'LIBRARY_OPERATION_FAILED',...(error.recovery?{recovery:error.recovery}:{}),details});
|
|
44
|
+
try{warn('hanamesh-app-host library: %s operation %s failed\n%s',type,operationId,details.diagnostic);}
|
|
45
|
+
catch{try{console.warn('hanamesh-app-host library: diagnostic logger unavailable; operation %s\n%s',operationId,details.diagnostic);}catch{}}
|
|
46
|
+
throw error;
|
|
43
47
|
}).finally(()=>running.delete(operationId));running.set(operationId,work);work.catch(()=>{});return{operationId,status:'started'};}
|
|
44
48
|
const requireInstaller=(message)=>requireCondition(ready(),'LIBRARY_INSTALL_UNAVAILABLE',message,{need:unavailable()},503);
|
|
45
49
|
/**
|
package/docs/CONTRACT.md
CHANGED
|
@@ -252,3 +252,10 @@ B17 review repair(同0.6.8候选):`AppHost` 未注入 `runtimeLedgerReader
|
|
|
252
252
|
|
|
253
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
254
|
历史 GitHub DTO 按已审标准分发提议继续可读:source 只许 source/repo/ref,repo=目标 githubRepo,ref 为40位hex;可缺历史版本事实,不自动转换或选备用源。目录发布事实不作为 Core 兼容声明、安装授权或本机已装观察。随包 catalog/schema/page.json 同时描述默认和 HanaMesh 两视图,跨字段相等性由运行解码器检查。
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
## 0.6.16 市场查询来源与诊断(原协议1)
|
|
258
|
+
|
|
259
|
+
普通市场安装/升级的 latest 查询消费 DSH 公开 pluginManager.registries() 与公开 registryPlan/attributeFailure,沿用用户配置源顺序和私有源隔离规则;成功后将同一源选择传入原受控CLI:显式源用--registry=<source>,pnpm自有源(null)不传--registry以保留scope/认证配置。整个配置/查询/读回共30000ms,超时abort并报REGISTRY_LOOKUP_TIMEOUT,未提供公开能力/解析源报REGISTRY_CONFIG_UNAVAILABLE;原生异常为REGISTRY_LOOKUP_FAILED并保留cause。没有新的用户配置、请求字段或协议版本。既有 details.diagnostic 带脱敏类型/消息/cause.code/实际阶段及已知CLI exit/stderr摘要,同时写宿主日志;其它错误、回滚与重启语义保留。Core套件升级路径的声明/源和缺省值单独保留,不采用普通市场的源选择。
|
|
260
|
+
|
|
261
|
+
0.6.16最小接线:公开规则模块从宿主manager同锚点/版本守卫加载,不在AppHost声明manager依赖/peer;找不到模块或公开函数则REGISTRY_CONFIG_UNAVAILABLE,无npmjs回退或自创排序。发布依赖闭包与0.6.15一致。
|
package/docs/LIBRARY.md
CHANGED
|
@@ -84,3 +84,13 @@ HanaMesh 视图原样保留可选 `official:boolean`(包括 false)与 C-INDE
|
|
|
84
84
|
|
|
85
85
|
|
|
86
86
|
0.6.12:目录条目的展示/跳转链接(`sourceLinks.sourceUrl`、`homepage`、`repository.url`、`publisher.url`)接受 fragment 并原样保留;目录自身 attribution 展示链接亦保留 fragment。manifest 来源和 `transport.endpoint` 等请求 URL 校验不变。单条非法仍拒绝整个 provider page,不返回部分结果。
|
|
87
|
+
|
|
88
|
+
## 0.6.16 · 安装查询来源与失败诊断
|
|
89
|
+
|
|
90
|
+
市场通过公开 `pluginManager.registries()` 读取 DSH 的首选源、顺序 fallback 和 pnpm 已解析的源;从宿主自己的安装锚点按既有版本守卫定位 `exports['./registry']`,动态加载其公开 `registryPlan` / `attributeFailure`,不读取私有配置或发明兜底源。`library.registry` 若显式设置,沿用 DSH 对请求源的规则(外部私有源单独使用)。查询 latest 成功后,精确版本及同一源选择传入原受控CLI:显式配置源用 `add --save-exact --registry=<source>`,DSH的pnpm自有源(null)沿原生语义不传 `--registry`,保留scope源和认证配置;缺公开源能力或 pnpm 自有源无法解析,明确 `REGISTRY_CONFIG_UNAVAILABLE`,不猜 npmjs。DSH rc.2、alpha.2、rc.3 均公开这些端口。
|
|
91
|
+
|
|
92
|
+
整个查询(配置读取、配置源顺序查询、JSON 读回)共享 30000ms 上限。到期 abort 并报 `REGISTRY_LOOKUP_TIMEOUT`,不重试、不在超时后继续 fallback。只有 DSH 规则允许的网络/未找到失败才查询其已配置后继源;HTTP 认证/权限拒绝及无效 JSON 不换源。包版本稳定性策略、受控 CLI、读回、补偿、restart-required 行为保留。
|
|
93
|
+
|
|
94
|
+
原生 lookup 异常包为 `REGISTRY_LOOKUP_FAILED`,原 Error 保留于 cause;已知具名码保留。`library.*-failed.details.diagnostic` 最多4001字符,含脱敏异常类型/消息/cause.code 与实际 registry configuration/fetch/readback、CLI run、profile readback 阶段;CLI 非零另含 exit、signal 与 stderr/输出摘要。同一摘要及 operationId 写宿主 logger.warn(logger失效时安全console),UI已有可展开技术详情显示,并提示检查 DSH 源设置/网络后手动重试。不记录请求头、正文、环境、stack 或任意 details;不猜历史丢失的原错。
|
|
95
|
+
|
|
96
|
+
规则模块只由宿主公开exports提供;普通DSH用与manager版本查询相同createRequire(anchor),Desktop限已证明的runtimeDir与匹配版本。没有新增manager依赖/peer、没有生产规则副本;无法定位/加载规则或端口不完整均REGISTRY_CONFIG_UNAVAILABLE。钉SHA的rc.2规则仅位于tests/fixtures,不进入发布包。
|
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.16",
|
|
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"
|
|
@@ -281,18 +281,19 @@
|
|
|
281
281
|
}
|
|
282
282
|
},
|
|
283
283
|
"@hanamesh/release-platform-gate": {
|
|
284
|
-
"version": "0.1.
|
|
284
|
+
"version": "0.1.10",
|
|
285
285
|
"license": "UNKNOWN: private development tool does not declare a license",
|
|
286
|
-
"source": "file:vendor/hanamesh-release-platform-gate-0.1.
|
|
287
|
-
"purpose": "Development-only shared immutable platform
|
|
286
|
+
"source": "file:vendor/hanamesh-release-platform-gate-0.1.10.tgz",
|
|
287
|
+
"purpose": "Development-only shared immutable platform gate0.1.10; R2 PASS docs cce1576, source C7a39e4e and same reviewed archive SHA; never runtime or bundled",
|
|
288
288
|
"usedAs": [
|
|
289
289
|
"devDependencies"
|
|
290
290
|
],
|
|
291
291
|
"specs": {
|
|
292
|
-
"devDependencies": "file:vendor/hanamesh-release-platform-gate-0.1.
|
|
292
|
+
"devDependencies": "file:vendor/hanamesh-release-platform-gate-0.1.10.tgz"
|
|
293
293
|
},
|
|
294
|
-
"sourceCommit": "
|
|
295
|
-
"archiveSha256": "
|
|
294
|
+
"sourceCommit": "7a39e4e332a2cbb5dcbfddec68383fb5cef39eb8",
|
|
295
|
+
"archiveSha256": "71545cbb636d3b1a5e7d9fbba728b16e006b8eaf70bc2540038d077df7263a61",
|
|
296
|
+
"reviewDocs": "cce157612b1390b845ccca3f0632247de9fb3229"
|
|
296
297
|
}
|
|
297
298
|
},
|
|
298
299
|
"downloadedRuntimes": {
|
package/docs/SECURITY.md
CHANGED
|
@@ -49,6 +49,8 @@ DSH/宿主 cookie、控制头与 Authorization 默认不传上游;应用 cooki
|
|
|
49
49
|
|
|
50
50
|
## 日志与凭据
|
|
51
51
|
|
|
52
|
+
市场异步操作失败只投影有界脱敏的Error类型/消息、最多两层cause与既有CLI退出摘要,记录实际执行阶段和operationId;同一安全诊断进入技术详情与宿主logger。拒绝任意details、stack、response对象;Bearer、常见secret字段、URL userinfo/query和Authorization/Cookie行脱敏。不记录环境/请求正文,不承诺识别任意业务敏感文本。原生 registry 查询异常以 REGISTRY_LOOKUP_FAILED 具名并保留原 cause;其它安装异常保持原对象,logger故障不覆盖原异常。
|
|
53
|
+
|
|
52
54
|
日志有界、按完整行处理、Bearer/常见 token 字段及显式 secret env 值脱敏;超大行丢弃。此规则不可能识别任意业务敏感信息,默认不记录请求体/配置环境。leaseToken 原文只在回执中返回,磁盘保存哈希;工作台不得把 token 放进公开 URL、分析日志或应用 iframe。
|
|
53
55
|
|
|
54
56
|
恢复凭据属于同一已认证 owner 的显式操作,要求 confirm:true,旋转 generation;真实 DSH bridge 的 authorize 必须对该路径落实授权。示例/测试的固定认证值绝不能作为真实 DSH 身份实现。
|
|
@@ -81,3 +83,7 @@ The authenticated browser supplies settings or opaque plan/operation identifiers
|
|
|
81
83
|
|
|
82
84
|
|
|
83
85
|
B17 候选的套件来源选择仅在 AppHost 自有同源已准入设置端点写入。Core 来源状态是公开只读事实,不授予安装权限;预览/确认/执行及异步声明返回时复读来源,沿用原 planId/精确 tuple/物理 baseline,来源丢失或替换即拒绝原确认。设置保存只串行持久化选择,不触发下载/安装或改写 Core 配置;持久失败保持旧选择。无 service/watch/generation 时保持不可观察边界,不缓存 ready 或回退 registry。详见 CONTRACT.md B17 段。
|
|
86
|
+
|
|
87
|
+
0.6.16:只消费 DSH 公开源事实和公开排序/后继许可规则,不读 .npmrc、凭据、私有服务字段;配置源与受控 CLI 使用同一选择;null项沿用pnpm自有配置,不显式传registry。30秒上限只用于本次安装前查询,超时终止且不重试;配置源的顺序由 DSH 决定。诊断只投影脱敏的类型/消息/code/cause链及已知CLI字段,同时进既有UI和宿主日志;URL用户信息、全部查询值、Authorization/Cookie、token/secret均删除,限4001字符,不传任意details/stack/响应正文。
|
|
88
|
+
|
|
89
|
+
公开registry规则只从已证明的宿主manager安装位置加载,复用原版本守卫;不安装第二份manager、不复制生产规则、不从任意浏览器路径加载模块。tests/fixtures的原始MIT规则与SHA/许可证只作测试,不进发布包。
|
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.16",
|
|
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",
|
|
@@ -129,7 +129,7 @@
|
|
|
129
129
|
"typescript": "5.8.3",
|
|
130
130
|
"@hanamesh/devkit": "file:vendor/hanamesh-devkit-0.2.3.tgz",
|
|
131
131
|
"@deepseek-ai/dsh-client-ui-renderer": "0.2.1-alpha.2",
|
|
132
|
-
"@hanamesh/release-platform-gate": "file:vendor/hanamesh-release-platform-gate-0.1.
|
|
132
|
+
"@hanamesh/release-platform-gate": "file:vendor/hanamesh-release-platform-gate-0.1.10.tgz"
|
|
133
133
|
},
|
|
134
134
|
"bundleDependencies": [
|
|
135
135
|
"@hanamesh/lib-provision"
|