shennian 0.4.35 → 0.4.36

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.
@@ -891,13 +891,13 @@
891
891
  },
892
892
  {
893
893
  "file": "src/upgrade/updater-bootstrap.js",
894
- "beforeBytes": 3367,
895
- "afterBytes": 1796
894
+ "beforeBytes": 3595,
895
+ "afterBytes": 1840
896
896
  },
897
897
  {
898
898
  "file": "src/upgrade/updater-launcher.js",
899
- "beforeBytes": 8025,
900
- "afterBytes": 3968
899
+ "beforeBytes": 9356,
900
+ "afterBytes": 4695
901
901
  },
902
902
  {
903
903
  "file": "src/upgrade/updater-protocol.js",
@@ -1,2 +1,2 @@
1
- import r from"node:fs";import e from"node:path";import{fileURLToPath as S}from"node:url";import{randomUUID as y}from"node:crypto";import{resolveShennianPath as d}from"../config/index.js";import{UPDATER_PROTOCOL_VERSION as l}from"./updater-protocol.js";function f(o,t){r.mkdirSync(e.dirname(o),{recursive:!0,mode:448});const n=`${o}.tmp-${process.pid}-${y()}`;try{r.writeFileSync(n,`${JSON.stringify(t,null,2)}
2
- `,{mode:384,flag:"wx"});const i=r.openSync(n,process.platform==="win32"?"r+":"r");try{r.fsyncSync(i)}finally{r.closeSync(i)}r.renameSync(n,o)}finally{try{r.unlinkSync(n)}catch{}}}function U(o={}){const t=o.root??d("runtime","updater"),n=e.join(t,"versions",String(l));r.mkdirSync(n,{recursive:!0,mode:448}),process.platform!=="win32"&&r.chmodSync(n,448);const i=o.assetPath??S(new URL("./updater-worker.js",import.meta.url)),s=e.join(n,"updater-worker.js"),m=r.readFileSync(i),p=(()=>{try{return r.readFileSync(s)}catch{return null}})();if(!p||!p.equals(m)){const c=`${s}.tmp-${process.pid}-${y()}`;try{r.writeFileSync(c,m,{mode:448,flag:"wx"}),process.platform!=="win32"&&r.chmodSync(c,448);const u=r.openSync(c,process.platform==="win32"?"r+":"r");try{r.fsyncSync(u)}finally{r.closeSync(u)}r.renameSync(c,s)}finally{try{r.unlinkSync(c)}catch{}}}const a={protocolVersion:l,workerPath:s,installedAt:(o.now??new Date).toISOString()};return f(e.join(t,"current.json"),a),f(e.join(t,"readiness.json"),{protocolVersion:a.protocolVersion,installedAt:a.installedAt}),a}function $(o=d("runtime","updater")){try{const t=JSON.parse(r.readFileSync(e.join(o,"current.json"),"utf8"));if(t.protocolVersion!==l||!t.workerPath)return null;const n=r.lstatSync(t.workerPath);return n.isSymbolicLink()||!n.isFile()?null:t}catch{return null}}export{U as ensureUpdaterInstalled,$ as readUpdaterReadiness};
1
+ import r from"node:fs";import e from"node:path";import{fileURLToPath as S}from"node:url";import{randomUUID as d}from"node:crypto";import{resolveShennianPath as f}from"../config/index.js";import{UPDATER_PROTOCOL_VERSION as l}from"./updater-protocol.js";function m(t,o){r.mkdirSync(e.dirname(t),{recursive:!0,mode:448});const n=`${t}.tmp-${process.pid}-${d()}`;try{r.writeFileSync(n,`${JSON.stringify(o,null,2)}
2
+ `,{mode:384,flag:"wx"});const i=r.openSync(n,process.platform==="win32"?"r+":"r");try{r.fsyncSync(i)}finally{r.closeSync(i)}r.renameSync(n,t)}finally{try{r.unlinkSync(n)}catch{}}}function U(t={}){const o=t.root??f("runtime","updater"),n=e.join(o,"versions",String(l));r.mkdirSync(n,{recursive:!0,mode:448}),process.platform!=="win32"&&r.chmodSync(n,448),m(e.join(n,"package.json"),{type:"module"});const i=t.assetPath??S(new URL("./updater-worker.js",import.meta.url)),s=e.join(n,"updater-worker.js"),p=r.readFileSync(i),u=(()=>{try{return r.readFileSync(s)}catch{return null}})();if(!u||!u.equals(p)){const c=`${s}.tmp-${process.pid}-${d()}`;try{r.writeFileSync(c,p,{mode:448,flag:"wx"}),process.platform!=="win32"&&r.chmodSync(c,448);const y=r.openSync(c,process.platform==="win32"?"r+":"r");try{r.fsyncSync(y)}finally{r.closeSync(y)}r.renameSync(c,s)}finally{try{r.unlinkSync(c)}catch{}}}const a={protocolVersion:l,workerPath:s,installedAt:(t.now??new Date).toISOString()};return m(e.join(o,"current.json"),a),m(e.join(o,"readiness.json"),{protocolVersion:a.protocolVersion,installedAt:a.installedAt}),a}function $(t=f("runtime","updater")){try{const o=JSON.parse(r.readFileSync(e.join(t,"current.json"),"utf8"));if(o.protocolVersion!==l||!o.workerPath)return null;const n=r.lstatSync(o.workerPath);return n.isSymbolicLink()||!n.isFile()?null:o}catch{return null}}export{U as ensureUpdaterInstalled,$ as readUpdaterReadiness};
@@ -1,4 +1,4 @@
1
- import { spawn, type ChildProcess } from 'node:child_process';
1
+ import { spawn, spawnSync, type ChildProcess } from 'node:child_process';
2
2
  import { ensureUpdaterInstalled } from './updater-bootstrap.js';
3
3
  import { type UpdaterJob } from './updater-protocol.js';
4
4
  export declare function createUpdaterJob(input: {
@@ -15,14 +15,18 @@ export declare function createUpdaterJob(input: {
15
15
  isolatedDaemonStart?: boolean;
16
16
  }): UpdaterJob;
17
17
  export declare function writeUpdaterJob(job: UpdaterJob, root?: string): string;
18
+ export declare function needsSystemdUpdater(platform: string, cgroup: string): boolean;
18
19
  export declare function launchUpdater(input: Parameters<typeof createUpdaterJob>[0], deps?: {
19
20
  root?: string;
20
21
  spawn?: typeof spawn;
21
22
  readiness?: ReturnType<typeof ensureUpdaterInstalled>;
22
23
  now?: () => number;
23
24
  isAlive?: (pid: number) => boolean;
25
+ serviceCgroup?: string;
26
+ platform?: NodeJS.Platform;
27
+ systemdRun?: typeof spawnSync;
24
28
  }): {
25
29
  job: UpdaterJob;
26
- process: ChildProcess;
30
+ process: ChildProcess | null;
27
31
  };
28
32
  export declare function markUpdaterHealthy(jobId: string, effectiveVersion: string, root?: string): boolean;
@@ -1,3 +1,3 @@
1
- import r from"node:fs";import l from"node:path";import{randomUUID as y}from"node:crypto";import{spawn as j}from"node:child_process";import{resolveShennianPath as p}from"../config/index.js";import{findPackageJson as x,resolveNpmGlobalPrefixFromPackageJson as I}from"./engine.js";import{ensureUpdaterInstalled as P}from"./updater-bootstrap.js";import{UPDATER_PROTOCOL_VERSION as S}from"./updater-protocol.js";function g(e,n){r.mkdirSync(l.dirname(e),{recursive:!0,mode:448});const o=`${e}.tmp-${process.pid}-${y()}`;try{r.writeFileSync(o,`${JSON.stringify(n,null,2)}
2
- `,{mode:384,flag:"wx"});const c=r.openSync(o,process.platform==="win32"?"r+":"r");try{r.fsyncSync(c)}finally{r.closeSync(c)}if(r.renameSync(o,e),process.platform!=="win32"){const t=r.openSync(l.dirname(e),"r");try{r.fsyncSync(t)}finally{r.closeSync(t)}}}finally{try{r.unlinkSync(o)}catch{}}}function O(e){const n=e.root??p("runtime","updater"),o=x(),c=e.npmPrefix??(o?I(o):null);if(!c)throw new Error("Cannot determine the active npm prefix");const t=e.jobId??y();return{protocolVersion:S,jobId:t,fromVersion:e.fromVersion,targetVersion:e.targetVersion,npmPrefix:c,npmExecutable:e.npmExecutable??(process.platform==="win32"?"npm.cmd":"npm"),nodeExecutable:e.nodeExecutable??process.execPath,packageName:"shennian",operation:e.operation??"upgrade",oldDaemonPid:e.oldDaemonPid,platform:process.platform,createdAt:(e.now??new Date).toISOString(),daemonExitTimeoutMs:15e3,commandTimeoutMs:12e4,resultPath:l.join(n,"results",`${t}.json`),attemptPath:p("upgrade-attempt.json"),...e.isolatedDaemonStart?{isolatedDaemonStart:!0}:{}}}function E(e,n=p("runtime","updater")){const o=l.join(n,"jobs");r.mkdirSync(o,{recursive:!0,mode:448});const c=l.join(o,`${e.jobId}.json`),t=`${c}.tmp-${process.pid}-${y()}`;try{r.writeFileSync(t,`${JSON.stringify(e,null,2)}
3
- `,{mode:384,flag:"wx"});const a=r.openSync(t,process.platform==="win32"?"r+":"r");try{r.fsyncSync(a)}finally{r.closeSync(a)}if(r.renameSync(t,c),process.platform!=="win32"){const s=r.openSync(o,"r");try{r.fsyncSync(s)}finally{r.closeSync(s)}}return c}finally{try{r.unlinkSync(t)}catch{}}}function U(e,n={}){const o=n.root??e.root??p("runtime","updater"),c=n.readiness??P({root:o}),t=O({...e,root:o}),a=E(t,o),s=l.join(o,"updater.lock");r.mkdirSync(o,{recursive:!0,mode:448});let i=null,f=!1;const w=()=>{if(f){try{JSON.parse(r.readFileSync(s,"utf8")).jobId===t.jobId&&r.unlinkSync(s)}catch{}f=!1}};try{try{i=r.openSync(s,"wx",384),f=!0}catch(m){if(m.code!=="EEXIST")throw m;let h=!1;try{const u=JSON.parse(r.readFileSync(s,"utf8")),k=n.isAlive??(b=>{try{return process.kill(b,0),!0}catch{return!1}});h=!!(u.pid&&k(u.pid)&&(n.now??Date.now)()-Number(u.createdAt)<30*6e4)}catch{}if(h)throw new Error("upgrade_locked");r.rmSync(s,{force:!0}),i=r.openSync(s,"wx",384),f=!0}r.writeFileSync(i,JSON.stringify({jobId:t.jobId,pid:process.pid,createdAt:(n.now??Date.now)()})),r.closeSync(i),i=null;const d=(n.spawn??j)(t.nodeExecutable,[c.workerPath,a],{detached:!0,stdio:"ignore",windowsHide:!0});return d.once?.("error",m=>{g(t.resultPath,{protocolVersion:S,jobId:t.jobId,state:"failed",fromVersion:t.fromVersion,targetVersion:t.targetVersion,errorCode:"internal_error",errorSummary:m instanceof Error?m.message.slice(0,500):"updater_spawn_failed",updatedAt:new Date().toISOString()}),w()}),d.unref(),{job:t,process:d}}catch(d){i!==null&&r.closeSync(i),w();try{r.unlinkSync(a)}catch{}throw d}}function v(e,n,o=p("runtime","updater")){if(!/^[A-Za-z0-9_-]{1,128}$/.test(e))return!1;const c=l.join(o,"results",`${e}.json`);try{const t=r.lstatSync(c);if(t.isSymbolicLink()||!t.isFile())return!1;const a=JSON.parse(r.readFileSync(c,"utf8"));return a.protocolVersion!==S||a.jobId!==e||a.state!=="starting"||a.targetVersion!==n?!1:(g(c,{...a,state:a.operation==="rollback"?"rolled_back":"healthy",effectiveVersion:n,updatedAt:new Date().toISOString()}),!0)}catch{return!1}}export{O as createUpdaterJob,U as launchUpdater,v as markUpdaterHealthy,E as writeUpdaterJob};
1
+ import e from"node:fs";import u from"node:path";import{randomUUID as S}from"node:crypto";import{spawn as j,spawnSync as P}from"node:child_process";import{resolveShennianPath as p}from"../config/index.js";import{findPackageJson as I,resolveNpmGlobalPrefixFromPackageJson as E}from"./engine.js";import{ensureUpdaterInstalled as _}from"./updater-bootstrap.js";import{UPDATER_PROTOCOL_VERSION as w}from"./updater-protocol.js";function b(r,o){e.mkdirSync(u.dirname(r),{recursive:!0,mode:448});const n=`${r}.tmp-${process.pid}-${S()}`;try{e.writeFileSync(n,`${JSON.stringify(o,null,2)}
2
+ `,{mode:384,flag:"wx"});const c=e.openSync(n,process.platform==="win32"?"r+":"r");try{e.fsyncSync(c)}finally{e.closeSync(c)}if(e.renameSync(n,r),process.platform!=="win32"){const t=e.openSync(u.dirname(r),"r");try{e.fsyncSync(t)}finally{e.closeSync(t)}}}finally{try{e.unlinkSync(n)}catch{}}}function $(r){const o=r.root??p("runtime","updater"),n=I(),c=r.npmPrefix??(n?E(n):null);if(!c)throw new Error("Cannot determine the active npm prefix");const t=r.jobId??S();return{protocolVersion:w,jobId:t,fromVersion:r.fromVersion,targetVersion:r.targetVersion,npmPrefix:c,npmExecutable:r.npmExecutable??(process.platform==="win32"?"npm.cmd":"npm"),nodeExecutable:r.nodeExecutable??process.execPath,packageName:"shennian",operation:r.operation??"upgrade",oldDaemonPid:r.oldDaemonPid,platform:process.platform,createdAt:(r.now??new Date).toISOString(),daemonExitTimeoutMs:15e3,commandTimeoutMs:12e4,resultPath:u.join(o,"results",`${t}.json`),attemptPath:p("upgrade-attempt.json"),...r.isolatedDaemonStart?{isolatedDaemonStart:!0}:{}}}function O(r,o=p("runtime","updater")){const n=u.join(o,"jobs");e.mkdirSync(n,{recursive:!0,mode:448});const c=u.join(n,`${r.jobId}.json`),t=`${c}.tmp-${process.pid}-${S()}`;try{e.writeFileSync(t,`${JSON.stringify(r,null,2)}
3
+ `,{mode:384,flag:"wx"});const s=e.openSync(t,process.platform==="win32"?"r+":"r");try{e.fsyncSync(s)}finally{e.closeSync(s)}if(e.renameSync(t,c),process.platform!=="win32"){const a=e.openSync(n,"r");try{e.fsyncSync(a)}finally{e.closeSync(a)}}return c}finally{try{e.unlinkSync(t)}catch{}}}function v(r,o){return r==="linux"&&/(?:^|\/)shennian\.service(?:\n|$)/m.test(o.trim())}function C(r,o={}){const n=o.root??r.root??p("runtime","updater"),c=o.readiness??_({root:n}),t=$({...r,root:n}),s=O(t,n),a=u.join(n,"updater.lock");e.mkdirSync(n,{recursive:!0,mode:448});let l=null,m=!1;const h=()=>{if(m){try{JSON.parse(e.readFileSync(a,"utf8")).jobId===t.jobId&&e.unlinkSync(a)}catch{}m=!1}};try{try{l=e.openSync(a,"wx",384),m=!0}catch(i){if(i.code!=="EEXIST")throw i;let g=!1;try{const y=JSON.parse(e.readFileSync(a,"utf8")),k=o.isAlive??(x=>{try{return process.kill(x,0),!0}catch{return!1}});g=!!(y.pid&&k(y.pid)&&(o.now??Date.now)()-Number(y.createdAt)<30*6e4)}catch{}if(g)throw new Error("upgrade_locked");e.rmSync(a,{force:!0}),l=e.openSync(a,"wx",384),m=!0}e.writeFileSync(l,JSON.stringify({jobId:t.jobId,pid:process.pid,createdAt:(o.now??Date.now)()})),e.closeSync(l),l=null;let d=o.serviceCgroup??"";if(process.platform==="linux"&&!o.spawn&&o.serviceCgroup===void 0)try{d=e.readFileSync("/proc/self/cgroup","utf8")}catch{}if(v(o.platform??process.platform,d)){const i=(o.systemdRun??P)("systemd-run",["--user","--quiet","--collect",`--unit=shennian-updater-${t.jobId}`,"--property=Type=exec","--property=KillMode=process",`--setenv=PATH=${process.env.PATH??"/usr/local/bin:/usr/bin:/bin"}`,"--",t.nodeExecutable,c.workerPath,s],{encoding:"utf8",stdio:"pipe",timeout:1e4});if(i.error||i.status!==0)throw new Error("updater_service_launch_failed");return{job:t,process:null}}const f=(o.spawn??j)(t.nodeExecutable,[c.workerPath,s],{detached:!0,stdio:"ignore",windowsHide:!0});return f.once?.("error",i=>{b(t.resultPath,{protocolVersion:w,jobId:t.jobId,state:"failed",fromVersion:t.fromVersion,targetVersion:t.targetVersion,errorCode:"internal_error",errorSummary:i instanceof Error?i.message.slice(0,500):"updater_spawn_failed",updatedAt:new Date().toISOString()}),h()}),f.unref(),{job:t,process:f}}catch(d){l!==null&&e.closeSync(l),h();try{e.unlinkSync(s)}catch{}throw d}}function H(r,o,n=p("runtime","updater")){if(!/^[A-Za-z0-9_-]{1,128}$/.test(r))return!1;const c=u.join(n,"results",`${r}.json`);try{const t=e.lstatSync(c);if(t.isSymbolicLink()||!t.isFile())return!1;const s=JSON.parse(e.readFileSync(c,"utf8"));return s.protocolVersion!==w||s.jobId!==r||s.state!=="starting"||s.targetVersion!==o?!1:(b(c,{...s,state:s.operation==="rollback"?"rolled_back":"healthy",effectiveVersion:o,updatedAt:new Date().toISOString()}),!0)}catch{return!1}}export{$ as createUpdaterJob,C as launchUpdater,H as markUpdaterHealthy,v as needsSystemdUpdater,O as writeUpdaterJob};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shennian",
3
- "version": "0.4.35",
3
+ "version": "0.4.36",
4
4
  "description": "Shennian — AI Agent Control Plane CLI",
5
5
  "type": "module",
6
6
  "bin": {