shennian 0.4.15 → 0.4.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.
|
@@ -306,8 +306,8 @@
|
|
|
306
306
|
},
|
|
307
307
|
{
|
|
308
308
|
"file": "src/commands/daemon.js",
|
|
309
|
-
"beforeBytes":
|
|
310
|
-
"afterBytes":
|
|
309
|
+
"beforeBytes": 44966,
|
|
310
|
+
"afterBytes": 21766
|
|
311
311
|
},
|
|
312
312
|
{
|
|
313
313
|
"file": "src/commands/integration.js",
|
|
@@ -436,8 +436,8 @@
|
|
|
436
436
|
},
|
|
437
437
|
{
|
|
438
438
|
"file": "src/index.js",
|
|
439
|
-
"beforeBytes":
|
|
440
|
-
"afterBytes":
|
|
439
|
+
"beforeBytes": 35946,
|
|
440
|
+
"afterBytes": 17661
|
|
441
441
|
},
|
|
442
442
|
{
|
|
443
443
|
"file": "src/log-redaction.js",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import c from"chalk";import o from"node:fs";import a from"node:path";import d from"node:os";import{execSync as u,spawn as fe}from"node:child_process";import{randomUUID as F}from"node:crypto";import{fileURLToPath as Se}from"node:url";import{getShennianDir as he,loadConfig as B,resolveShennianPath as S,saveConfig as ge}from"../config/index.js";import{buildAugmentedPath as ye}from"../env-path.js";import{buildWindowsLauncherCommand as we,buildWindowsScheduledTaskCommands as Ee,buildWindowsScheduledTaskXml as ve,buildWindowsStartupVbs as
|
|
2
|
-
`)}function Z(e){const n=e?.trim();if(!n)return;const r=B();r.serverUrl=n,ge(r)}function ee(e){return e?.trim()||process.env.SHENNIAN_DESKTOP_SERVER_URL?.trim()||void 0}const H="com.shennian.agent",h=a.join(d.homedir(),"Library/LaunchAgents",`${H}.plist`),ne=S("Shennian.app"),Ve=a.join(d.homedir(),"AppData","Roaming","Microsoft","Windows","Start Menu","Programs","Startup"),
|
|
1
|
+
import c from"chalk";import o from"node:fs";import a from"node:path";import d from"node:os";import{execSync as u,spawn as fe}from"node:child_process";import{randomUUID as F}from"node:crypto";import{fileURLToPath as Se}from"node:url";import{getShennianDir as he,loadConfig as B,resolveShennianPath as S,saveConfig as ge}from"../config/index.js";import{buildAugmentedPath as ye}from"../env-path.js";import{buildWindowsLauncherCommand as we,buildWindowsScheduledTaskCommands as Ee,buildWindowsScheduledTaskXml as ve,buildWindowsStartupVbs as Ae,escapeXml as x,isWindowsCmdScript as Pe,quoteCmdArg as V,quoteSystemdArg as Ne,suspendWindowsScheduledTask as Ie}from"./daemon-windows.js";import{waitForDaemonReadyStatus as T}from"./daemon-start-wait.js";import{findRunningWindowsDaemonProcessIds as De,isShennianRunServiceCommand as ke}from"./daemon-process.js";import{getCurrentVersion as G}from"../upgrade/engine.js";import{buildWindowsLauncherCommand as bn,buildWindowsScheduledTaskCommands as Rn,buildWindowsScheduledTaskSuspendCommands as xn,buildWindowsScheduledTaskXml as Tn,buildWindowsStartupVbs as Ln,suspendWindowsScheduledTask as On}from"./daemon-windows.js";import{isShennianRunServiceCommand as Hn}from"./daemon-process.js";const _e=a.dirname(Se(import.meta.url)),g=he(),y=S("daemon.pid"),m=S("daemon.log"),L=S("remote-access.disabled"),k=S("daemon-launcher.json"),K=a.resolve(_e,"../../bin/shennian.js"),J=process.execPath,q=new Set(["PATH","HOME","USERPROFILE","HOMEDRIVE","HOMEPATH","USERNAME","USERDOMAIN","COMPUTERNAME","USER","LOGNAME","SHELL","TMPDIR","LANG","LC_ALL","LC_CTYPE","SSH_AUTH_SOCK","XDG_CONFIG_HOME","XDG_DATA_HOME","TEMP","TMP","APPDATA","LOCALAPPDATA","ELECTRON_RUN_AS_NODE","SHENNIAN_DESKTOP_CLI_SCRIPT","SHENNIAN_DESKTOP_CLI_BRIDGE","SHENNIAN_DESKTOP_SERVER_URL","SHENNIAN_WECHAT_CHANNEL_HELPER_DIR","SHENNIAN_HOME","SHENNIAN_ACCOUNT_NAMESPACE_ID","SHENNIAN_NATIVE_FUSION_DISABLED","SHENNIAN_WECHAT_DELIVERABILITY_TRACE"]);function be(e){return q.has(e)}function p(){const e=d.platform();return e==="darwin"||e==="linux"||e==="win32"?e:"linux"}function O(e){const n=e.replace(/\\/g,"/").toLowerCase(),r=d.tmpdir().replace(/\\/g,"/").toLowerCase();return n.includes("/_npx/")||n.includes("/npm-cache/_npx/")||n.includes("/pnpm/dlx/")||n.startsWith(r.endsWith("/")?r:`${r}/`)}function Re(e){return process.env.ELECTRON_RUN_AS_NODE==="1"&&e.desktopBridgePath&&o.existsSync(e.scriptPath)&&o.existsSync(e.desktopBridgePath)?{command:e.nodeExec,args:[e.desktopBridgePath,e.scriptPath,"run-service"],mode:"direct"}:o.existsSync(e.scriptPath)&&(process.env.SHENNIAN_E2E_ISOLATED_DAEMON==="1"||!O(e.scriptPath))?{command:e.nodeExec,args:[e.scriptPath,"run-service"],mode:"direct"}:e.shennianCommandPath&&!O(e.shennianCommandPath)?{command:e.shennianCommandPath,args:["run-service"],mode:"global-shim"}:e.npxPath?{command:e.npxPath,args:["--yes","shennian","run-service"],mode:"npx"}:{command:e.nodeExec,args:[e.scriptPath,"run-service"],mode:"direct"}}function xe(e){const n=e.trim();if(!n)return null;if(n.startsWith("{"))try{const t=JSON.parse(n),s=Number(t.pid);return!Number.isInteger(s)||s<=0?null:{pid:s,...typeof t.instanceId=="string"?{instanceId:t.instanceId}:{},...typeof t.version=="string"?{version:t.version}:{},...t.launcher==="desktop-managed"||t.launcher==="global-cli"||t.launcher==="unknown"?{launcher:t.launcher}:{},...typeof t.startedAt=="string"?{startedAt:t.startedAt}:{},...t.adopted===!0?{adopted:!0}:{}}}catch{return null}const r=parseInt(n,10);return isNaN(r)?null:{pid:r}}function P(){try{return xe(o.readFileSync(y,"utf-8"))}catch{return null}}function v(e){try{return process.kill(e,0),!0}catch{return!1}}function X(e,n=3e3){const r=Date.now();for(;Date.now()-r<n;){if(!v(e))return!0;Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,100)}return!v(e)}function Te(){return o.existsSync(L)}function Le(){try{o.unlinkSync(L)}catch{}}function Oe(e,n){const r=a.dirname(e),t=`${e}.${process.pid}.${F()}.tmp`;o.mkdirSync(r,{recursive:!0});let s=null;try{s=o.openSync(t,"w",384),o.writeSync(s,n,void 0,"utf8"),o.fsyncSync(s),o.closeSync(s),s=null,o.renameSync(t,e);try{const i=o.openSync(r,"r");try{o.fsyncSync(i)}finally{o.closeSync(i)}}catch{}}finally{s!==null&&o.closeSync(s);try{o.unlinkSync(t)}catch{}}}function Ce(e={}){const n=e.disabledAt??new Date().toISOString(),r=e.markerFile??L,t=e.platform??p(),s=e.servicePresent??M(t),i=e.execCommand??(l=>{u(l,{stdio:"pipe",windowsHide:!0})});if(Oe(r,n),!s)return{disabledAt:n,autoStartDisabled:!0};try{return i(t==="darwin"?`launchctl disable "${A()}"`:t==="linux"?"systemctl --user disable shennian":`schtasks /change /tn "${D}" /disable`),{disabledAt:n,autoStartDisabled:!0}}catch(l){return{disabledAt:n,autoStartDisabled:!1,autoStartError:l instanceof Error?l.message:String(l)}}}function He(e={}){const n=e.platform??p();if(!(e.servicePresent??M(n)))return{serviceStopped:!0};const t=e.execCommand??(s=>{u(s,{stdio:"pipe",windowsHide:!0})});try{return t(n==="darwin"?`launchctl bootout "${A()}"`:n==="linux"?"systemctl --user stop shennian":`schtasks /end /tn "${D}"`),{serviceStopped:!0}}catch(s){return{serviceStopped:!1,error:s instanceof Error?s.message:String(s)}}}function Y(){return process.env.SHENNIAN_DESKTOP_CLI_SCRIPT?"desktop-managed":"global-cli"}function $e(){return`${process.pid}-${Date.now()}-${F()}`}function Me(e,n,r={}){o.mkdirSync(g,{recursive:!0}),o.writeFileSync(y,JSON.stringify({pid:e,instanceId:n,...r.version?{version:r.version}:{},launcher:r.launcher??Y(),...r.adopted?{adopted:!0}:{},startedAt:new Date().toISOString()},null,2))}function In(e,n){const r=P();if(!(r?.pid!==e||r.instanceId!==n))try{o.unlinkSync(y)}catch{}}function z(e,n=Y(),r){o.mkdirSync(g,{recursive:!0}),o.writeFileSync(k,JSON.stringify({pid:e,launcher:n,...r?{instanceId:r}:{},updatedAt:new Date().toISOString()},null,2))}function Ue(e){if(e)try{if(JSON.parse(o.readFileSync(k,"utf-8")).instanceId!==e)return}catch{return}try{o.unlinkSync(k)}catch{}}function je(e){e&&z(e)}function We(e,n){if(!e||!n)return"unknown";try{const r=JSON.parse(o.readFileSync(k,"utf-8"));if(r.pid!==e)return"unknown";if(r.launcher==="desktop-managed"||r.launcher==="global-cli")return r.launcher}catch{}return C(e)}function C(e){if(p()==="win32")return"unknown";try{const n=u(`ps -p ${e} -o command=`,{encoding:"utf-8",stdio:["ignore","pipe","ignore"],timeout:1e3,windowsHide:!0}).replace(/\\/g,"/");if(n.includes("/cli-bridge.js")||n.includes("/Resources/daemon/")||n.includes("\\resources\\daemon\\"))return"desktop-managed";if(n.includes("/node_modules/shennian/")||n.includes("/bin/shennian")||n.includes(" shennian run-service")||n.includes(" shennian.js run-service"))return"global-cli"}catch{}return"unknown"}function Q(e=process.pid){if(p()==="win32")return De(e);try{return u("ps -axo pid=,command=",{encoding:"utf-8",stdio:["ignore","pipe","ignore"],timeout:1e3,windowsHide:!0}).split(/\r?\n/).map(r=>{const t=r.trim().match(/^(\d+)\s+(.+)$/);if(!t)return null;const s=Number(t[1]),i=t[2];return!Number.isInteger(s)||s===e?null:ke(i)?s:null}).filter(r=>typeof r=="number")}catch{return[]}}function Fe(e=process.env,n=g,r=d.tmpdir(),t=a.join(d.homedir(),".shennian")){if(e.SHENNIAN_E2E_ISOLATED_DAEMON!=="1"||!e.SHENNIAN_HOME?.trim()||a.resolve(n)===a.resolve(t))return!1;const s=a.relative(a.resolve(r),a.resolve(n));return s!==""&&s!==".."&&!s.startsWith(`..${a.sep}`)&&!a.isAbsolute(s)||a.resolve(n)===a.resolve(e.SHENNIAN_HOME)}function Be(e=process.env,n=g){return!Fe(e,n)}function f(e={}){let n=P(),r=n?.pid??null,t=r!==null&&v(r),s=r!==null&&!t,i=n?.adopted===!0;const l=B();if(s&&e.cleanupStale){try{o.unlinkSync(y)}catch{}n=null,r=null,t=!1,s=!1}if(!t&&Be()){const E=Q()[0];if(E&&(r=E,t=!0,s=!1,i=!0,e.cleanupStale)){const W=$e();Me(E,W,{launcher:C(E),adopted:!0}),z(E,C(E)),n={pid:E,instanceId:W,adopted:!0}}}return{running:t,pid:r,stale:s,remoteAccessDisabled:Te(),launcher:We(r,t),platform:p(),shennianDir:g,pidFile:y,logFile:m,...n?.instanceId?{instanceId:n.instanceId}:{},...i?{adopted:!0}:{},...l.machineId?{machineId:l.machineId}:{},paired:!!(l.machineToken&&l.machineId),...l.serverUrl?{serverUrl:l.serverUrl}:{}}}function w(e){process.stdout.write(`${JSON.stringify(e,null,2)}
|
|
2
|
+
`)}function Z(e){const n=e?.trim();if(!n)return;const r=B();r.serverUrl=n,ge(r)}function ee(e){return e?.trim()||process.env.SHENNIAN_DESKTOP_SERVER_URL?.trim()||void 0}const H="com.shennian.agent",h=a.join(d.homedir(),"Library/LaunchAgents",`${H}.plist`),ne=S("Shennian.app"),Ve=a.join(d.homedir(),"AppData","Roaming","Microsoft","Windows","Start Menu","Programs","Startup"),_=S("autostart.cmd"),b=S("autostart.vbs"),R=S("autostart.xml"),Ge=a.join(Ve,"ShennianAgent.vbs");function te(e){const n=p()==="win32"?`where ${e}`:`command -v ${e}`;try{return u(n,{stdio:["ignore","pipe","pipe"],encoding:"utf-8",windowsHide:!0}).split(/\r?\n/).map(s=>s.trim()).find(Boolean)??null}catch{return null}}function Ke(){const e=p()==="win32"?"npx.cmd":"npx",n=a.join(a.dirname(J),e);return o.existsSync(n)?n:te("npx")}function N(){return Re({nodeExec:J,scriptPath:K,shennianCommandPath:te("shennian"),npxPath:Ke(),desktopBridgePath:process.env.SHENNIAN_DESKTOP_CLI_BRIDGE})}function re(){try{u(`schtasks /delete /tn "${D}" /f`,{stdio:"pipe",windowsHide:!0})}catch{}}function oe(){try{o.unlinkSync(Ge)}catch{}}function Je(){const e=process.env.USERDOMAIN?.trim(),n=process.env.COMPUTERNAME?.trim(),r=process.env.USERNAME?.trim()||d.userInfo().username;return e&&e.toUpperCase()!=="WORKGROUP"?`${e}\\${r}`:n?`${n}\\${r}`:r}function qe(e){if(p()==="win32"&&Pe(e.command)){const n=[e.command,...e.args].map(V).join(" ");return{command:process.env.ComSpec||"cmd.exe",args:["/d","/s","/c",`"${n}"`],windowsVerbatimArguments:!0}}return{command:e.command,args:e.args}}function Xe(e,n,r=process.env){return{detached:!0,stdio:["ignore",n,n],env:r,windowsHide:!0,...e.windowsVerbatimArguments?{windowsVerbatimArguments:!0}:{}}}function Ye(){const e=N();o.writeFileSync(_,we(e,m)),o.writeFileSync(b,Ae(_)),oe(),re();const n=ve({userId:Je(),command:a.join(process.env.SystemRoot||"C:\\Windows","System32","wscript.exe"),arguments:[V(b)]});o.writeFileSync(R,n,"utf8");const r=Ee({taskName:D,xmlPath:R});try{return u(r.create,{stdio:"pipe",windowsHide:!0}),u(r.run,{stdio:"pipe",windowsHide:!0}),!0}catch{return!1}}function ze(){Ie(D,e=>{u(e,{stdio:"pipe",windowsHide:!0})})}function se(){const e={};for(const n of q)process.env[n]&&(e[n]=process.env[n]);if(e.HOME??=d.homedir(),e.PATH=ye({pathValue:e.PATH,env:e}),e.USER??=d.userInfo().username,p()==="win32"){const n=process.env.TEMP||process.env.TMP||d.tmpdir(),r=d.homedir();e.USERPROFILE??=r;const t=a.win32.parse(r);e.HOMEDRIVE??=t.root.replace(/[\\/]$/,"")||process.env.HOMEDRIVE||"",e.HOMEPATH??=r.slice((e.HOMEDRIVE||"").length)||process.env.HOMEPATH||"",e.USERNAME??=process.env.USERNAME||d.userInfo().username,e.COMPUTERNAME??=process.env.COMPUTERNAME||"",e.TMPDIR??=n,e.TEMP??=n,e.TMP??=n}else e.TMPDIR??="/tmp";return e}function Qe(e={}){const n=se(),r=e.launch??N(),t=Object.entries(n).map(([i,l])=>` <key>${x(i)}</key>
|
|
3
3
|
<string>${x(l)}</string>`).join(`
|
|
4
4
|
`),s=[r.command,...r.args].map(i=>` <string>${x(i)}</string>`).join(`
|
|
5
5
|
`);return`<?xml version="1.0" encoding="UTF-8"?>
|
|
@@ -27,8 +27,8 @@ ${t}
|
|
|
27
27
|
<key>StandardErrorPath</key>
|
|
28
28
|
<string>${m}</string>
|
|
29
29
|
</dict>
|
|
30
|
-
</plist>`}function ie(e=ne){o.rmSync(e,{recursive:!0,force:!0})}function Ze(e=p()){return e!=="darwin"?null:"macOS may show \u201CNode.js Foundation\u201D under Allow in the Background. This is the Node.js runtime used by Shennian Client."}const
|
|
31
|
-
`),t=[n.command,...n.args].map(
|
|
30
|
+
</plist>`}function ie(e=ne){o.rmSync(e,{recursive:!0,force:!0})}function Ze(e=p()){return e!=="darwin"?null:"macOS may show \u201CNode.js Foundation\u201D under Allow in the Background. This is the Node.js runtime used by Shennian Client."}const I=a.join(d.homedir(),".config/systemd/user/shennian.service");function en(){const e=se(),n=N(),r=Object.entries(e).map(([s,i])=>`Environment=${s}=${i}`).join(`
|
|
31
|
+
`),t=[n.command,...n.args].map(Ne).join(" ");return`[Unit]
|
|
32
32
|
Description=Shennian Agent Daemon
|
|
33
33
|
After=network.target
|
|
34
34
|
|
|
@@ -41,6 +41,6 @@ StandardOutput=append:${m}
|
|
|
41
41
|
StandardError=append:${m}
|
|
42
42
|
|
|
43
43
|
[Install]
|
|
44
|
-
WantedBy=default.target`}const D="ShennianAgent";function nn(e){u(e,{stdio:"pipe",windowsHide:!0})}function tn(){const e=process.getuid?.();if(typeof e=="number")return e;const n=d.userInfo();return typeof n.uid=="number"?n.uid:n.username}function
|
|
44
|
+
WantedBy=default.target`}const D="ShennianAgent";function nn(e){u(e,{stdio:"pipe",windowsHide:!0})}function tn(){const e=process.getuid?.();if(typeof e=="number")return e;const n=d.userInfo();return typeof n.uid=="number"?n.uid:n.username}function A(e=tn()){return`gui/${e}/${H}`}function rn(e=nn){try{return e(`launchctl enable "${A()}"; launchctl unload "${h}" 2>/dev/null; launchctl load -w "${h}"`),e(`launchctl kickstart -k "${A()}"`),!0}catch{try{return e(`launchctl enable "${A()}"`),e(`launchctl load -w "${h}"`),e(`launchctl kickstart -k "${A()}"`),!0}catch{return!1}}}function on(){o.mkdirSync(g,{recursive:!0});const e={};for(const[n,r]of Object.entries(process.env))r!==void 0&&be(n)&&(e[n]=r);o.writeFileSync(S("env.json"),JSON.stringify(e,null,2))}function ce(e={}){o.mkdirSync(g,{recursive:!0});const n=f({cleanupStale:!0});if(n.pid!==null&&n.running&&!$(n)){e.quiet||console.log(c.green(`\u2713 Background service already running (PID ${n.pid})`));return}ae(n);const r=o.openSync(m,"a"),t=qe(N()),s=fe(t.command,t.args,Xe(t,r));s.unref(),o.closeSync(r),je(s.pid),e.quiet||(console.log(c.green(`\u2713 Background service started (PID ${s.pid})`)),console.log(c.gray(` Logs: ${m}`)))}function $(e,n=P(),r=G()){return e.pid===null||!e.running?!1:e.adopted===!0||n?.version!==r}function M(e=p()){switch(e){case"darwin":return o.existsSync(h)&&!o.existsSync(ne);case"linux":return o.existsSync(I);case"win32":return o.existsSync(_)&&o.existsSync(b)&&o.existsSync(R)}}function sn(e,n,r,t=P(),s=G()){return!n.api?.trim()&&e.pid!==null&&e.running&&!e.stale&&!e.remoteAccessDisabled&&r&&!$(e,t,s)}function ae(e=f({cleanupStale:!0})){if(!(!$(e)||e.pid===null))try{process.kill(e.pid,"SIGTERM"),X(e.pid,5e3)||(process.kill(e.pid,"SIGKILL"),X(e.pid,2e3));try{P()?.pid===e.pid&&o.unlinkSync(y)}catch{}}catch{}}function ue(e={}){o.mkdirSync(g,{recursive:!0});const n=p();switch(N().mode==="direct"&&O(K)&&console.warn(c.yellow("\u26A0 Warning: Current CLI path is temporary (npx). Auto-start may not work after reboot.\n Run `npm install -g shennian@latest` for reliable auto-start.")),n){case"darwin":{const t=a.dirname(h);o.mkdirSync(t,{recursive:!0}),ie(),o.writeFileSync(h,Qe());const s=rn(),i=Ze(n);return!e.quiet&&i&&console.log(c.gray(`\u2139 ${i}`)),s}case"linux":{const t=a.dirname(I);o.mkdirSync(t,{recursive:!0}),o.writeFileSync(I,en());try{u("systemctl --user daemon-reload && systemctl --user enable shennian",{stdio:"pipe",windowsHide:!0})}catch{return!1}try{u(`loginctl enable-linger ${d.userInfo().username}`,{stdio:"pipe",windowsHide:!0})}catch{}try{return u("systemctl --user restart shennian",{stdio:"pipe",windowsHide:!0}),!0}catch{return!1}}case"win32":return Ye()}}function cn(){const n=f({cleanupStale:!0}).pid;if(n===null)return{};if(!v(n)){try{o.unlinkSync(y)}catch{}return{stalePid:n}}try{return process.kill(n,"SIGTERM"),{stoppedPid:n}}catch(r){return{error:r instanceof Error?r.message:String(r)}}}async function de(e=5e3){const n=cn();if(!n.stoppedPid)return n;if(!await j(n.stoppedPid,e))try{process.kill(n.stoppedPid,"SIGKILL"),await j(n.stoppedPid,2e3)}catch(t){return{...n,error:t instanceof Error?t.message:String(t)}}try{o.unlinkSync(y)}catch{}Ue();for(const t of Q())if(t!==n.stoppedPid)try{process.kill(t,"SIGTERM"),await j(t,2e3)}catch{}return n}function an(e={}){Z(ee(e.api)),Le(),ae(),ue({quiet:e.json})||ce({quiet:e.json}),e.json?w(T(()=>f({cleanupStale:!0}))):console.log(c.green("\u2713 Background service started"))}async function un(e={}){const n=Ce(),r=He(),t=await de(),s=f({cleanupStale:!0}),i=t.error??n.autoStartError??r.error;if(e.json){w(i?{...s,error:i}:s);return}i?console.error(c.red(`\u2717 Failed to stop completely: ${i}`)):console.log(c.green("\u2713 Background service stopped"))}function le(e){const n=f({cleanupStale:!0});if(sn(n,e,M())){e.json?w(n):console.log(c.green(`\u2713 Background service already running (PID ${n.pid})`));return}on(),an(e)}async function U(e={}){await un(e)}async function j(e,n=5e3){const r=Date.now();for(;Date.now()-r<n;){if(!v(e))return!0;await new Promise(t=>setTimeout(t,100))}return!v(e)}async function dn(e={}){const n=f({cleanupStale:!0});if(n.remoteAccessDisabled){e.json?w({...n,error:"remote_access_disabled_use_shennian_start"}):console.error(c.yellow("Remote access is paused. Run `shennian start` on this computer to resume."));return}Z(ee(e.api));const r=p();r==="win32"&&ze();const t=await de();if(t.error){e.json?w({...f(),error:t.error}):console.error(c.red(`\u2717 Failed to stop: ${t.error}`));return}if(t.stoppedPid!==void 0?e.json||console.log(c.green(`\u2713 Background service stopped (PID ${t.stoppedPid})`)):t.stalePid!==void 0?e.json||console.log(c.yellow(`\u26A0 Service process no longer exists, cleaned up stale PID ${t.stalePid}`)):e.json||console.log(c.gray("\u25CF Background service not running, starting it now")),(r==="linux"||r==="win32")&&ue()){e.json&&w(T(()=>f({cleanupStale:!0})));return}ce({quiet:e.json}),e.json&&w(T(()=>f({cleanupStale:!0})))}function pe(e={}){const n=f({cleanupStale:!0});if(e.json){w(n);return}if(n.pid===null){console.log(c.gray("\u25CF Background service not running"));return}n.running?(console.log(c.green(`\u25CF Background service running (PID ${n.pid})`)),console.log(c.gray(` Logs: ${m}`))):console.log(c.yellow(`\u25CF Background service stopped (PID ${n.pid} is stale)`))}function me(e){if(!o.existsSync(m)){console.log(c.gray("No logs yet"));return}try{const n=u(d.platform()==="win32"?`powershell Get-Content -Tail ${e.lines} "${m}"`:`tail -n ${e.lines} "${m}"`,{encoding:"utf-8",windowsHide:!0});process.stdout.write(n)}catch{const n=o.readFileSync(m,"utf-8").split(`
|
|
45
45
|
`).slice(-e.lines);console.log(n.join(`
|
|
46
|
-
`))}}async function ln(){switch(await U(),p()){case"darwin":{if(o.existsSync(h)){try{u(`launchctl unload -w "${h}"`,{stdio:"pipe",windowsHide:!0})}catch{}o.unlinkSync(h)}ie(),console.log(c.green("\u2713 Auto-start uninstalled (launchd)"));break}case"linux":{try{u("systemctl --user disable --now shennian",{stdio:"pipe",windowsHide:!0})}catch{}if(o.existsSync(
|
|
46
|
+
`))}}async function ln(){switch(await U(),p()){case"darwin":{if(o.existsSync(h)){try{u(`launchctl unload -w "${h}"`,{stdio:"pipe",windowsHide:!0})}catch{}o.unlinkSync(h)}ie(),console.log(c.green("\u2713 Auto-start uninstalled (launchd)"));break}case"linux":{try{u("systemctl --user disable --now shennian",{stdio:"pipe",windowsHide:!0})}catch{}if(o.existsSync(I)){o.unlinkSync(I);try{u("systemctl --user daemon-reload",{stdio:"pipe",windowsHide:!0})}catch{}}console.log(c.green("\u2713 Auto-start uninstalled (systemd)"));break}case"win32":{re();try{o.unlinkSync(_)}catch{}try{o.unlinkSync(b)}catch{}try{o.unlinkSync(R)}catch{}oe(),console.log(c.green("\u2713 Auto-start uninstalled (Task Scheduler)"));break}}}function Dn(e){e.command("start").description("Start the background service").option("--json","Print machine-readable status JSON").option("--api <url>","Server URL override").action(t=>le(t)),e.command("stop").description("Stop the background service").option("--json","Print machine-readable status JSON").action(t=>U(t)),e.command("status").description("Show background service status").option("--json","Print machine-readable status JSON").action(t=>pe(t)),e.command("logs").description("Show recent logs").option("-n, --lines <n>","Number of lines","50").action(t=>me({lines:parseInt(t.lines,10)}));const n=e.command("daemon",{hidden:!0}).description("Deprecated: use top-level start/stop/status/logs"),r=(t,s)=>{s||console.error(c.yellow(`\u26A0 Deprecated command. Use \`shennian ${t}\` instead.`))};n.command("start").description("Start the background service").option("--json","Print machine-readable status JSON").option("--api <url>","Server URL override").action(t=>{r("start",t.json),le(t)}),n.command("stop").description("Stop the background service").option("--json","Print machine-readable status JSON").action(async t=>{r("stop",t.json),await U(t)}),n.command("restart").description("Restart the background service").option("--json","Print machine-readable status JSON").option("--api <url>","Server URL override").action(t=>{r("stop && shennian start",t.json),dn(t)}),n.command("status").description("Show background service status").option("--json","Print machine-readable status JSON").action(t=>{r("status",t.json),pe(t)}),n.command("logs").description("Show recent logs").option("-n, --lines <n>","Number of lines","50").action(t=>{r("logs"),me({lines:parseInt(t.lines,10)})}),n.command("uninstall").description("Uninstall auto-start service").action(ln)}export{Xe as buildDaemonSpawnOptions,Qe as buildMacLaunchdPlist,en as buildSystemdUnit,bn as buildWindowsLauncherCommand,Rn as buildWindowsScheduledTaskCommands,xn as buildWindowsScheduledTaskSuspendCommands,Tn as buildWindowsScheduledTaskXml,Ln as buildWindowsStartupVbs,sn as canReuseRunningDaemonForStart,se as captureEnvForService,Ue as clearDaemonLauncher,In as clearDaemonPidIfOwner,Le as clearRemoteAccessDisabled,$e as createDaemonInstanceId,Q as findRunningDaemonProcessIds,Y as getCurrentProcessDaemonLauncher,f as getDaemonStatus,A as getLaunchdServiceTarget,Ze as getMacBackgroundItemNotice,ue as installService,M as isAutoStartServicePresent,O as isEphemeralCliPath,Fe as isIsolatedDaemonE2eInstance,Te as isRemoteAccessDisabled,be as isSafeSnapshotEnvKey,Hn as isShennianRunServiceCommand,Ce as prepareRemoteAccessPause,je as recordStartedDaemon,Dn as registerDaemonCommand,rn as reloadLaunchdAgent,ie as removeLegacyMacBackgroundApp,Re as resolveServiceLaunchSpec,on as saveEnvSnapshot,Be as shouldDiscoverGlobalDaemonProcesses,$ as shouldReplaceRunningDaemon,ce as startDaemonProcess,He as stopPreparedRemoteAccessService,On as suspendWindowsScheduledTask,z as writeDaemonLauncher,Me as writeDaemonPid};
|
package/dist/src/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import{Command as ve}from"commander";import k from"chalk";import Ie from"node:os";import B from"node:fs";import{loadConfig as p,saveConfig as Se,getConfigPath as ye,getShennianDir as we,resolveShennianPath as Q,normalizeAutoUpgradePolicy as Ce}from"./config/index.js";import{CliRelayClient as Re}from"./relay/client.js";import{collectKnownLogSecrets as X,installCliLogRedaction as ke,sanitizeCliLogValue as be}from"./log-redaction.js";import{registerPairCommand as Ee,runSmartStart as Ae}from"./commands/pair.js";import{registerLogoutCommand as Ue}from"./commands/logout.js";import{clearDaemonPidIfOwner as ee,createDaemonInstanceId as De,clearDaemonLauncher as oe,findRunningDaemonProcessIds as Pe,isRemoteAccessDisabled as te,prepareRemoteAccessPause as Te,stopPreparedRemoteAccessService as Me,isIsolatedDaemonE2eInstance as _e,getDaemonStatus as $e,registerDaemonCommand as Ne,writeDaemonPid as ne,writeDaemonLauncher as re}from"./commands/daemon.js";import{registerRuntimeCommand as Oe}from"./commands/runtime.js";import{registerAgentCommand as xe}from"./commands/agent.js";import{registerManagerCommand as Ve}from"./commands/manager.js";import{handleUpgradeStart as Le,registerUpgradeCommand as je}from"./commands/upgrade.js";import{registerToolsCommand as Be}from"./commands/tools.js";import{registerIntegrationCommand as He}from"./commands/integration.js";import{registerRoomCommand as Fe}from"./commands/room.js";import{SessionManager as Ge}from"./session/manager.js";import{SERVERS as H,regionToUrl as ze,urlToRegion as Ke}from"./region.js";import{clearUpgradeAttempt as Je,getCurrentVersion as ae,handleStartupCrashCheck as qe,readUpgradeAttempt as We}from"./upgrade/engine.js";import{AutoUpgradeScheduler as Ye}from"./upgrade/scheduler.js";import{ensureUpdaterInstalled as Ze}from"./upgrade/updater-bootstrap.js";import{launchUpdater as Qe,markUpdaterHealthy as Xe}from"./upgrade/updater-launcher.js";import{detectAgents as eo}from"./agents/detect.js";import{augmentProcessPath as se}from"./env-path.js";se();const F=ae();import{getCachedAgentInfos as oo,resolveAgentInfos as to}from"./agents/model-registry.js";import{initCliLogReporter as no,reportLog as b,reportUpgradeAggregateMetric as ro}from"./log-reporter.js";import{isNativeFusionEnabled as ao}from"./native-fusion/config.js";import{NativeSessionFusionService as so}from"./native-fusion/service.js";import{startDaemonLogRetention as io}from"./daemon-log.js";import{startDaemonIpcServer as ie}from"./daemon-ipc/server.js";import{resolveDaemonIpcPaths as co,resolveManagedRoomIpcPaths as lo}from"./daemon-ipc/paths.js";import{createDaemonControlHandlers as mo}from"./daemon-ipc/control.js";import{DAEMON_IPC_VERSION as po}from"./daemon-ipc/protocol.js";import{RoomSessionRegistry as go}from"./room/session-registry.js";import{HttpRoomBindingClient as uo}from"./room/room-binding-client.js";import{TrustedManagedRoomBinder as fo}from"./room/managed-room-binding.js";import{RoomHostSessionObservationStore as ho}from"./room/host-session-observations.js";import{RoomMirrorSyncService as vo}from"./room/mirror-sync-service.js";import{RoomActivationService as Io}from"./room/activation-service.js";import{RoomActivationAudit as So}from"./room/activation-audit.js";import{RoomAttachmentCache as yo}from"./room/attachment-cache.js";import{RoomAttachmentUploader as wo}from"./room/attachment-upload.js";import{ManagedRoomContextAuthority as Co}from"./room/managed-context.js";import{createManagedRoomExecutor as Ro}from"./room/managed-room-ipc.js";import{SESSION_SYNC_PROTOCOL_VERSION as ce,SESSION_SYNC_ADVERTISED_CAPABILITIES as le}from"@shennian/wire";import{getDaemonInstallationId as ko}from"./session/store.js";import{synchronizeMachinePersonalAuthority as bo}from"./personal-sync/machine-authority.js";import{CLI_SESSION_SYNC_DATA_EPOCH as de,ensurePersonalDataEpoch as Eo}from"./personal-sync/epoch.js";import{DeletionSyncRetryWaitError as Ao,DeletionSyncTerminalError as Uo}from"./session/deletion-sync-state.js";const me=we(),Do=Q("daemon.pid");function Po(){try{const t=B.readFileSync(Do,"utf-8").trim();if(t.startsWith("{")){const c=JSON.parse(t),m=Number(c.pid);return Number.isInteger(m)&&m>0?m:null}const s=parseInt(t,10);return Number.isInteger(s)&&s>0?s:null}catch{return null}}function To(t){return t.replace(/^https:\/\//,"wss://").replace(/^http:\/\//,"ws://")}function G(){process.stdout.write(`${JSON.stringify({ok:!1,error:"integration_command_retired",message:"Room MCP and Hooks were retired. Use the Shennian Skill and CLI.",nextAction:{type:"open_install_guide",url:"https://shennian.net/install.md"}})}
|
|
2
|
-
`),process.exitCode=1}function Mo(t){const s=[`trigger=${t.trigger??"unknown"}`,`phase=${t.phase}`,`code=${t.code??"unknown"}`,`attempt=${t.reconnectAttempt}`];return t.reason&&s.push(`reason=${t.reason}`),t.error&&s.push(`error=${t.error}`),s.join(" ")}async function pe(t,s=5e3){const c=Date.now();for(;Date.now()-c<s;){try{process.kill(t,0)}catch{return!0}await new Promise(m=>setTimeout(m,100))}try{return process.kill(t,0),!1}catch{return!0}}const i=new ve;i.name("shennian").description("Shennian \u2014 AI Agent Control Plane").version(F),i.enablePositionalOptions(),He(i),Fe(i),i.option("--api <url>","Server URL override").option("--name <name>","Machine name",Ie.hostname()).action(async t=>{const s=p(),c=t.api??process.env.SHENNIAN_DESKTOP_SERVER_URL??s.serverUrl??void 0;await Ae(c,t.name)}),i.command("run-service",{hidden:!0}).description("(internal) Connect to relay server, called by the background service").option("--api <url>","Server URL override").action(async t=>{const s=Q("env.json");try{const e=JSON.parse(B.readFileSync(s,"utf-8"));for(const[n,o]of Object.entries(e))process.env[n]||(process.env[n]=o);se()}catch{}ke(X(process.env,p()));const c=io();te()&&(console.log(`[${new Date().toISOString()}] remote access disabled, service start skipped`),clearInterval(c),process.exit(0));const m=!!(process.env.INVOCATION_ID||process.env.JOURNAL_STREAM||process.env.SHENNIAN_DESKTOP_SERVER_URL),z=async(e,n)=>{console.log(`[${new Date().toISOString()}] ${n} (PID ${e})`),process.kill(e,"SIGTERM"),await pe(e)||(process.kill(e,"SIGKILL"),await pe(e,2e3))};try{const e=Po();if(e&&e!==process.pid)try{process.kill(e,0),m?await z(e,"managed start taking over from existing daemon"):(console.log(`[${new Date().toISOString()}] daemon already running (PID ${e}), skipping duplicate start`),process.exit(0))}catch{}}catch{}const _=_e(process.env,me)?[]:Pe(process.pid);if(_.length>0)if(m)for(const e of _)try{await z(e,"managed start taking over from orphan daemon")}catch{}else console.log(`[${new Date().toISOString()}] daemon already running (PID ${_[0]}), skipping duplicate start`),process.exit(0);const g=De();ne(process.pid,g,{version:F}),re(process.pid,void 0,g),process.on("exit",()=>{ee(process.pid,g),oe(g)});let $=null;try{$=Ze()}catch(e){console.error(`[upgrade] updater bootstrap unavailable: ${e instanceof Error?e.message:String(e)}`)}const N=await qe();if(N){$||(console.error("[upgrade] Crash-loop rollback blocked: updater bootstrap unavailable"),process.exit(1));try{Qe({fromVersion:F,targetVersion:N.from,oldDaemonPid:process.pid,operation:"rollback"},{readiness:$}),console.error(`[upgrade] New version crashed ${N.attemptCount} times; external rollback scheduled.`),process.stdin.resume();return}catch(e){console.error(`[upgrade] Crash-loop rollback could not start: ${e instanceof Error?e.message:String(e)}`),process.exit(1)}}try{Eo()}catch(e){console.error(`[personal-sync] data epoch initialization failed: ${e instanceof Error?e.message:String(e)}`),process.exitCode=1;return}const l=p(),h=t.api??process.env.SHENNIAN_DESKTOP_SERVER_URL??l.serverUrl??H.cn.url,u=ae(),E=await eo({serverUrl:h,authToken:l.machineToken??l.accessToken}),y=E.map(e=>e.type),O=oo(E),v=new go,A=new ho,w=new Co(v),I=new uo(h),ue=new yo({actionClient:I}),fe=new wo({actionClient:I});let S=null,a;const K=Ro({authority:w,actionClient:I,attachmentCache:ue,attachmentUploader:fe,observations:A,getMachineCredentials:()=>({machineToken:p().machineToken}),onPaused:({registryKey:e,sessionId:n})=>{const r=v.getByRegistryKey(e)?.binding;r&&v.setBinding({registryKey:e,bindingId:r.bindingId,roomId:r.roomId,agentIdentityId:r.agentIdentityId,agentMembershipId:r.agentMembershipId,status:"paused",activationMode:"paused"}),w.revokeSession(e),a?.discardPendingManagedRoomMessages(n)}});let x=null,V=null,L=null;const C=await ie({paths:co(),handlers:mo({daemonVersion:u,protocolVersion:po,getStatus:()=>$e(),getAgents:()=>E,completeAgentAuthentication:e=>a.resumePendingAcpAuthentication(e),startUpgrade:async e=>{if(!x)throw new Error("upgrade_unavailable");return x(e)},prepareExternalUpgrade:e=>{if(!V)throw new Error("external_upgrade_unavailable");return V(e)},cancelExternalUpgrade:()=>{if(!L)throw new Error("external_upgrade_unavailable");return L()}})});let R;try{R=await ie({paths:lo(),handlers:{"managed-room.execute":e=>K(e)}})}catch(e){throw await C.close(),e}let J=!1;const U=()=>{J||(J=!0,Promise.allSettled([C.close(),R.close()]).finally(()=>process.exit(0)))};if(process.once("SIGINT",U),process.once("SIGTERM",U),!l.machineToken){console.error('Shennian CLI is not paired. Run "shennian pair --browser" or "shennian pair --token" first.'),await Promise.allSettled([C.close(),R.close()]),process.exitCode=1;return}process.removeListener("SIGINT",U),process.removeListener("SIGTERM",U);const q=`${To(h)}/relay/machine`,j=l.machineToken;if(!j)return;try{await bo({serverUrl:h,machineToken:j,machineId:l.machineId})}catch(e){console.error(`[personal-sync] machine authority verification failed: ${e instanceof Error?e.message:String(e)}`),await Promise.allSettled([C.close(),R.close()]),process.exitCode=1;return}l.machineId&&no(h,l.machineId),console.log(`[${new Date().toISOString()}] Connecting to ${q}... (v${u}) agents: ${y.join(",")}`),b({level:"info",wsEvent:"daemon.start",metadata:{version:u,agents:y}});let D=null,P=null,T=0,f=We();const W=Ce(l.autoUpgrade),d=new Re({serverUrl:q,machineToken:j,cliVersion:u,agentList:y,autoUpgradePolicy:W,sessionSyncProtocolVersion:ce,sessionSyncCapabilities:le,daemonInstallationId:ko(),dataEpoch:de,updaterProtocolVersion:1,onConnected:()=>{console.log(`[${new Date().toISOString()}] \u2713 Connected`),b({level:"info",wsEvent:"daemon.connected"}),O.some(o=>o.models.length>0)&&d.sendEvent({type:"event",event:"machine.agents",payload:{agentList:y,agents:O}}),to(E,{serverUrl:l.serverUrl??h,authToken:l.machineToken??l.accessToken}).then(o=>{JSON.stringify(o)!==JSON.stringify(O)&&d.sendEvent({type:"event",event:"machine.agents",payload:{agentList:y,agents:o}})}).catch(()=>{}),f&&d.sendEvent({type:"event",event:"upgrade",payload:{state:"recovering",machineId:"self",from:f.from,to:f.to,...f.jobId?{jobId:f.jobId}:{},activeRunCount:a.getPendingRecoveryCount()}}),M.start(),P?.start();const e=++T,n=async()=>{try{if(await a.synchronizeSessionDeletions(),await a.recoverInterruptedRuns(),f){const o=f;f=null,o.jobId&&Xe(o.jobId,o.to),Je(),console.log(`[${new Date().toISOString()}] [upgrade] ${o.operation==="rollback"?"Rolled back":"Healthy"}: ${o.from} \u2192 ${o.to}`),ro({kind:"upgrade",result:o.operation==="rollback"?"rolled_back":"healthy",stage:"terminal"}),d.sendEvent({type:"event",event:"upgrade",payload:{state:o.operation==="rollback"?"rolled_back":"healthy",machineId:"self",from:o.from,to:o.to,...o.jobId?{jobId:o.jobId}:{}}}),o.operation!=="rollback"&&d.sendEvent({type:"event",event:"upgrade",payload:{state:"success",machineId:"self",from:o.from,to:o.to}})}e===T&&d.getState()==="connected"&&D?.handleConnected()}catch(o){if(console.error("[session.deletions] startup catch-up failed",o),e===T&&d.getState()==="connected"){if(o instanceof Uo)return;const r=o instanceof Ao?Math.max(1e3,o.retryAfterMs):5e3;setTimeout(()=>{n()},r).unref?.()}}};n()},onDisconnected:e=>{T+=1,P?.stop(),console.log(`[${new Date().toISOString()}] \u26A0 Disconnected, reconnecting... ${Mo(e)}`),b({level:"warn",wsEvent:"daemon.disconnected",metadata:{code:e.code,reason:e.reason,error:e.error,phase:e.phase,trigger:e.trigger,reconnectAttempt:e.reconnectAttempt}})},onReq:e=>{console.log(`[${new Date().toISOString()}] [req] ${e.method}`),b({level:"info",type:"ws",wsEvent:e.method,wsDirection:"in",traceId:e.traceId}),a.handleReq(e)}});D=ao()?new so(d):null,P=new vo({registry:v,observations:A,actionClient:I,getMachineCredentials:()=>({machineToken:p().machineToken}),onMessages:async(e,n,o,r)=>{await S?.processMessages(e,n,o,r)},onBindingPaused:e=>{w.revokeSession(e.registryKey),a?.discardPendingManagedRoomMessages(e.nianSessionId)}});const M=new Ye({currentVersion:u,machineId:l.machineId??"unknown-machine",serverUrl:l.serverUrl??h,getIdleState:()=>a.getUpgradeIdleState(),sessionSyncProtocolVersion:ce,dataEpoch:de,advertisedCapabilities:le,updaterProtocolVersion:1,startUpgrade:async e=>{const{handleUpgradeStart:n}=await import("./commands/upgrade.js");await n(d,"auto-upgrade",e,{currentVersion:u,prepareForUpgrade:o=>a.prepareForUpgrade(o),cancelUpgradePreparation:()=>a.cancelUpgradePreparation(),shutdownAfterHandoff:()=>{setTimeout(()=>process.kill(process.pid,"SIGTERM"),100).unref?.()},suppressResponse:!0})}});M.setPolicy(W),a=new Ge(d,D,u,M,new fo({registry:v,observations:A,bindingClient:I,getMachineCredentials:()=>{const e=p();return{machineId:e.machineId,machineToken:e.machineToken}},hostProcessIdentity:()=>`daemon:${g}`}),{prepare:()=>Te(),isDisabled:()=>te(),shutdownAfterResponse:()=>{setTimeout(()=>{Me(),process.kill(process.pid,"SIGTERM")},100).unref?.()}},e=>w.revokeToken(e),K),x=async e=>{const n=e&&typeof e=="object"&&!Array.isArray(e)?e:{},o=typeof n.targetVersion=="string"?n.targetVersion:void 0,r=n.confirmedMajor===!0;return await Le(d,`local-upgrade-${Date.now()}`,o,{currentVersion:u,confirmedMajor:r,prepareForUpgrade:he=>a.prepareForUpgrade(he),cancelUpgradePreparation:()=>a.cancelUpgradePreparation(),shutdownAfterHandoff:()=>{setTimeout(()=>process.kill(process.pid,"SIGTERM"),100).unref?.()},suppressResponse:!0})},V=e=>{const n=e&&typeof e=="object"&&!Array.isArray(e)?e:{},r=`desktop-${(typeof n.targetVersion=="string"?n.targetVersion:"unknown").replace(/[^A-Za-z0-9._-]/g,"_")}-${Date.now()}`;return{jobId:r,...a.prepareForUpgrade(r)}},L=()=>(a.cancelUpgradePreparation(),{cancelled:!0}),S=new Io({registry:v,observations:A,enqueueConversationBatch:e=>a.enqueueManagedRoomBatch(e),loadContext:async e=>{const n=p().machineToken?.trim(),o=e.binding;if(!n||!o)throw new Error("managed_room_context_unavailable");const r=await I.status({machineToken:n,roomId:o.roomId,bindingId:o.bindingId});return{roomName:r.room.name,memberCount:r.room.memberCount,members:r.room.members,agentsByIdentityId:r.agent?{[r.agentIdentityId]:r.agent}:{},agentSystemPromptsByIdentityId:r.agent?{[r.agentIdentityId]:r.agent.systemPrompt??""}:{}}},issueManagedRoomContext:(e,n)=>{const o=p().machineToken;if(!o)throw new Error("machine_not_authorized");return w.issue({registryKey:e,machineCredential:o,activationCauseMessageId:n})},audit:new So}),a.setManagedRoomTurnPreparer(e=>S.prepareConversationTurn(e)),a.setManagedAgentDirectTurnPreparer(e=>S.prepareDirectConversationTurn(e)),a.setManagedRecoveryPreparer(async e=>e.source==="managed_direct"?S.prepareDirectConversationTurn(e.sessionId):e.source!=="managed_room"||!e.roomContextRegistryKey||!e.activationCauseMessageId||!e.rootCauseMessageId||!e.activationSequence||e.activationHopCount===null?null:S.prepareConversationTurn([{registryKey:e.roomContextRegistryKey,causeMessageId:e.activationCauseMessageId,rootCauseMessageId:e.rootCauseMessageId,sequence:e.activationSequence,hopCount:e.activationHopCount}])),B.mkdirSync(me,{recursive:!0}),ne(process.pid,g,{version:u}),re(process.pid,void 0,g),d.connect(),process.stdin.resume();let Y=!1;const Z=async()=>{if(!Y){Y=!0,console.log(k.gray(`
|
|
2
|
+
`),process.exitCode=1}function Mo(t){const s=[`trigger=${t.trigger??"unknown"}`,`phase=${t.phase}`,`code=${t.code??"unknown"}`,`attempt=${t.reconnectAttempt}`];return t.reason&&s.push(`reason=${t.reason}`),t.error&&s.push(`error=${t.error}`),s.join(" ")}async function pe(t,s=5e3){const c=Date.now();for(;Date.now()-c<s;){try{process.kill(t,0)}catch{return!0}await new Promise(m=>setTimeout(m,100))}try{return process.kill(t,0),!1}catch{return!0}}const i=new ve;i.name("shennian").description("Shennian \u2014 AI Agent Control Plane").version(F),i.enablePositionalOptions(),He(i),Fe(i),i.option("--api <url>","Server URL override").option("--name <name>","Machine name",Ie.hostname()).action(async t=>{const s=p(),c=t.api??process.env.SHENNIAN_DESKTOP_SERVER_URL??s.serverUrl??void 0;await Ae(c,t.name)}),i.command("run-service",{hidden:!0}).description("(internal) Connect to relay server, called by the background service").option("--api <url>","Server URL override").action(async t=>{const s=Q("env.json");try{const e=JSON.parse(B.readFileSync(s,"utf-8"));for(const[n,o]of Object.entries(e))process.env[n]||(process.env[n]=o);se()}catch{}ke(X(process.env,p()));const c=io();te()&&(console.log(`[${new Date().toISOString()}] remote access disabled, service start skipped`),clearInterval(c),process.exit(0));const m=!!(process.env.INVOCATION_ID||process.env.JOURNAL_STREAM||process.env.SHENNIAN_DESKTOP_SERVER_URL),z=async(e,n)=>{console.log(`[${new Date().toISOString()}] ${n} (PID ${e})`),process.kill(e,"SIGTERM"),await pe(e)||(process.kill(e,"SIGKILL"),await pe(e,2e3))};try{const e=Po();if(e&&e!==process.pid)try{process.kill(e,0),m?await z(e,"managed start taking over from existing daemon"):(console.log(`[${new Date().toISOString()}] daemon already running (PID ${e}), skipping duplicate start`),process.exit(0))}catch{}}catch{}const _=_e(process.env,me)?[]:Pe(process.pid);if(_.length>0)if(m)for(const e of _)try{await z(e,"managed start taking over from orphan daemon")}catch{}else console.log(`[${new Date().toISOString()}] daemon already running (PID ${_[0]}), skipping duplicate start`),process.exit(0);const g=De();ne(process.pid,g,{version:F}),re(process.pid,void 0,g),process.on("exit",()=>{ee(process.pid,g),oe(g)});let $=null;try{$=Ze()}catch(e){console.error(`[upgrade] updater bootstrap unavailable: ${e instanceof Error?e.message:String(e)}`)}const N=await qe();if(N){$||(console.error("[upgrade] Crash-loop rollback blocked: updater bootstrap unavailable"),process.exit(1));try{Qe({fromVersion:F,targetVersion:N.from,oldDaemonPid:process.pid,operation:"rollback"},{readiness:$}),console.error(`[upgrade] New version crashed ${N.attemptCount} times; external rollback scheduled.`),process.stdin.resume();return}catch(e){console.error(`[upgrade] Crash-loop rollback could not start: ${e instanceof Error?e.message:String(e)}`),process.exit(1)}}try{Eo()}catch(e){console.error(`[personal-sync] data epoch initialization failed: ${e instanceof Error?e.message:String(e)}`),process.exitCode=1;return}const l=p(),h=t.api??process.env.SHENNIAN_DESKTOP_SERVER_URL??l.serverUrl??H.cn.url,u=ae(),E=await eo({serverUrl:h,authToken:l.machineToken??l.accessToken}),y=E.map(e=>e.type),O=oo(E),v=new go,A=new ho,w=new Co(v),I=new uo(h),ue=new yo({actionClient:I}),fe=new wo({actionClient:I});let S=null,a;const K=Ro({authority:w,actionClient:I,attachmentCache:ue,attachmentUploader:fe,observations:A,getMachineCredentials:()=>({machineToken:p().machineToken}),onPaused:({registryKey:e,sessionId:n})=>{const r=v.getByRegistryKey(e)?.binding;r&&v.setBinding({registryKey:e,bindingId:r.bindingId,roomId:r.roomId,agentIdentityId:r.agentIdentityId,agentMembershipId:r.agentMembershipId,status:"paused",activationMode:"paused"}),w.revokeSession(e),a?.discardPendingManagedRoomMessages(n)}});let x=null,V=null,L=null;const C=await ie({paths:co(),handlers:mo({daemonVersion:u,protocolVersion:po,getStatus:()=>$e(),getAgents:()=>E,completeAgentAuthentication:e=>a.resumePendingAcpAuthentication(e),startUpgrade:async e=>{if(!x)throw new Error("upgrade_unavailable");return x(e)},prepareExternalUpgrade:e=>{if(!V)throw new Error("external_upgrade_unavailable");return V(e)},cancelExternalUpgrade:()=>{if(!L)throw new Error("external_upgrade_unavailable");return L()}})});let R;try{R=await ie({paths:lo(),handlers:{"managed-room.execute":e=>K(e)}})}catch(e){throw await C.close(),e}let J=!1;const U=()=>{J||(J=!0,Promise.allSettled([C.close(),R.close()]).finally(()=>process.exit(0)))};if(process.once("SIGINT",U),process.once("SIGTERM",U),!l.machineToken){console.error('Shennian CLI is not paired. Run "shennian pair --browser" or "shennian pair --token" first.'),await Promise.allSettled([C.close(),R.close()]),process.exitCode=1;return}process.removeListener("SIGINT",U),process.removeListener("SIGTERM",U);const q=`${To(h)}/relay/machine`,j=l.machineToken;if(!j)return;try{await bo({serverUrl:h,machineToken:j,machineId:l.machineId})}catch(e){console.error(`[personal-sync] machine authority verification failed: ${e instanceof Error?e.message:String(e)}`),await Promise.allSettled([C.close(),R.close()]),process.exit(1)}l.machineId&&no(h,l.machineId),console.log(`[${new Date().toISOString()}] Connecting to ${q}... (v${u}) agents: ${y.join(",")}`),b({level:"info",wsEvent:"daemon.start",metadata:{version:u,agents:y}});let D=null,P=null,T=0,f=We();const W=Ce(l.autoUpgrade),d=new Re({serverUrl:q,machineToken:j,cliVersion:u,agentList:y,autoUpgradePolicy:W,sessionSyncProtocolVersion:ce,sessionSyncCapabilities:le,daemonInstallationId:ko(),dataEpoch:de,updaterProtocolVersion:1,onConnected:()=>{console.log(`[${new Date().toISOString()}] \u2713 Connected`),b({level:"info",wsEvent:"daemon.connected"}),O.some(o=>o.models.length>0)&&d.sendEvent({type:"event",event:"machine.agents",payload:{agentList:y,agents:O}}),to(E,{serverUrl:l.serverUrl??h,authToken:l.machineToken??l.accessToken}).then(o=>{JSON.stringify(o)!==JSON.stringify(O)&&d.sendEvent({type:"event",event:"machine.agents",payload:{agentList:y,agents:o}})}).catch(()=>{}),f&&d.sendEvent({type:"event",event:"upgrade",payload:{state:"recovering",machineId:"self",from:f.from,to:f.to,...f.jobId?{jobId:f.jobId}:{},activeRunCount:a.getPendingRecoveryCount()}}),M.start(),P?.start();const e=++T,n=async()=>{try{if(await a.synchronizeSessionDeletions(),await a.recoverInterruptedRuns(),f){const o=f;f=null,o.jobId&&Xe(o.jobId,o.to),Je(),console.log(`[${new Date().toISOString()}] [upgrade] ${o.operation==="rollback"?"Rolled back":"Healthy"}: ${o.from} \u2192 ${o.to}`),ro({kind:"upgrade",result:o.operation==="rollback"?"rolled_back":"healthy",stage:"terminal"}),d.sendEvent({type:"event",event:"upgrade",payload:{state:o.operation==="rollback"?"rolled_back":"healthy",machineId:"self",from:o.from,to:o.to,...o.jobId?{jobId:o.jobId}:{}}}),o.operation!=="rollback"&&d.sendEvent({type:"event",event:"upgrade",payload:{state:"success",machineId:"self",from:o.from,to:o.to}})}e===T&&d.getState()==="connected"&&D?.handleConnected()}catch(o){if(console.error("[session.deletions] startup catch-up failed",o),e===T&&d.getState()==="connected"){if(o instanceof Uo)return;const r=o instanceof Ao?Math.max(1e3,o.retryAfterMs):5e3;setTimeout(()=>{n()},r).unref?.()}}};n()},onDisconnected:e=>{T+=1,P?.stop(),console.log(`[${new Date().toISOString()}] \u26A0 Disconnected, reconnecting... ${Mo(e)}`),b({level:"warn",wsEvent:"daemon.disconnected",metadata:{code:e.code,reason:e.reason,error:e.error,phase:e.phase,trigger:e.trigger,reconnectAttempt:e.reconnectAttempt}})},onReq:e=>{console.log(`[${new Date().toISOString()}] [req] ${e.method}`),b({level:"info",type:"ws",wsEvent:e.method,wsDirection:"in",traceId:e.traceId}),a.handleReq(e)}});D=ao()?new so(d):null,P=new vo({registry:v,observations:A,actionClient:I,getMachineCredentials:()=>({machineToken:p().machineToken}),onMessages:async(e,n,o,r)=>{await S?.processMessages(e,n,o,r)},onBindingPaused:e=>{w.revokeSession(e.registryKey),a?.discardPendingManagedRoomMessages(e.nianSessionId)}});const M=new Ye({currentVersion:u,machineId:l.machineId??"unknown-machine",serverUrl:l.serverUrl??h,getIdleState:()=>a.getUpgradeIdleState(),sessionSyncProtocolVersion:ce,dataEpoch:de,advertisedCapabilities:le,updaterProtocolVersion:1,startUpgrade:async e=>{const{handleUpgradeStart:n}=await import("./commands/upgrade.js");await n(d,"auto-upgrade",e,{currentVersion:u,prepareForUpgrade:o=>a.prepareForUpgrade(o),cancelUpgradePreparation:()=>a.cancelUpgradePreparation(),shutdownAfterHandoff:()=>{setTimeout(()=>process.kill(process.pid,"SIGTERM"),100).unref?.()},suppressResponse:!0})}});M.setPolicy(W),a=new Ge(d,D,u,M,new fo({registry:v,observations:A,bindingClient:I,getMachineCredentials:()=>{const e=p();return{machineId:e.machineId,machineToken:e.machineToken}},hostProcessIdentity:()=>`daemon:${g}`}),{prepare:()=>Te(),isDisabled:()=>te(),shutdownAfterResponse:()=>{setTimeout(()=>{Me(),process.kill(process.pid,"SIGTERM")},100).unref?.()}},e=>w.revokeToken(e),K),x=async e=>{const n=e&&typeof e=="object"&&!Array.isArray(e)?e:{},o=typeof n.targetVersion=="string"?n.targetVersion:void 0,r=n.confirmedMajor===!0;return await Le(d,`local-upgrade-${Date.now()}`,o,{currentVersion:u,confirmedMajor:r,prepareForUpgrade:he=>a.prepareForUpgrade(he),cancelUpgradePreparation:()=>a.cancelUpgradePreparation(),shutdownAfterHandoff:()=>{setTimeout(()=>process.kill(process.pid,"SIGTERM"),100).unref?.()},suppressResponse:!0})},V=e=>{const n=e&&typeof e=="object"&&!Array.isArray(e)?e:{},r=`desktop-${(typeof n.targetVersion=="string"?n.targetVersion:"unknown").replace(/[^A-Za-z0-9._-]/g,"_")}-${Date.now()}`;return{jobId:r,...a.prepareForUpgrade(r)}},L=()=>(a.cancelUpgradePreparation(),{cancelled:!0}),S=new Io({registry:v,observations:A,enqueueConversationBatch:e=>a.enqueueManagedRoomBatch(e),loadContext:async e=>{const n=p().machineToken?.trim(),o=e.binding;if(!n||!o)throw new Error("managed_room_context_unavailable");const r=await I.status({machineToken:n,roomId:o.roomId,bindingId:o.bindingId});return{roomName:r.room.name,memberCount:r.room.memberCount,members:r.room.members,agentsByIdentityId:r.agent?{[r.agentIdentityId]:r.agent}:{},agentSystemPromptsByIdentityId:r.agent?{[r.agentIdentityId]:r.agent.systemPrompt??""}:{}}},issueManagedRoomContext:(e,n)=>{const o=p().machineToken;if(!o)throw new Error("machine_not_authorized");return w.issue({registryKey:e,machineCredential:o,activationCauseMessageId:n})},audit:new So}),a.setManagedRoomTurnPreparer(e=>S.prepareConversationTurn(e)),a.setManagedAgentDirectTurnPreparer(e=>S.prepareDirectConversationTurn(e)),a.setManagedRecoveryPreparer(async e=>e.source==="managed_direct"?S.prepareDirectConversationTurn(e.sessionId):e.source!=="managed_room"||!e.roomContextRegistryKey||!e.activationCauseMessageId||!e.rootCauseMessageId||!e.activationSequence||e.activationHopCount===null?null:S.prepareConversationTurn([{registryKey:e.roomContextRegistryKey,causeMessageId:e.activationCauseMessageId,rootCauseMessageId:e.rootCauseMessageId,sequence:e.activationSequence,hopCount:e.activationHopCount}])),B.mkdirSync(me,{recursive:!0}),ne(process.pid,g,{version:u}),re(process.pid,void 0,g),d.connect(),process.stdin.resume();let Y=!1;const Z=async()=>{if(!Y){Y=!0,console.log(k.gray(`
|
|
3
3
|
Disconnecting...`)),b({level:"info",wsEvent:"daemon.stop"}),clearInterval(c);try{await a.cleanup(),M.stop(),D?.stop(),P?.stop(),d.disconnect(),await Promise.allSettled([C.close(),R.close()]),ee(process.pid,g),oe(g)}finally{process.exit(0)}}};process.on("SIGINT",()=>{Z()}),process.on("SIGTERM",()=>{Z()})}),i.command("workbuddy-session-hook",{hidden:!0}).description("(retired) WorkBuddy Room Hook").allowUnknownOption(!0).action(()=>G()),i.command("room-session-hook",{hidden:!0}).description("(internal) Authorize the official Room tool for one trusted Agent Session").argument("<runtime>","Trusted runtime adapter").allowUnknownOption(!0).action(()=>G()),i.command("room-tool-server",{hidden:!0}).description("(internal) Run the official Shennian Room MCP tool").allowUnknownOption(!0).action(()=>G());const ge=i.command("config").description("View or modify configuration");ge.command("show",{isDefault:!0}).description("Show current configuration").action(()=>{console.log(`Config file: ${ye()}`);const t=p(),s=be(t,X(process.env,t));if(t.serverUrl){const c=Ke(t.serverUrl);console.log(`Region: ${c} (${H[c].label})`)}console.log(JSON.stringify(s,null,2))}),ge.command("set").description("Update a config value").argument("<key>",'Config key (e.g. "server")').argument("<value>",'Config value (e.g. "cn" or "global")').action((t,s)=>{const c=p();if(t==="server"){s!=="cn"&&s!=="global"&&(console.error(k.red('\u2717 Value must be "cn" or "global"')),process.exit(1));const m=s;c.serverUrl=ze(m),Se(c),console.log(k.green(`\u2713 Server set to ${H[m].label}`)),console.log(k.yellow(" Restart the background service for this to take effect: shennian stop && shennian start"));return}console.error(k.red(`\u2717 Unknown config key: ${t}. Supported: server`)),process.exit(1)}),Ee(i),Ue(i),Ne(i),Oe(i),xe(i),Ve(i),je(i),Be(i),i.parse();
|