@hanamesh/dsh-app-host 0.6.16 → 0.6.17

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 CHANGED
@@ -235,3 +235,7 @@ Actions runs on the standard public/free `macos-15` arm64 runner because the exi
235
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 或旁路入口。
236
236
 
237
237
  固定提供方 main 提交 `13ec0db3bc6aa6b6235f2105d3f0a6b998855ff5`,与 R2 审过的 0.1.10 正常包6载荷逐字一致;来源/归档摘要见 `vendor/release-platform-gate.json`。
238
+
239
+ ## Core 公开套件声明的测试依赖
240
+
241
+ 源码测试通过 `@hanamesh/core/suite` 读取 Core 单源公开命令。`@hanamesh/core` 是仓库声明的 devDependency,沿本仓现有固定 vendor 正常包写法消费 Core main `62059ca586be2bffa054b5d392ebde41c51939ec` /0.3.28 的 R2 已审原件;来源、许可与完整 SHA 见 `vendor/supply.json`,声明指向本仓固定提交 `cb0bb9f7da4ca817649b65882e06ea2103b9b49d` 中该原件的公开 HTTPS tarball;现有 workflow 的 `vendor/SHA256SUMS` 和冻结 pnpm 锁分别验证同一归档的 SHA256/SRI,许可证记录见 `docs/LICENSES.json`。公开不可变 URL 符合现有发布元数据测试对新增依赖禁止 `file:/link:` 的要求,不增加例外或改变断言。干净 clone 直接按 workflow 安装,无需本机 provider consumer、手工连接或额外输入;Core 不成为运行时依赖或 bundled 包。Git 源的原 prepack 会安装私有 Devkit 开发来源,不能用于当前无凭据 CI;这里保留已审正常包原字节,不改 Core、不绕构建检查、不发布 npm。
@@ -10,7 +10,7 @@ import {noteFailurePhase} from './diagnostics.js';
10
10
  import { validateDefinition } from '../descriptor.js';
11
11
  import { itemKind } from './catalog.js';
12
12
  import { profileSnapshot, runtimeSnapshot, profileChecks, runtimeChecks, packageRestoreArgs, keepOriginals } from './recovery.js';
13
- import {prepareSuiteUpgrade,readSuiteStatus,createCoreSuiteAdapter,SUITE_INSTALL_COMMAND} from './suite.js';
13
+ import {prepareSuiteUpgrade,readSuiteStatus,createCoreSuiteAdapter} from './suite.js';
14
14
 
15
15
  /** The HanaMesh suite itself (real names + the historical scoped spellings) is never installed or removed through the market. */
16
16
  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']);
