@josephyulei/summon-foundation 0.1.11 → 0.1.12
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 +2 -2
- package/bin/summon.mjs +4 -3
- package/lib/acquisition-task.mjs +1 -1
- package/lib/acquisition-worker.mjs +3 -2
- package/lib/manager-step.mjs +15 -1
- package/lib/operation-failure.mjs +18 -0
- package/lib/progress-page.mjs +1 -1
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
保留真实下载状态与独立确认。基础环境不能安全启动页面时,由 Codex 对话先披露并取得独立批准,再准备专用环境;页面启动后全程同页。具体方法见仓库 `docs/install-with-codex.md`,不能先运行 npx 来准备缺失的 Node。不修改共享 PATH;获取器专用环境及其回执按缓存保留,程序另用随包运行时。发行发现仅需 curl,获取还需 tar;已验证载荷的确认、恢复和卸载不依赖下载工具。
|
|
6
6
|
|
|
7
|
-
0.1.
|
|
7
|
+
0.1.12 单页试用入口与 Foundation 0.2.17 的运行引擎共同支持当前用户共用安装及清晰的完成、取消和部分完成反馈。同一页面内保留目录选择、程序和 Skill 独立确认。真实安装、新任务发现和主动告知仍需独立验收。
|
|
8
8
|
|
|
9
9
|
首次 URL 持续显示本次流程;确认仅转交本次受控子进程的当前计划,不新开manager页。--acquire仍只读获取;--resume仅在用户明确要求后恢复未完成Skill,不重放程序安装。
|
|
10
10
|
|
|
@@ -16,7 +16,7 @@ Foundation 的薄获取入口,不包含完整运行包,没有 npm 安装生
|
|
|
16
16
|
确认 registry 中存在本版本及对应不可变 Release 后,在具备安装权限的新 Codex 对话粘贴:
|
|
17
17
|
|
|
18
18
|
```text
|
|
19
|
-
npx --yes --package @josephyulei/summon-foundation@0.1.
|
|
19
|
+
npx --yes --package @josephyulei/summon-foundation@0.1.12 summon foundation
|
|
20
20
|
```
|
|
21
21
|
|
|
22
22
|
旧版退出推荐入口;支持边界见[现行支持策略](https://github.com/y15801172513-lang/foundation/blob/main/docs/install-platform-024.md#现行支持策略)。旧不可变版本不修改,不自动迁移或清除旧安装。
|
package/bin/summon.mjs
CHANGED
|
@@ -8,7 +8,7 @@ import {acquireInWorker} from '../lib/acquisition-task.mjs';
|
|
|
8
8
|
import {startProgressPage} from '../lib/progress-page.mjs';
|
|
9
9
|
import {followSelectedSkill} from '../lib/skill-handoff.mjs';
|
|
10
10
|
import {followMaintenance} from '../lib/maintenance-handoff.mjs';
|
|
11
|
-
import {installedClient} from '../lib/manager-step.mjs';
|
|
11
|
+
import {installedClient,assertUpdateEngineSupport,operationFailure} from '../lib/manager-step.mjs';
|
|
12
12
|
import {resumeSkillHandoff} from '../lib/resume-handoff.mjs';
|
|
13
13
|
import {parseSummonArgs} from '../lib/cli-options.mjs';
|
|
14
14
|
import {createOperation,saveOperation,readOperation,runtimeObservation,operationExitCode} from '../lib/operation-result.mjs';
|
|
@@ -78,7 +78,7 @@ try{
|
|
|
78
78
|
}else if(maintenance){
|
|
79
79
|
plainPath(args.root);
|
|
80
80
|
const env={...process.env};for(const key of ['NODE_OPTIONS','NODE_PATH','NODE_V8_COVERAGE','NODE_REDIRECT_WARNINGS','NODE_COMPILE_CACHE','NODE_COMPILE_CACHE_PORTABLE','NODE_PRESERVE_SYMLINKS'])delete env[key];
|
|
81
|
-
const installed=installedClient(args.root,env),
|
|
81
|
+
const installed=installedClient(args.root,env),inspection=installed.call(['manager','inspect','--root',args.root]),current=inspection.installation?.current;
|
|
82
82
|
if(!current?.identity?.installId)fail('当前安装身份无法核实;尚未获取或执行维护');
|
|
83
83
|
const capability=spawnSync(installed.launcher,['--help'],{encoding:'utf8',env,timeout:15000});
|
|
84
84
|
if(capability.status!==0||!capability.stdout.includes('--journey-channel'))fail('当前安装不受新版单页流程支持。请另行确认卸载旧版并重新安装新版;本次不删除、不迁移、不回退旧页面');
|
|
@@ -89,6 +89,7 @@ try{
|
|
|
89
89
|
if(environment.state!=='ready')fail('环境复检未通过:'+environment.state+';缺少 '+environment.missing.join('、')+';保留当前安装,不修改系统工具');
|
|
90
90
|
let candidate;
|
|
91
91
|
if(args.update){
|
|
92
|
+
assertUpdateEngineSupport(inspection,{root:args.root,stage});
|
|
92
93
|
updateOperation({phase:'discovering'});
|
|
93
94
|
const {context,receipt}=await acquireInWorker({version:args.version,stage,operationId:operation.operationId,onContext:context=>updateOperation({version:context.version,sourceCommit:context.sourceCommit}),onProgress:download=>updateOperation({download}),onPhase:phase=>updateOperation({phase,...(phase==='fetching-and-verifying-runtime'?{download:null}:{})})});
|
|
94
95
|
const directory=plainPath(path.dirname(receipt.launcher)),manifest=JSON.parse(fs.readFileSync(plainPath(path.join(directory,'manifest.json'))));
|
|
@@ -149,4 +150,4 @@ try{
|
|
|
149
150
|
if(!operation.terminal)updateOperation({state:'verification-required',terminal:true,childExitCode:ended.code,next:'服务退出不等于成功或取消;只读核验原 session 与 installed 状态,不自动重试'});
|
|
150
151
|
process.exitCode=operationExitCode(operation);
|
|
151
152
|
}
|
|
152
|
-
}catch(e){const message=String(e.message).replace(/https?:\/\/\S+/g,'[已脱敏网址]').replace(/(?:github_pat_|ghp_|npm_)[A-Za-z0-9_]+/g,'[已脱敏凭据]');updateOperation({state:e.code==='ENTRY_CHOICE_CANCELLED'?'cancelled-no-install':e.code==='ENTRY_CHOICE_EXPIRED'?'expired-no-install':operation?.programState==='completed'?'partial':operation&&operation.installationWrites!=='none'?'verification-required':'failed',terminal:true,...(operation?.programState==='completed'?{failedPhase:operation.phase,phase:'finished'}:{}),errorCode:
|
|
153
|
+
}catch(e){const failure=operationFailure(e);const message=String(e.message).replace(/https?:\/\/\S+/g,'[已脱敏网址]').replace(/(?:github_pat_|ghp_|npm_)[A-Za-z0-9_]+/g,'[已脱敏凭据]');updateOperation({state:e.code==='ENTRY_CHOICE_CANCELLED'?'cancelled-no-install':e.code==='ENTRY_CHOICE_EXPIRED'?'expired-no-install':operation?.programState==='completed'?'partial':operation&&operation.installationWrites!=='none'?'verification-required':'failed',terminal:true,...(operation?.programState==='completed'?{failedPhase:operation.phase,phase:'finished'}:{}),errorCode:failure.code,errorStage:failure.stage,retryable:failure.retryable,diagnostic:{...failure,...(e.code==='ACQUISITION_TRANSPORT_FAILED'?{transport:e.diagnostic}:{} )},next:(e.diagnostic?.category?failure.next:message)+'。先核验旧工具进程已结束及同次结果;保留材料,不自动重试或重放安装确认'});console.error(`错误:[${failure.code} / ${failure.stage} / retryable=${failure.retryable}] ${message}`);process.exitCode=operationExitCode(operation);}finally{if(progressPage)await progressPage.close();}
|
package/lib/acquisition-task.mjs
CHANGED
|
@@ -11,7 +11,7 @@ export function acquireInWorker({version,stage,operationId,onContext=()=>{},onPh
|
|
|
11
11
|
else if(event.type==='phase')onPhase(event.phase);
|
|
12
12
|
else if(event.type==='progress')onProgress(event.download);
|
|
13
13
|
else if(event.type==='complete')receipt=event.receipt;
|
|
14
|
-
else if(event.type==='failure')failure=Object.assign(Error(event.message),{code:event.code,diagnostic:event.diagnostic});
|
|
14
|
+
else if(event.type==='failure')failure=Object.assign(Error(event.message),{code:event.code,stage:event.stage,retryable:event.retryable,diagnostic:event.diagnostic});
|
|
15
15
|
});
|
|
16
16
|
worker.on('error',error=>{failure=error;});
|
|
17
17
|
worker.once('exit',code=>{
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import {parentPort, workerData} from 'node:worker_threads';
|
|
4
4
|
import {inspectRelease, acquireRelease} from './acquire.mjs';
|
|
5
5
|
|
|
6
|
+
let phase='discovering';
|
|
6
7
|
try {
|
|
7
8
|
const context = inspectRelease(workerData.version);
|
|
8
9
|
// The parent persists context atomically in the stage. Finish that write
|
|
@@ -15,10 +16,10 @@ try {
|
|
|
15
16
|
await recorded;
|
|
16
17
|
const receipt = await acquireRelease(context, workerData.stage, {
|
|
17
18
|
operationId:workerData.operationId,
|
|
18
|
-
onPhase:phase
|
|
19
|
+
onPhase:value=>{phase=value;parentPort.postMessage({type:'phase',phase});},
|
|
19
20
|
onProgress:download=>parentPort.postMessage({type:'progress',download}),
|
|
20
21
|
});
|
|
21
22
|
parentPort.postMessage({type:'complete',receipt});
|
|
22
23
|
} catch(error) {
|
|
23
|
-
parentPort.postMessage({type:'failure',code:error.code,diagnostic:error.diagnostic||null,message:String(error.message).replace(/https?:\/\/\S+/g,'[已脱敏网址]').replace(/(?:github_pat_|ghp_|npm_)[A-Za-z0-9_]+/g,'[已脱敏凭据]')});
|
|
24
|
+
parentPort.postMessage({type:'failure',code:error.code||'ACQUISITION_VALIDATION_FAILED',stage:phase,retryable:error.code==='ACQUISITION_TRANSPORT_FAILED',diagnostic:error.diagnostic||null,message:String(error.message).replace(/https?:\/\/\S+/g,'[已脱敏网址]').replace(/(?:github_pat_|ghp_|npm_)[A-Za-z0-9_]+/g,'[已脱敏凭据]')});
|
|
24
25
|
}
|
package/lib/manager-step.mjs
CHANGED
|
@@ -2,13 +2,27 @@ import {spawn,spawnSync} from 'node:child_process';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import {plainPath} from './acquire.mjs';
|
|
4
4
|
|
|
5
|
+
import {operationFailure,installedCommandFailure} from './operation-failure.mjs';
|
|
6
|
+
export {operationFailure,installedCommandFailure} from './operation-failure.mjs';
|
|
7
|
+
|
|
8
|
+
export function assertUpdateEngineSupport(inspection,{root,stage}={}) {
|
|
9
|
+
if(['0.2.15','0.2.16'].includes(inspection?.installation?.current?.version)&&root&&stage){
|
|
10
|
+
const relative=path.relative(path.dirname(plainPath(root)),plainPath(stage));
|
|
11
|
+
if(relative&&!relative.startsWith('..'+path.sep)&&relative!=='..'&&!path.isAbsolute(relative))return;
|
|
12
|
+
}
|
|
13
|
+
if(inspection?.supportedLifecycleOptions?.updateCandidateAuthority!=='os-account-acquisition-v1'){
|
|
14
|
+
const detail=operationFailure({code:'UPDATE_ENGINE_ACQUISITION_UNSUPPORTED',stage:'authority',retryable:false});
|
|
15
|
+
throw Object.assign(Error(detail.next),detail,{diagnostic:detail});
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
5
19
|
// A client of the existing installed manager, never a confirmation authority.
|
|
6
20
|
export function installedClient(root,env,journeyControl=null) {
|
|
7
21
|
root=plainPath(root);
|
|
8
22
|
const launcher=plainPath(path.join(root,'bin/foundation-kit'));
|
|
9
23
|
const call=args=>{
|
|
10
24
|
const r=spawnSync(launcher,args,{env,encoding:'utf8',timeout:30000,maxBuffer:4*1024*1024});
|
|
11
|
-
if(r.status!==0)
|
|
25
|
+
if(r.status!==0)throw installedCommandFailure(r);
|
|
12
26
|
return JSON.parse(r.stdout);
|
|
13
27
|
};
|
|
14
28
|
return {root,launcher,call,env,journeyControl};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export function operationFailure(error) {
|
|
2
|
+
const code=/^[A-Z][A-Z0-9_]{1,79}$/.test(error?.code||'')?error.code:'INSTALL_ENTRY_FAILED';
|
|
3
|
+
const stage=/^[a-z][a-z-]{0,39}$/.test(error?.stage||'')?error.stage:'entry';
|
|
4
|
+
const category=code==='ACQUISITION_TRANSPORT_FAILED'?'transport':/PATH|TRUSTED_ROOT|SYMLINK|ANCESTOR|ACQUISITION_CACHE|UPDATE_ENGINE/.test(code)?'local-path':/CANDIDATE|INTEGRITY|SIGNATURE|ACQUISITION_VALIDATION/.test(code)?'integrity':/CONFIRM|PLAN|SESSION/.test(code)?'confirmation':'engine';
|
|
5
|
+
const next=category==='local-path'?'当前引擎不能安全接收此更新候选。保留原安装与已下载材料,不重复下载或搬移候选;核实版本兼容性。旧引擎不支持时,须另行确认保留项目资料后的卸载与新版首装。':category==='integrity'?'候选完整性或来源核验失败;保留证据,不执行或手工修补材料。':category==='transport'?'网络传输未完成;保留本次记录,核实网络后再明确发起获取,尚未执行安装。':category==='confirmation'?'确认或计划未完成;核实同次状态,不重放旧批准。':'当前操作未核实完成;保留结果,检查同次记录,不自动重试。';
|
|
6
|
+
return {code,stage,retryable:error?.retryable===true,category,next};
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function installedCommandFailure(result) {
|
|
10
|
+
let value;
|
|
11
|
+
for(const text of [result.stderr,result.stdout]){
|
|
12
|
+
if(typeof text!=='string'||text.length>65536)continue;
|
|
13
|
+
try{const parsed=JSON.parse(text);value=parsed.error||parsed;if(value?.code)break;}catch{}
|
|
14
|
+
}
|
|
15
|
+
const detail=operationFailure(value||{code:'INSTALLED_COMMAND_FAILED',stage:'installed-command'});
|
|
16
|
+
// Never forward arbitrary stderr/message/details, credentials or plan nonces.
|
|
17
|
+
return Object.assign(Error(detail.next),detail,{diagnostic:detail,exitCode:result.status});
|
|
18
|
+
}
|
package/lib/progress-page.mjs
CHANGED
|
@@ -53,7 +53,7 @@ export function acquisitionProgress(record) {
|
|
|
53
53
|
const content=lifecycleContent(record,observations);
|
|
54
54
|
Object.assign(phases,{'awaiting-confirmation':'等待你确认'+verb,executing:'正在'+verb,'runtime-ended':'读取'+verb+'结果',finished:verb+'结果已返回'});
|
|
55
55
|
if(record.terminal&&record.state==='partial'&&record.programState==='completed'&&kind==='update')journey.title='程序已更新,Skill 尚未更新';
|
|
56
|
-
return {content,journey,operationId:record.operationId,title:journey.title,phase:({'skill-confirmation':'等待你确认 Codex 操作','skill-executing':'正在处理 Codex 对话能力'})[record.phase]||phases[record.phase]||'正在核实本次操作',state:record.state,version:record.currentVersion&&kind==='update'?record.currentVersion+' → '+record.version:record.version||record.currentVersion||null,download:record.download||null,downloadStatus:downloadStatus(record),program:record.programState==='completed'?'程序'+verb+'已完成;'+(kind==='uninstall'?'结果由独立回执保留':record.runtimeHealth==='passed'?'稳定入口健康已核验':'健康待核实'):record.installationWrites==='none'?'尚未执行程序操作':'程序结果以同次记录为准',skill:kind==='uninstall'?(observations.remove?.state==='completed'?'已解除已归属 Codex 注册;修改和未知文件按计划保留':'本次没有已核实的注册移除结果'):record.skillRegistered===true?'Skill 文件与归属已核验;新对话识别另验':record.journeyContext?.skillChoice==='skipped'?'本次不接入 Codex':record.journeyContext?.skillChoice==='selected'?'已选择接入 Codex;仅在分别确认后执行':'Codex 接入可选,尚未选择',project:'未自动接入项目',installationRoot:record.installationRoot||null,skillDestination:record.skillDestination||null,next:record.next||(record.terminal&&record.state==='completed'&&record.journeyContext?.skillChoice==='skipped'?'本次已跳过 Codex 接入;程序操作已完成,项目另行确认。':journey.summary),nextUrl:record.terminal?(kind!=='uninstall'&&record.workbench?.state==='ready'?record.workbench.url:null):record.confirmationUrl||null,nextLabel:record.terminal?'打开已安装工作台':record.phase?.startsWith('skill-')?'查看 Codex 精确确认':kind==='install'?'继续到目录选择 / 本人确认页':'查看'+verb+'确认',resultFile:record.resultFile||null,terminal,diagnostic:record.diagnostic||null};
|
|
56
|
+
return {content,journey,operationId:record.operationId,title:journey.title,phase:({'skill-confirmation':'等待你确认 Codex 操作','skill-executing':'正在处理 Codex 对话能力'})[record.phase]||phases[record.phase]||'正在核实本次操作',state:record.state,version:record.currentVersion&&kind==='update'?record.currentVersion+' → '+record.version:record.version||record.currentVersion||null,download:record.download||null,downloadStatus:downloadStatus(record),program:record.programState==='completed'?'程序'+verb+'已完成;'+(kind==='uninstall'?'结果由独立回执保留':record.runtimeHealth==='passed'?'稳定入口健康已核验':'健康待核实'):record.installationWrites==='none'?'尚未执行程序操作':'程序结果以同次记录为准',skill:kind==='uninstall'?(observations.remove?.state==='completed'?'已解除已归属 Codex 注册;修改和未知文件按计划保留':'本次没有已核实的注册移除结果'):record.skillRegistered===true?'Skill 文件与归属已核验;新对话识别另验':record.journeyContext?.skillChoice==='skipped'?'本次不接入 Codex':record.journeyContext?.skillChoice==='selected'?'已选择接入 Codex;仅在分别确认后执行':'Codex 接入可选,尚未选择',project:'未自动接入项目',installationRoot:record.installationRoot||null,skillDestination:record.skillDestination||null,next:record.next||(record.terminal&&record.state==='completed'&&record.journeyContext?.skillChoice==='skipped'?'本次已跳过 Codex 接入;程序操作已完成,项目另行确认。':journey.summary),nextUrl:record.terminal?(kind!=='uninstall'&&record.workbench?.state==='ready'?record.workbench.url:null):record.confirmationUrl||null,nextLabel:record.terminal?'打开已安装工作台':record.phase?.startsWith('skill-')?'查看 Codex 精确确认':kind==='install'?'继续到目录选择 / 本人确认页':'查看'+verb+'确认',resultFile:record.resultFile||null,terminal,errorCode:record.errorCode||null,errorStage:record.errorStage||null,retryable:record.retryable===true,diagnostic:record.diagnostic||null};
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
// A bounded, read-only loopback view over the existing in-memory operation.
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@josephyulei/summon-foundation",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.12",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@josephyulei/summon-foundation",
|
|
9
|
-
"version": "0.1.
|
|
9
|
+
"version": "0.1.12",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@sigstore/bundle": "4.0.0",
|
package/package.json
CHANGED