shennian 0.4.32 → 0.4.33
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.
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"jsMinified": true,
|
|
4
4
|
"minifier": "esbuild",
|
|
5
|
-
"fileCount":
|
|
5
|
+
"fileCount": 182,
|
|
6
6
|
"files": [
|
|
7
7
|
{
|
|
8
8
|
"file": "bin/shennian.js",
|
|
@@ -366,8 +366,8 @@
|
|
|
366
366
|
},
|
|
367
367
|
{
|
|
368
368
|
"file": "src/commands/upgrade.js",
|
|
369
|
-
"beforeBytes":
|
|
370
|
-
"afterBytes":
|
|
369
|
+
"beforeBytes": 10405,
|
|
370
|
+
"afterBytes": 5597
|
|
371
371
|
},
|
|
372
372
|
{
|
|
373
373
|
"file": "src/config/index.js",
|
|
@@ -874,10 +874,15 @@
|
|
|
874
874
|
"beforeBytes": 15004,
|
|
875
875
|
"afterBytes": 8684
|
|
876
876
|
},
|
|
877
|
+
{
|
|
878
|
+
"file": "src/upgrade/channel.js",
|
|
879
|
+
"beforeBytes": 2185,
|
|
880
|
+
"afterBytes": 1207
|
|
881
|
+
},
|
|
877
882
|
{
|
|
878
883
|
"file": "src/upgrade/engine.js",
|
|
879
|
-
"beforeBytes":
|
|
880
|
-
"afterBytes":
|
|
884
|
+
"beforeBytes": 16155,
|
|
885
|
+
"afterBytes": 6796
|
|
881
886
|
},
|
|
882
887
|
{
|
|
883
888
|
"file": "src/upgrade/scheduler.js",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import c from"chalk";import{execSync as
|
|
1
|
+
import c from"chalk";import{execSync as U,spawn as R}from"node:child_process";import{randomUUID as S}from"node:crypto";import j from"node:os";import{createInterface as x}from"node:readline/promises";import{getCurrentVersion as u,fetchLatestVersion as f,compareVersions as g,checkForUpdate as C}from"../upgrade/engine.js";import{callDaemonIpc as T}from"../daemon-ipc/client.js";import{getDaemonStatus as I}from"./daemon.js";import{ensureUpdaterInstalled as h}from"../upgrade/updater-bootstrap.js";import{launchUpdater as y}from"../upgrade/updater-launcher.js";import{getUpgradeChannel as v,setUpgradeChannel as V}from"../upgrade/channel.js";const k=["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"];function $(n,e){return n!=="major"||e}function Y(n={}){const e=n.platform??j.platform(),a=n.kill??process.kill,r=n.pid??process.pid,l=n.env??process.env,t=n.spawn??R,o=n.execSync??U;if(e==="win32"){const s=l.ComSpec||"cmd.exe";try{t(s,["/d","/s","/c","timeout /t 2 /nobreak >nul & schtasks /Run /TN ShennianAgent >nul"],{detached:!0,stdio:"ignore",windowsHide:!0}).unref(),a(r,"SIGTERM");return}catch{try{o('schtasks /run /tn "ShennianAgent"',{stdio:"pipe",windowsHide:!0})}catch{}}}else try{t("sh",["-c","sleep 2; shennian start >/dev/null 2>&1"],{detached:!0,stdio:"ignore",env:l}).unref()}catch{}a(r,"SIGTERM")}function q(n){n.command("upgrade").description("Upgrade shennian CLI to the latest version").option("--channel <channel>","Persist stable (npm latest) or preview (npm next); does not install").option("--check","Only check for updates, do not install").option("--to <version>","Upgrade to a specific version").option("--yes","Confirm a major version upgrade (required in non-interactive environments)").action(async e=>{if(e.channel!==void 0){if(e.to||e.yes)throw new Error("--channel cannot be combined with --to or --yes");if(V(e.channel),console.log(`Upgrade channel: ${v()} (saved; no installation)`),!e.check)return}console.log(`Upgrade channel: ${v()}`);const a=u();console.log(c.gray(`Current version: ${a}`)),console.log();let r=0;const l=setInterval(()=>{process.stdout.write(`\r ${k[r%k.length]} Checking for updates...`),r++},80);let t;try{t=e.to??await f(),clearInterval(l),process.stdout.write("\r"+" ".repeat(40)+"\r")}catch(s){clearInterval(l),process.stdout.write("\r"+" ".repeat(40)+"\r"),console.error(c.red(`\u2717 Failed to check for updates: ${s instanceof Error?s.message:String(s)}`)),process.exit(1)}const o=g(a,t);if(o==="none"&&!e.to){console.log(c.green(`\u2713 Already up to date (${a})`));return}if(console.log(` Available: ${c.green(t)} ${o==="major"?c.red("(major)"):o==="minor"?c.yellow("(minor)"):c.green("(patch)")}`),console.log(),!e.check){if(o==="major"&&(console.log(c.yellow(" \u26A0 This is a major version update and may contain breaking changes.")),console.log(c.gray(" Please review the changelog before upgrading.")),console.log(),!e.yes)){(!process.stdin.isTTY||!process.stdout.isTTY)&&(console.error(c.red("\u2717 Major upgrades require explicit confirmation; rerun with --yes.")),process.exit(1));const s=x({input:process.stdin,output:process.stdout}),d=await s.question(` Type ${t} to confirm this major upgrade: `);if(s.close(),d.trim()!==t){console.log(c.gray(" Upgrade cancelled."));return}}try{await T({method:"upgrade.start",params:{targetVersion:t,confirmedMajor:o==="major"||e.yes===!0},timeoutMs:1e4})}catch(s){if(I({cleanupStale:!0}).running)throw s;const i=h();y({fromVersion:a,targetVersion:t,oldDaemonPid:null},{readiness:i})}console.log(),console.log(c.green(`\u2713 Upgrade scheduled ${a} \u2192 ${t}`)),console.log(c.gray(" The independent updater will verify, roll back if needed, and restart Shennian."))}})}async function O(n,e,a,r={}){const l=i=>{n.sendEvent({type:"event",event:"upgrade",payload:i})},t=r.currentVersion??u();let o;try{l({state:"checking",machineId:"self"}),o=a??await f()}catch(i){const p=`Version check failed: ${i instanceof Error?i.message:String(i)}`;return r.suppressResponse||n.sendRes({type:"res",id:e,ok:!1,error:p}),{ok:!1,error:p}}const s=g(t,o);if(s==="none")return r.suppressResponse||n.sendRes({type:"res",id:e,ok:!0,payload:{alreadyLatest:!0,version:t}}),{ok:!0,alreadyLatest:!0,version:t};if(!$(s,r.confirmedMajor===!0))return r.suppressResponse||n.sendRes({type:"res",id:e,ok:!1,error:"Major upgrades require explicit second confirmation"}),{ok:!1,error:"Major upgrades require explicit second confirmation"};const d=S();try{const i=h(),p=r.prepareForUpgrade?.(d)??{activeRunCount:0},{job:w}=y({fromVersion:t,targetVersion:o,oldDaemonPid:process.pid,jobId:d},{readiness:i});l({state:"installing",machineId:"self",version:o,jobId:d,activeRunCount:p.activeRunCount}),l({state:"restarting",machineId:"self",from:t,to:o,jobId:d});const m={from:t,to:o,jobId:d,activeRunCount:p.activeRunCount};return r.suppressResponse||n.sendRes({type:"res",id:e,ok:!0,payload:m}),r.shutdownAfterHandoff?.(),{ok:!0,...m,resultPath:w.resultPath}}catch(i){r.cancelUpgradePreparation?.();const p=i instanceof Error?i.message:String(i);return l({state:"failed",machineId:"self",error:p}),r.suppressResponse||n.sendRes({type:"res",id:e,ok:!1,error:p}),{ok:!1,error:p}}}async function _(n,e,a={}){const r=await C(a.currentVersion);n.sendRes({type:"res",id:e,ok:!0,payload:r.hasUpdate?{hasUpdate:!0,current:r.current,latest:r.latest,changeType:r.changeType}:{hasUpdate:!1,current:a.currentVersion??u()}})}export{O as handleUpgradeStart,_ as handleUpgradeStatus,$ as hasMajorUpgradeConfirmation,q as registerUpgradeCommand,Y as restartCurrentDaemonAfterUpgrade};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export type UpgradeChannel = 'stable' | 'preview';
|
|
2
|
+
/** Separate from identity config: selecting a channel must never rewrite pairing data. */
|
|
3
|
+
export declare function getUpgradeChannel(): UpgradeChannel;
|
|
4
|
+
export declare function setUpgradeChannel(channel: string): void;
|
|
5
|
+
export declare function getUpgradeDistTag(): 'latest' | 'next';
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import e from"node:fs";import i from"node:path";import{randomUUID as s}from"node:crypto";import{resolveShennianPath as c}from"../config/index.js";function l(){try{const n=JSON.parse(e.readFileSync(c("upgrade-channel.json"),"utf8"));if(n?.schemaVersion!==1||!["stable","preview"].includes(n.channel))throw new Error("invalid upgrade channel configuration");return n.channel}catch(n){if(n.code==="ENOENT")return"stable";throw new Error("Cannot read upgrade channel; use shennian upgrade --channel stable to repair it")}}function m(n){if(n!=="stable"&&n!=="preview")throw new Error("Upgrade channel must be stable or preview");const r=c("upgrade-channel.json");e.mkdirSync(i.dirname(r),{recursive:!0,mode:448});const t=`${r}.${process.pid}.${s()}.tmp`;try{e.writeFileSync(t,JSON.stringify({schemaVersion:1,channel:n})+`
|
|
2
|
+
`,{flag:"wx",mode:384});const o=e.openSync(t,"r+");try{e.fsyncSync(o)}finally{e.closeSync(o)}if(e.renameSync(t,r),process.platform!=="win32"){const a=e.openSync(i.dirname(r),"r");try{e.fsyncSync(a)}finally{e.closeSync(a)}}}finally{try{e.unlinkSync(t)}catch{}}}function u(){return l()==="preview"?"next":"latest"}export{l as getUpgradeChannel,u as getUpgradeDistTag,m as setUpgradeChannel};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import s from"node:fs";import c from"node:path";import{execSync as F,execFile as D}from"node:child_process";import{promisify as
|
|
1
|
+
import s from"node:fs";import c from"node:path";import{execSync as F,execFile as D}from"node:child_process";import{promisify as T}from"node:util";import{fileURLToPath as J}from"node:url";import{getShennianDir as U,resolveShennianPath as m}from"../config/index.js";import{getUpgradeDistTag as O}from"./channel.js";const f=T(D),y=m("upgrade-attempt.json"),L=3,P="https://registry.npmjs.org",I=10*6e4,_=[5*6e4,30*6e4,120*6e4,360*6e4];function g(){try{const r=h();if(r)return JSON.parse(s.readFileSync(r,"utf-8")).version}catch{}return"0.0.0"}function h(r=J(import.meta.url)){let e=c.dirname(r);for(let n=0;n<6;n++){const t=c.join(e,"package.json");if(s.existsSync(t)&&JSON.parse(s.readFileSync(t,"utf-8")).name==="shennian")return t;e=c.dirname(e)}return null}async function G(){const r=await fetch(`https://registry.npmjs.org/shennian/${O()}`,{headers:{Accept:"application/json"},signal:AbortSignal.timeout(1e4)});if(!r.ok)throw new Error(`npm registry returned ${r.status}`);const e=await r.json();if(typeof e?.version!="string"||!/^\d+\.\d+\.\d+$/.test(e.version))throw new Error("npm registry returned an unsupported CLI version (expected numeric major.minor.patch)");return e.version}function H(r,e){const n=p=>p.replace(/^v/,"").split(".").map(Number),[t,a,o]=n(r),[i,l,u]=n(e);return i>t?"major":i===t&&l>a?"minor":i===t&&l===a&&u>o?"patch":"none"}function B(){return F("npm root -g",{encoding:"utf-8",stdio:"pipe",windowsHide:!0}).trim()}function Y(r,e=process.platform){const n=e==="win32"?c.win32:c.posix,t=n.dirname(r),a=n.dirname(t);if(n.basename(a)!=="node_modules")return null;const o=n.dirname(a);return n.basename(o)==="lib"?n.dirname(o):o}function N(){const r=h();return r?Y(r):null}function z(){const r=h();return r?c.join(c.dirname(r),"dist","bin","shennian.js"):c.join(B(),"shennian","dist","bin","shennian.js")}function K(){try{return JSON.parse(s.readFileSync(y,"utf-8"))}catch{return null}}function d(r){s.mkdirSync(U(),{recursive:!0}),s.writeFileSync(y,JSON.stringify(r,null,2))}function X(){try{s.unlinkSync(y)}catch{}}function $(){return m("upgrade-failures.json")}function Z(){return m("upgrade.lock")}function q(r=Z(),e={}){const n=e.now??Date.now(),t=e.pid??process.pid,a=e.isProcessAlive??(u=>{try{return process.kill(u,0),!0}catch{return!1}});s.mkdirSync(c.dirname(r),{recursive:!0});const o=()=>s.openSync(r,"wx",384);let i;try{i=o()}catch(u){if(u.code!=="EEXIST")throw u;let p=!1;try{const A=JSON.parse(s.readFileSync(r,"utf-8")),b=Number(A.pid),E=Number(A.createdAt);p=!Number.isInteger(b)||!Number.isFinite(E)||n-E>I||!a(b)}catch{p=!0}if(!p)throw new Error("Another Shennian upgrade is already running");s.rmSync(r,{force:!0}),i=o()}s.writeFileSync(i,JSON.stringify({pid:t,createdAt:n})),s.closeSync(i);let l=!1;return{release(){if(!l){l=!0;try{s.unlinkSync(r)}catch{}}}}}function w(r=$()){try{const e=JSON.parse(s.readFileSync(r,"utf-8"));return!e||typeof e!="object"||!e.versions?{versions:{}}:{versions:e.versions}}catch{return{versions:{}}}}function j(r){s.mkdirSync(U(),{recursive:!0}),s.writeFileSync($(),JSON.stringify(r,null,2))}function Q(r){return _[Math.min(Math.max(r-1,0),_.length-1)]}function cr(r,e=Date.now(),n=w()){const t=n.versions[r];return!!(t&&t.nextRetryAt>e)}function ur(r,e,n=Date.now()){const t=w(),a=t.versions[r],o=(a?.failureCount??0)+1,i={version:r,failureCount:o,firstFailedAt:a?.firstFailedAt??n,lastFailedAt:n,nextRetryAt:n+Q(o),error:e};return t.versions[r]=i,j(t),i}function k(r){const e=w();e.versions[r]&&(delete e.versions[r],j(e))}async function lr(){const r=K();return r?Date.now()-r.attemptAt>600*1e3?(X(),null):(r.attemptCount++,r.attemptCount>=L?(d(r),r):(d(r),null)):null}async function pr(r,e,n={}){let t;try{t=q()}catch(a){return{ok:!1,error:a instanceof Error?a.message:String(a)}}try{return await W(r,e,n)}finally{t.release()}}async function W(r,e,n={}){const t=n.currentVersion??g(),a=g();if(t===r)return k(r),{ok:!1,error:`Already on version ${r}`};e({step:"checking"});try{F("npm --version",{stdio:"pipe",windowsHide:!0})}catch{return{ok:!1,error:"npm is not available in PATH"}}if(a===r){e({step:"verifying",version:r});try{await v(r)}catch(o){return{ok:!1,error:`Smoke test failed: ${o instanceof Error?o.message:String(o)}`}}return d({from:t,to:r,attemptCount:0,attemptAt:Date.now()}),k(r),e({step:"restarting",from:t,to:r}),{ok:!0,from:t,to:r}}e({step:"backing-up"}),e({step:"installing",version:r});try{await M(r)}catch(o){const i=await R(t);return{ok:!1,error:`npm install failed: ${o instanceof Error?o.message:String(o)}${i.ok?"":`; rollback failed: ${i.error}`}`,rolledBack:i.ok}}e({step:"verifying",version:r});try{await v(r)}catch(o){const i=await R(t);return{ok:!1,error:`Smoke test failed: ${o instanceof Error?o.message:String(o)}${i.ok?"":`; rollback failed: ${i.error}`}`,rolledBack:i.ok}}return d({from:t,to:r,attemptCount:0,attemptAt:Date.now()}),k(r),e({step:"restarting",from:t,to:r}),{ok:!0,from:t,to:r}}function C(r){if(!/^[0-9]+\.[0-9]+\.[0-9]+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.test(r))throw new Error(`Invalid npm version: ${r}`)}function S(){return process.platform==="win32"?"npm.cmd":"npm"}function V(r,e){return{timeout:e,windowsHide:!0,shell:r==="win32"}}function x(r){return V(process.platform,r)}async function M(r){C(r);const e=N(),n=["install","-g",`shennian@${r}`,...e?["--prefix",e]:[]];try{await f(S(),n,x(12e4))}catch(t){const a=t instanceof Error?t.message:String(t);try{await f(S(),[...n,"--registry",P],{...x(12e4)})}catch(o){const i=o instanceof Error?o.message:String(o);throw new Error(`${a}
|
|
2
2
|
--- npmjs fallback ---
|
|
3
|
-
${i}`)}}}async function
|
|
3
|
+
${i}`)}}}async function v(r){C(r);const e=z(),{stdout:n}=await f(process.execPath,[e,"--version"],{timeout:1e4,windowsHide:!0});if(n.trim()!==r)throw new Error(`Version verification failed: expected ${r}, got ${n.trim()||"empty output"}`);const t=N();await f(S(),["ls","-g",`shennian@${r}`,"--omit=dev","--depth=0",...t?["--prefix",t]:[]],{...x(3e4)})}async function rr(r,e={}){await(e.install??M)(r),await(e.verify??v)(r)}async function R(r){try{return await rr(r),{ok:!0}}catch(e){return{ok:!1,error:e instanceof Error?e.message:String(e)}}}async function fr(r){const e=r??g(),n=await G(),t=H(e,n);return t==="none"?{hasUpdate:!1}:{hasUpdate:!0,current:e,latest:n,changeType:t}}export{q as acquireUpgradeLock,fr as checkForUpdate,X as clearUpgradeAttempt,k as clearUpgradeFailure,H as compareVersions,G as fetchLatestVersion,h as findPackageJson,g as getCurrentVersion,Q as getUpgradeRetryDelayMs,lr as handleStartupCrashCheck,M as installShennianVersion,cr as isUpgradeVersionInCooldown,V as npmExecOptionsForPlatform,pr as performUpgrade,K as readUpgradeAttempt,w as readUpgradeFailures,ur as recordUpgradeFailure,Y as resolveNpmGlobalPrefixFromPackageJson,rr as rollbackToExactVersion,v as verifyInstalledVersion,d as writeUpgradeAttempt};
|