@@ -122,13 +122,13 @@ export function createLibraryInstaller({profileDir,profileName,dataRoot,nodeBina
122
122
  * items this operation newly claimed are removed by ownership, a replaced baseline item is provisioned again from the
123
123
  * restored definition, and then every item is read back. `restored`/`not-needed` only when every row matches the baseline.
124
124
  */
125
- async function recover({name,stage,base,runtime,operationId}){
125
+ async function recover({name,stage,base,runtime,operationId,installRegistry}){
126
126
  const report={status:'failed',mode:base?.spec===null?'new-install':'upgrade',stage,previousVersion:base?.module??null,actions:[],checks:[],residue:[]};
127
127
  if(!base){report.mode='unknown';report.status='not-needed';return report;}
128
128
  const fail=error=>{report.error??={code:error.code??'INSTALL_RECOVERY_FAILED',message:String(error.message??error)};};
129
129
  try{
130
130
  let now=await profileSnapshot(profileDir,name);const args=packageRestoreArgs(base,now,name);
131
- if(args){report.actions.push(['dsh','plugin',...args].join(' '));try{await run(args);}catch(error){fail(error);}now=await profileSnapshot(profileDir,name);}
131
+ if(args){report.actions.push(['dsh','plugin',...args].join(' '));try{await run([args[0],...(args[0]!=='remove'&&installRegistry!==null?[`--registry=${installRegistry}`]:[]),...args.slice(1)]);}catch(error){fail(error);}now=await profileSnapshot(profileDir,name);}
132
132
  report.checks.push(...profileChecks(base,now,name));
133
133
  }catch(error){fail(error);report.residue.push({item:'profile',reason:'NOT_VERIFIED'});}
134
134
  if(runtime)try{
@@ -157,10 +157,10 @@ export function createLibraryInstaller({profileDir,profileName,dataRoot,nodeBina
157
157
  */
158
158
  async function transaction(item,kind,context){
159
159
  requireCondition(cliAvailable,'DSH_EXACT_INSTALL_UNAVAILABLE','The host cannot install a package with an exact saved version.',{need:unavailable()},503);
160
- const name=item.package.name,operationId=/^[A-Za-z0-9-]{1,64}$/.test(context?.operationId??'')?context.operationId:randomUUID();let stage='baseline',base,runtime;
160
+ const name=item.package.name,operationId=/^[A-Za-z0-9-]{1,64}$/.test(context?.operationId??'')?context.operationId:randomUUID();let stage='baseline',base,runtime,installRegistry=null;
161
161
  try{
162
162
  base=await profileSnapshot(profileDir,name);
163
- stage='before-add';const {version,registry:installRegistry}=await latest(name);
163
+ stage='before-add';let version;({version,registry:installRegistry}=await latest(name));
164
164
  const status='restart-required';
165
165
  stage='add';await run(['add','--save-exact',...(installRegistry===null?[]:[`--registry=${installRegistry}`]),`${name}@${version}`]);
166
166
  stage='readback';const added=await profileSnapshot(profileDir,name);
@@ -170,7 +170,7 @@ export function createLibraryInstaller({profileDir,profileName,dataRoot,nodeBina
170
170
  if(declared){stage='provision';const root=runtimeRoot(record.definition.id);runtime={root,base:await runtimeSnapshot(await runtimeApi('ledger'),root)};
171
171
  await (await runtimeApi('provision'))(declared.manifest,{root,only:[declared.item],onProgress:event=>emit({type:'library.provision-progress',appId:record.definition.id,...event})});}
172
172
  return{status:'restart-required',kind,appId:record.definition.id,packageName:name,version};
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;}
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,installRegistry});throw error;}
174
174
  }
175
175
  async function installAs(item,kind,context){
176
176
  permitted(item.package.name);const name=item.package.name;
@@ -211,7 +211,7 @@ export function createLibraryInstaller({profileDir,profileName,dataRoot,nodeBina
211
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);
212
212
  return{status:'restart-required',kind:'suite-upgrade',packageName:'@hanamesh/core',components:fresh.components,version:actual.version};
213
213
  }catch(error){
214
- error.details={...error.details,stage:'suite-upgrade',actual:await readSuiteStatus(profileDir),command:SUITE_INSTALL_COMMAND};
214
+ error.details={...error.details,stage:'suite-upgrade',actual:await readSuiteStatus(profileDir),command:fresh.installCommand};
215
215
  emit({type:'library.install-failed',packageName:'@hanamesh/core',code:error.code??'SUITE_UPGRADE_FAILED'});throw error;
216
216
  }
217
217
  });},
@@ -2,7 +2,7 @@ import { randomUUID } from 'node:crypto';
2
2
  import { AppHostError, requireCondition, SerialQueue } from '../errors.js';
3
3
  import { loadCatalog, isApplication, itemKind, upgradeAvailable, validateManifestUrl } from './catalog.js';
4
4
  import { scanInstalled, scanInstalledPlugins, snapshotProfileDependencies } from './installed.js';
5
- import {SUITE_TARGET,SUITE_PACKAGES,readSuiteStatus,SUITE_INSTALL_COMMAND,CORE_SUITE_DECLARATION_MISSING,readSuiteSourceState} from './suite.js';
5
+ import {SUITE_TARGET,SUITE_PACKAGES,readSuiteStatus,CORE_SUITE_DECLARATION_MISSING,readSuiteSourceState} from './suite.js';
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';
@@ -38,7 +38,7 @@ export function createLibraryService({domain,host,config={},dataRoot,ledgerReade
38
38
  const settingsWriter=new SerialQueue();
39
39
  function persist(update){return settingsWriter.run(async()=>{const next={...update(state),revision:state.revision+1};await domain.global.set(next);state=next;});}
40
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=>{
41
- const details=error.details?.stage==='suite-upgrade'?{stage:'suite-upgrade',actual:error.details.actual,command:SUITE_INSTALL_COMMAND}:{};
41
+ const details=error.details?.stage==='suite-upgrade'?{stage:'suite-upgrade',actual:error.details.actual,command:error.details.command}:{};
42
42
  details.diagnostic=installationDiagnostic(error);
43
43
  emit({type:`library.${type}-failed`,operationId,code:error.code??'LIBRARY_OPERATION_FAILED',...(error.recovery?{recovery:error.recovery}:{}),details});
44
44
  try{warn('hanamesh-app-host library: %s operation %s failed\n%s',type,operationId,details.diagnostic);}
@@ -152,7 +152,7 @@ export function createLibraryService({domain,host,config={},dataRoot,ledgerReade
152
152
  get live(){return Boolean(installer?.live);},
153
153
  async upgradeSuite(input){requireInstaller('Suite upgrade is unavailable.');
154
154
  requireCondition(input&&Object.keys(input).length===1&&typeof input.planId==='string','INVALID_INPUT','Only the current planId is accepted.');
155
- 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;}});
155
+ 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:error.details.command};emit({type:'library.install-failed',packageName:'@hanamesh/core',code:error.code??'SUITE_UPGRADE_FAILED',details:{stage:'suite-upgrade',actual,command:error.details.command}});throw error;}});
156
156
  },
157
157
  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);},
158
158
  async provision(input){requireInstaller('Runtime provision is unavailable.');
@@ -10,7 +10,6 @@ export const SUITE_PACKAGES=Object.freeze([
10
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="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
13
  const json=async path=>JSON.parse(await readFile(path,'utf8'));
15
14
  /** Physical inventory only, never a compatibility declaration or permission to upgrade. */
16
15
  export async function readSuiteStatus(profileDir){
@@ -33,7 +32,7 @@ const digest=value=>typeof value==='string'&&/^[a-f0-9]{64}$/.test(value);
33
32
  const nullableText=value=>value===null||text(value);
34
33
  const invalid=()=>requireCondition(false,'CORE_SUITE_DECLARATION_INVALID','Core 套件声明无法用于精确安装。',{},503);
35
34
  function validateDeclaration(value){
36
- if(!object(value)||value.schemaVersion!==1||!text(value.checkedAt)||!digest(value.declarationId)||!text(value.nodeVersion)||!nullableText(value.registry)||!['complete','incomplete','unknown'].includes(value.completeness))invalid();
35
+ if(!object(value)||value.schemaVersion!==1||!text(value.checkedAt)||!text(value.installCommand)||!digest(value.declarationId)||!text(value.nodeVersion)||!nullableText(value.registry)||!['complete','incomplete','unknown'].includes(value.completeness))invalid();
37
36
  const manifest=value.manifest;
38
37
  if(!object(manifest)||manifest.schemaVersion!==1||!text(manifest.id)||!object(manifest.core)||manifest.core.name!==SUITE_PACKAGES[0].name||!exact(manifest.core.version)||!text(manifest.core.repository)||!Array.isArray(manifest.components)||manifest.components.length!==2)invalid();
39
38
  const members=new Map();
@@ -99,7 +98,7 @@ export function createCoreSuiteAdapter({core=()=>undefined,registry,suiteSourceM
99
98
  const selectedUpgrade=declaration.upgrades[0];
100
99
  requireCondition(selectedUpgrade.registry===declaration.registry,'CORE_SUITE_SOURCE_MISMATCH','Core 升级组合来源与执行 registry 不符。',{},409);
101
100
  const components=SUITE_PACKAGES.map(({name,label})=>{const from=declaration.installed.find(row=>row.moduleName===name).actualVersion;if(!exact(from))invalid();return{name,label,from,to:selectedUpgrade.targets.find(row=>row.moduleName===name).version};});
102
- return{available:true,components,sourceBinding:binding,declarationId:declaration.declarationId,registry:declaration.registry,selectedUpgrade,physicalBaseline:declaration.installed,upgrades:declaration.upgrades,blocked:declaration.blocked};
101
+ return{available:true,components,installCommand:declaration.installCommand,sourceBinding:binding,declarationId:declaration.declarationId,registry:declaration.registry,selectedUpgrade,physicalBaseline:declaration.installed,upgrades:declaration.upgrades,blocked:declaration.blocked};
103
102
  };
104
103
  Object.defineProperty(get,'unavailable',{get:()=>current()?null:CORE_SUITE_DECLARATION_MISSING});
105
104
  get.readFacts=async()=>{
@@ -120,7 +119,7 @@ export async function prepareSuiteUpgrade({coreSuitePlan}={}){
120
119
  const plan=await coreSuitePlan();
121
120
  requireCondition(plan!==undefined&&plan!==null,'CORE_SUITE_DECLARATION_MISSING',CORE_SUITE_DECLARATION_MISSING,{},503);
122
121
  const invalid=()=>requireCondition(false,'CORE_SUITE_DECLARATION_INVALID','Core 套件声明无法用于精确安装。',{},503);
123
- if(typeof plan!=='object'||Array.isArray(plan)||typeof plan.available!=='boolean'||!Array.isArray(plan.components)||plan.components.length!==SUITE_PACKAGES.length)invalid();
122
+ if(typeof plan!=='object'||Array.isArray(plan)||typeof plan.available!=='boolean'||!text(plan.installCommand)||!Array.isArray(plan.components)||plan.components.length!==SUITE_PACKAGES.length)invalid();
124
123
  const rows=new Map();
125
124
  for(const row of plan.components){
126
125
  if(!row||typeof row!=='object'||!SUITE_PACKAGES.some(p=>p.name===row.name)||rows.has(row.name)||typeof row.from!=='string'||semver.valid(row.from)!==row.from||typeof row.to!=='string'||semver.valid(row.to)!==row.to)invalid();
@@ -95,6 +95,8 @@ node node_modules/@hanamesh/dsh-app-host/dist/install-target/run.js consumer --u
95
95
 
96
96
  `checks` 逐项列出 dependency、module、bundle、lockfile、bundles 与 `runtime:<item>` 的 expected/actual/ok。别的写者在此期间改动的其他依赖永不撤销,lockfile 因此无法证明已恢复时报 `CONCURRENT_PROFILE_WRITE`。未认领文件与 `<dataRoot>` 下的应用数据从不删除。失败不会自动重试。
97
97
 
98
+ 补偿的 `add` / `install` 沿用本次 latest 查询与正向安装选中的同一 registry:显式源继续传 `--registry=<source>`,pnpm 自有源(`null`)继续省略该参数。回滚不会重新选源、查询 latest 或退到默认公共源;`remove` 参数保持原样。恢复动作列表保留原包操作摘要,不加入 registry URL。
99
+
98
100
  市场显示:`安装失败:<原码>(原因)` 后接 `已恢复到安装前状态(已逐项读回)` / `已恢复到原版本 x(已逐项读回)` / `安装前状态未被改动(已逐项读回)`;`failed` 显示 `恢复失败:<码>,未恢复:<项>,安装前原件:<目录>。未安装成功,也未回到安装前状态,需要人工处理`,不会显示为成功或「已回滚」。
99
101
 
100
102
  **已知边界(不是隐藏限制):** 官方 DSH 没有公开「跨多条 `dsh plugin` 命令持有 profile 写锁」或「外部回滚整个 add」的端口,所以宿主外的并发写者只能被检出并如实报告,不能被阻止。runtime 读回以 lib-provision ledger 为准;promote 与 ledger 之间的进程崩溃由 lib-provision `verify` 负责,不在本流程内调用。provision 对无当前平台资产的项返回 `unsupported` 而不抛错,沿用 rc9,不视为失败。
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "package": {
3
3
  "name": "@hanamesh/dsh-app-host",
4
- "version": "0.6.16",
4
+ "version": "0.6.17",
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"
@@ -294,6 +294,22 @@
294
294
  "sourceCommit": "7a39e4e332a2cbb5dcbfddec68383fb5cef39eb8",
295
295
  "archiveSha256": "71545cbb636d3b1a5e7d9fbba728b16e006b8eaf70bc2540038d077df7263a61",
296
296
  "reviewDocs": "cce157612b1390b845ccca3f0632247de9fb3229"
297
+ },
298
+ "@hanamesh/core": {
299
+ "version": "0.3.28",
300
+ "license": "MIT",
301
+ "source": "https://raw.githubusercontent.com/yzsnstotz/hanamesh-plugin-app-host/cb0bb9f7da4ca817649b65882e06ea2103b9b49d/vendor/hanamesh-core-0.3.28.tgz",
302
+ "purpose": "Development-only public Core suite declaration and provider-owned install command for clean CI; never runtime or bundled",
303
+ "usedAs": [
304
+ "devDependencies"
305
+ ],
306
+ "specs": {
307
+ "devDependencies": "https://raw.githubusercontent.com/yzsnstotz/hanamesh-plugin-app-host/cb0bb9f7da4ca817649b65882e06ea2103b9b49d/vendor/hanamesh-core-0.3.28.tgz"
308
+ },
309
+ "sourceCommit": "62059ca586be2bffa054b5d392ebde41c51939ec",
310
+ "archiveSha256": "9ef8c250efa3bc46de583dc82103719d71d9f3dcc2233fd49731b4467337efa9",
311
+ "licenseNotice": "MIT License\n\nCopyright (c) 2026 HanaMesh\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n",
312
+ "licenseSha256": "cd29e05178eecb3207c494901de7d41785a7b31216e94ab37f3e104e93f3f3b1"
297
313
  }
298
314
  },
299
315
  "downloadedRuntimes": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hanamesh/dsh-app-host",
3
- "version": "0.6.16",
3
+ "version": "0.6.17",
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",
@@ -115,6 +115,7 @@
115
115
  }
116
116
  },
117
117
  "devDependencies": {
118
+ "@hanamesh/core": "https://raw.githubusercontent.com/yzsnstotz/hanamesh-plugin-app-host/cb0bb9f7da4ca817649b65882e06ea2103b9b49d/vendor/hanamesh-core-0.3.28.tgz",
118
119
  "@deepseek-ai/cordis": "4.0.5-alpha.1",
119
120
  "@deepseek-ai/dsh-host-webserver": "0.2.1-alpha.2",
120
121
  "@deepseek-ai/dsh-scope": "0.2.1-alpha.2",