shennian 0.4.17 → 0.4.19

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.
@@ -101,8 +101,8 @@
101
101
  },
102
102
  {
103
103
  "file": "src/agents/claude.js",
104
- "beforeBytes": 17063,
105
- "afterBytes": 8544
104
+ "beforeBytes": 17218,
105
+ "afterBytes": 8552
106
106
  },
107
107
  {
108
108
  "file": "src/agents/codex-managed-permissions.js",
@@ -141,8 +141,8 @@
141
141
  },
142
142
  {
143
143
  "file": "src/agents/cursor.js",
144
- "beforeBytes": 12192,
145
- "afterBytes": 5890
144
+ "beforeBytes": 12436,
145
+ "afterBytes": 5960
146
146
  },
147
147
  {
148
148
  "file": "src/agents/custom-managed-runtime.js",
@@ -306,8 +306,8 @@
306
306
  },
307
307
  {
308
308
  "file": "src/commands/daemon.js",
309
- "beforeBytes": 44966,
310
- "afterBytes": 21766
309
+ "beforeBytes": 45184,
310
+ "afterBytes": 21864
311
311
  },
312
312
  {
313
313
  "file": "src/commands/integration.js",
@@ -371,8 +371,8 @@
371
371
  },
372
372
  {
373
373
  "file": "src/daemon-ipc/client.js",
374
- "beforeBytes": 2754,
375
- "afterBytes": 1393
374
+ "beforeBytes": 2786,
375
+ "afterBytes": 1428
376
376
  },
377
377
  {
378
378
  "file": "src/daemon-ipc/control.js",
@@ -391,18 +391,18 @@
391
391
  },
392
392
  {
393
393
  "file": "src/daemon-ipc/protocol.js",
394
- "beforeBytes": 426,
395
- "afterBytes": 200
394
+ "beforeBytes": 484,
395
+ "afterBytes": 245
396
396
  },
397
397
  {
398
398
  "file": "src/daemon-ipc/secret.js",
399
- "beforeBytes": 3263,
400
- "afterBytes": 1732
399
+ "beforeBytes": 3455,
400
+ "afterBytes": 1873
401
401
  },
402
402
  {
403
403
  "file": "src/daemon-ipc/server.js",
404
- "beforeBytes": 6704,
405
- "afterBytes": 3447
404
+ "beforeBytes": 7345,
405
+ "afterBytes": 3824
406
406
  },
407
407
  {
408
408
  "file": "src/daemon-log.js",
@@ -441,8 +441,8 @@
441
441
  },
442
442
  {
443
443
  "file": "src/log-redaction.js",
444
- "beforeBytes": 5228,
445
- "afterBytes": 2828
444
+ "beforeBytes": 5266,
445
+ "afterBytes": 2866
446
446
  },
447
447
  {
448
448
  "file": "src/log-reporter.js",
@@ -886,8 +886,8 @@
886
886
  },
887
887
  {
888
888
  "file": "src/upgrade/updater-worker.js",
889
- "beforeBytes": 13998,
890
- "afterBytes": 7478
889
+ "beforeBytes": 14538,
890
+ "afterBytes": 7608
891
891
  },
892
892
  {
893
893
  "file": "src/windows-shell.js",
@@ -1,8 +1,8 @@
1
- import c from"node:fs";import o from"node:path";import{createInterface as y}from"node:readline";import{randomUUID as p}from"node:crypto";import{AgentAdapter as S,registerAgent as E}from"./adapter.js";import{resolveBuiltinCommand as m,spawnAgentCommand as x,spawnResolvedCommandSync as P}from"./command-spec.js";import{buildAgentProcessEnv as f,buildManagedAgentProcessEnv as v}from"../agent-env.js";import{buildPlatformInstructions as g}from"./platform-instructions.js";import{resolveManagedAgentAdapterEnforcement as I}from"../security/managed-agent-policy.js";import{registerManagedAgentRuntimeSecurity as C}from"../security/managed-agent-runtime.js";import{resolveManagedRoomIpcPaths as k}from"../daemon-ipc/paths.js";import{CLAUDE_MANAGED_RUNTIME_SECURITY as w,compileClaudeManagedPermissions as A}from"./claude-managed-permissions.js";C("claude",w);function _(n){return n?.trim()||"default"}const R=new Set(["low","medium","high","xhigh","max"]),h=new Map;function T(n){const e=n?.trim();if(e){if(R.has(e))return e;throw new Error(`Unsupported Claude reasoning effort "${e}". Supported values: low, medium, high, xhigh, max.`)}}function Q(){h.clear()}function M(n){const e=n.trim();return e?!e.includes("claude.ai connectors are disabled because"):!1}function O(){const n=m("claude");if(!n)return!1;const e=`${n.kind}:${n.path}:${n.command}:${n.args.join("\0")}`;if(h.has(e))return h.get(e)??!1;try{const t=P(n,["--help"],{encoding:"utf-8",stdio:["ignore","pipe","pipe"],timeout:3e3,env:f()}),s=`${t.stdout??""}
2
- ${t.stderr??""}`,a=/(?:^|\s)--effort(?:\s|,|<)/.test(s);return h.set(e,a),a}catch{return h.set(e,!1),!1}}class N extends S{options;type="claude";get recoveryCapabilities(){return{inspectTerminal:!1,reattachRun:!1,resumeRun:!1,resumeSession:!0}}process=null;agentSessionId=null;sessionId=null;workDir=null;seq=0;runId="";hasEmittedText=!1;terminalState="open";persistNativeSession=!0;managedAgentPolicy=null;constructor(e={}){super(),this.options=e}externalChannel=null;shennianSessionId=null;extraEnv={};configure(e){this.shennianSessionId=e.sessionId??null,this.externalChannel=e.externalChannel??null,this.extraEnv=e.env??{},"managedAgentPolicy"in e&&(this.managedAgentPolicy=e.managedAgentPolicy??null),this.managedAgentPolicy&&(this.workDir=this.managedAgentPolicy.adapterPolicy.workDir)}async start(e,t,s,a){this.sessionId=e,this.workDir=this.managedAgentPolicy?.adapterPolicy.workDir??t,this.seq=0,this.persistNativeSession=a?.persistNativeSession!==!1,s&&(this.agentSessionId=s)}async send(e,t,s,a,l,r){if(r?.source==="room_activation"&&!this.managedAgentPolicy)throw new Error("managed_agent_policy_missing");await this.killProcess(),this.runId=p(),this.resetRunState();const i=["-p",e,"--output-format","stream-json","--verbose",...this.buildManagedPermissionConfig()?.args??[]];this.persistNativeSession||i.push("--no-session-persistence");const u=this.options.systemPrompt??g(this.workDir??process.cwd(),this.externalChannel,this.shennianSessionId??void 0,l);u&&i.push("--append-system-prompt",u),this.options.hidden&&i.push("--name","Shennian Manager Substrate"),!this.managedAgentPolicy&&process.getuid?.()!==0&&i.push("--dangerously-skip-permissions"),i.push("--model",_(t));const d=T(s);d&&O()&&i.push("--effort",d),this.agentSessionId&&i.push("--resume",this.agentSessionId),this.spawnAndParse(i)}async resume(e){await this.killProcess(),this.agentSessionId=e,this.runId=p(),this.resetRunState();const t=["--resume",e,"--output-format","stream-json","--verbose",...this.buildManagedPermissionConfig()?.args??[]],s=this.options.systemPrompt??g(this.workDir??process.cwd(),this.externalChannel,this.shennianSessionId??void 0);s&&t.push("--append-system-prompt",s),this.options.hidden&&t.push("--name","Shennian Manager Substrate"),!this.managedAgentPolicy&&process.getuid?.()!==0&&t.push("--dangerously-skip-permissions"),this.spawnAndParse(t)}async stop(){await this.killProcess()}spawnAndParse(e){const t=m("claude");if(!t){this.emit("error",new Error('Command "claude" not found. Is Claude Code CLI installed?'));return}const s=x(t,e,{cwd:this.workDir??void 0,stdio:["ignore","pipe","pipe"],env:this.buildProcessEnv()});this.process=s,y({input:s.stdout}).on("line",r=>{if(r.trim())try{const i=JSON.parse(r);this.handleStreamEvent(i)}catch{}});let l="";s.stderr?.on("data",r=>{const i=r.toString();if(l+=i,this.terminalState==="open")for(const u of i.split(`
1
+ import m from"node:fs";import o from"node:path";import{createInterface as g}from"node:readline";import{randomUUID as p}from"node:crypto";import{AgentAdapter as y,registerAgent as S}from"./adapter.js";import{resolveBuiltinCommand as c,spawnAgentCommand as E,spawnResolvedCommandSync as x}from"./command-spec.js";import{buildAgentProcessEnv as v,buildManagedAgentProcessEnv as P}from"../agent-env.js";import{buildPlatformInstructions as f}from"./platform-instructions.js";import{resolveManagedAgentAdapterEnforcement as I}from"../security/managed-agent-policy.js";import{registerManagedAgentRuntimeSecurity as C}from"../security/managed-agent-runtime.js";import{resolveManagedRoomIpcPaths as k}from"../daemon-ipc/paths.js";import{CLAUDE_MANAGED_RUNTIME_SECURITY as w,compileClaudeManagedPermissions as A}from"./claude-managed-permissions.js";C("claude",w);function _(n){return n?.trim()||"default"}const R=new Set(["low","medium","high","xhigh","max"]),h=new Map;function T(n){const e=n?.trim();if(e){if(R.has(e))return e;throw new Error(`Unsupported Claude reasoning effort "${e}". Supported values: low, medium, high, xhigh, max.`)}}function Q(){h.clear()}function M(n){const e=n.trim();return e?!e.includes("claude.ai connectors are disabled because"):!1}function O(){const n=c("claude");if(!n)return!1;const e=`${n.kind}:${n.path}:${n.command}:${n.args.join("\0")}`;if(h.has(e))return h.get(e)??!1;try{const t=x(n,["--help"],{encoding:"utf-8",stdio:["ignore","pipe","pipe"],timeout:3e3,env:process.env}),s=`${t.stdout??""}
2
+ ${t.stderr??""}`,a=/(?:^|\s)--effort(?:\s|,|<)/.test(s);return h.set(e,a),a}catch{return h.set(e,!1),!1}}class N extends y{options;type="claude";get recoveryCapabilities(){return{inspectTerminal:!1,reattachRun:!1,resumeRun:!1,resumeSession:!0}}process=null;agentSessionId=null;sessionId=null;workDir=null;seq=0;runId="";hasEmittedText=!1;terminalState="open";persistNativeSession=!0;managedAgentPolicy=null;constructor(e={}){super(),this.options=e}externalChannel=null;shennianSessionId=null;extraEnv={};configure(e){this.shennianSessionId=e.sessionId??null,this.externalChannel=e.externalChannel??null,this.extraEnv=e.env??{},"managedAgentPolicy"in e&&(this.managedAgentPolicy=e.managedAgentPolicy??null),this.managedAgentPolicy&&(this.workDir=this.managedAgentPolicy.adapterPolicy.workDir)}async start(e,t,s,a){this.sessionId=e,this.workDir=this.managedAgentPolicy?.adapterPolicy.workDir??t,this.seq=0,this.persistNativeSession=a?.persistNativeSession!==!1,s&&(this.agentSessionId=s)}async send(e,t,s,a,l,r){if(r?.source==="room_activation"&&!this.managedAgentPolicy)throw new Error("managed_agent_policy_missing");await this.killProcess(),this.runId=p(),this.resetRunState();const i=["-p",e,"--output-format","stream-json","--verbose",...this.buildManagedPermissionConfig()?.args??[]];this.persistNativeSession||i.push("--no-session-persistence");const u=this.options.systemPrompt??f(this.workDir??process.cwd(),this.externalChannel,this.shennianSessionId??void 0,l);u&&i.push("--append-system-prompt",u),this.options.hidden&&i.push("--name","Shennian Manager Substrate"),!this.managedAgentPolicy&&process.getuid?.()!==0&&i.push("--dangerously-skip-permissions"),i.push("--model",_(t));const d=T(s);d&&O()&&i.push("--effort",d),this.agentSessionId&&i.push("--resume",this.agentSessionId),this.spawnAndParse(i)}async resume(e){await this.killProcess(),this.agentSessionId=e,this.runId=p(),this.resetRunState();const t=["--resume",e,"--output-format","stream-json","--verbose",...this.buildManagedPermissionConfig()?.args??[]],s=this.options.systemPrompt??f(this.workDir??process.cwd(),this.externalChannel,this.shennianSessionId??void 0);s&&t.push("--append-system-prompt",s),this.options.hidden&&t.push("--name","Shennian Manager Substrate"),!this.managedAgentPolicy&&process.getuid?.()!==0&&t.push("--dangerously-skip-permissions"),this.spawnAndParse(t)}async stop(){await this.killProcess()}spawnAndParse(e){const t=c("claude");if(!t){this.emit("error",new Error('Command "claude" not found. Is Claude Code CLI installed?'));return}const s=E(t,e,{cwd:this.workDir??void 0,stdio:["ignore","pipe","pipe"],env:this.buildProcessEnv()});this.process=s,g({input:s.stdout}).on("line",r=>{if(r.trim())try{const i=JSON.parse(r);this.handleStreamEvent(i)}catch{}});let l="";s.stderr?.on("data",r=>{const i=r.toString();if(l+=i,this.terminalState==="open")for(const u of i.split(`
3
3
  `)){const d=u.trim();M(d)&&this.emitEvent({state:"delta",text:d+`
4
- `})}}),s.on("close",r=>{this.process===s&&(this.process=null,this.handleProcessClose(r,l))}),s.on("error",r=>{this.process===s&&(this.process=null,r.code==="ENOENT"?this.emit("error",new Error('Command "claude" not found. Is Claude Code CLI installed?')):this.emit("error",r))})}buildManagedPermissionConfig(){const e=this.managedAgentPolicy;if(!e)return null;const t=D();return A(I(e),e.adapterPolicy.workDir,{internalReadPaths:t.readPaths,internalUnixSockets:t.unixSockets,shellSubprocessPolicy:e.environmentPolicy.shellSubprocess})}buildProcessEnv(){return this.managedAgentPolicy?v({agentType:"claude",providerEnv:this.extraEnv,sessionEnv:this.extraEnv,runtimeEnv:{NO_COLOR:"1"}}):f(this.extraEnv)}handleStreamEvent(e){if(e.type==="system"&&e.subtype==="init"&&e.session_id){this.agentSessionId=e.session_id,this.emitEvent({state:"start",agentSessionId:this.agentSessionId});return}if(e.type==="system"&&e.subtype==="api_retry"&&e.error==="authentication_failed"){this.emitErrorIfOpen({state:"error",message:'Claude authentication failed. Run "claude auth login" and retry.'});return}if(e.type==="assistant"){if(e.message?.content){for(const t of e.message.content)if(t.type==="text"&&t.text){const s=this.hasEmittedText?`
4
+ `})}}),s.on("close",r=>{this.process===s&&(this.process=null,this.handleProcessClose(r,l))}),s.on("error",r=>{this.process===s&&(this.process=null,r.code==="ENOENT"?this.emit("error",new Error('Command "claude" not found. Is Claude Code CLI installed?')):this.emit("error",r))})}buildManagedPermissionConfig(){const e=this.managedAgentPolicy;if(!e)return null;const t=D();return A(I(e),e.adapterPolicy.workDir,{internalReadPaths:t.readPaths,internalUnixSockets:t.unixSockets,shellSubprocessPolicy:e.environmentPolicy.shellSubprocess})}buildProcessEnv(){return this.managedAgentPolicy?P({agentType:"claude",providerEnv:this.extraEnv,sessionEnv:this.extraEnv,runtimeEnv:{NO_COLOR:"1"}}):v(this.extraEnv)}handleStreamEvent(e){if(e.type==="system"&&e.subtype==="init"&&e.session_id){this.agentSessionId=e.session_id,this.emitEvent({state:"start",agentSessionId:this.agentSessionId});return}if(e.type==="system"&&e.subtype==="api_retry"&&e.error==="authentication_failed"){this.emitErrorIfOpen({state:"error",message:'Claude authentication failed. Run "claude auth login" and retry.'});return}if(e.type==="assistant"){if(e.message?.content){for(const t of e.message.content)if(t.type==="text"&&t.text){const s=this.hasEmittedText?`
5
5
 
6
6
  `:"";this.emitEvent({state:"delta",text:s+t.text}),this.hasEmittedText=!0}else t.type==="thinking"&&t.thinking?this.emitEvent({state:"delta",text:t.thinking,thinking:!0}):t.type==="tool_use"&&this.emitEvent({state:"tool-call",name:t.name,args:t.input});return}if(e.subtype==="text"&&e.text){const t=this.hasEmittedText?`
7
7
 
8
- `:"";this.emitEvent({state:"delta",text:t+e.text}),this.hasEmittedText=!0}else e.subtype==="thinking"&&e.thinking?this.emitEvent({state:"delta",text:e.thinking,thinking:!0}):e.subtype==="tool_use"&&this.emitEvent({state:"tool-call",name:e.name,args:e.input});return}if(e.type==="result")if(e.subtype==="tool_result"){const t=typeof e.content=="string"?e.content:JSON.stringify(e.content);this.emitEvent({state:"tool-result",name:e.name,result:t})}else if(e.subtype==="success"){e.session_id&&(this.agentSessionId=e.session_id);const t=e.usage??e.message?.usage;this.emitFinalIfOpen({state:"final",agentSessionId:this.agentSessionId??void 0,usage:t?{inputTokens:t.input_tokens,outputTokens:t.output_tokens}:void 0})}else e.subtype==="error"&&this.emitErrorIfOpen({state:"error",message:e.error??e.result??"unknown error"})}handleProcessClose(e,t){if(e!==0&&e!==null){const s=t.trim()||`claude exited with code ${e}`;this.emitErrorIfOpen({state:"error",message:s});return}this.emitFinalIfOpen({state:"final",agentSessionId:this.agentSessionId??void 0})}emitFinalIfOpen(e){this.terminalState==="open"&&(this.terminalState="final",this.emitEvent(e))}emitErrorIfOpen(e){this.terminalState==="open"&&(this.terminalState="error",this.emitEvent(e))}resetRunState(){this.seq=0,this.hasEmittedText=!1,this.terminalState="open"}emitEvent(e){const t={...e,runId:this.runId,seq:this.seq++};this.emit("agentEvent",t)}async killProcess(){const e=this.process;e&&(this.process=null,e.kill("SIGTERM"),await new Promise(t=>{e.on("close",t),setTimeout(()=>{e.kill("SIGKILL"),t()},3e3)}))}}function D(){const n=k(),e=[n.secretFile],t=n.transport==="unix_socket"?[n.endpoint]:[];n.transport==="unix_socket"&&e.push(n.endpoint);const s=m("pi");return s?.path&&e.push(...F(s.path)),{readPaths:[...new Set(e)].sort(),unixSockets:[...new Set(t)].sort()}}function F(n){const e=o.normalize(n.trim());if(!e||!o.isAbsolute(e))return[];const t=[e];try{const s=c.realpathSync(e);t.push(s,o.dirname(s),o.dirname(o.dirname(s)));let a=o.dirname(s);for(let l=0;l<8;l+=1){const r=o.join(a,"package.json");try{if(JSON.parse(c.readFileSync(r,"utf8")).name==="shennian"){t.push(a);break}}catch{}const i=o.dirname(a);if(i===a)break;a=i}}catch{}return[...new Set(t)].sort()}E("claude",()=>new N);export{N as ClaudeAdapter,_ as normalizeClaudeModelId,T as normalizeClaudeReasoningEffort,Q as resetClaudeCapabilityCacheForTests,D as resolveClaudeManagedInternalAccess,F as resolveClaudeShennianCliReadPaths,M as shouldForwardClaudeStderrLine,O as supportsClaudeReasoningEffort};
8
+ `:"";this.emitEvent({state:"delta",text:t+e.text}),this.hasEmittedText=!0}else e.subtype==="thinking"&&e.thinking?this.emitEvent({state:"delta",text:e.thinking,thinking:!0}):e.subtype==="tool_use"&&this.emitEvent({state:"tool-call",name:e.name,args:e.input});return}if(e.type==="result")if(e.subtype==="tool_result"){const t=typeof e.content=="string"?e.content:JSON.stringify(e.content);this.emitEvent({state:"tool-result",name:e.name,result:t})}else if(e.subtype==="success"){e.session_id&&(this.agentSessionId=e.session_id);const t=e.usage??e.message?.usage;this.emitFinalIfOpen({state:"final",agentSessionId:this.agentSessionId??void 0,usage:t?{inputTokens:t.input_tokens,outputTokens:t.output_tokens}:void 0})}else e.subtype==="error"&&this.emitErrorIfOpen({state:"error",message:e.error??e.result??"unknown error"})}handleProcessClose(e,t){if(e!==0&&e!==null){const s=t.trim()||`claude exited with code ${e}`;this.emitErrorIfOpen({state:"error",message:s});return}this.emitFinalIfOpen({state:"final",agentSessionId:this.agentSessionId??void 0})}emitFinalIfOpen(e){this.terminalState==="open"&&(this.terminalState="final",this.emitEvent(e))}emitErrorIfOpen(e){this.terminalState==="open"&&(this.terminalState="error",this.emitEvent(e))}resetRunState(){this.seq=0,this.hasEmittedText=!1,this.terminalState="open"}emitEvent(e){const t={...e,runId:this.runId,seq:this.seq++};this.emit("agentEvent",t)}async killProcess(){const e=this.process;e&&(this.process=null,e.kill("SIGTERM"),await new Promise(t=>{e.on("close",t),setTimeout(()=>{e.kill("SIGKILL"),t()},3e3)}))}}function D(){const n=k(),e=[n.secretFile],t=n.transport==="unix_socket"?[n.endpoint]:[];n.transport==="unix_socket"&&e.push(n.endpoint);const s=c("pi");return s?.path&&e.push(...F(s.path)),{readPaths:[...new Set(e)].sort(),unixSockets:[...new Set(t)].sort()}}function F(n){const e=o.normalize(n.trim());if(!e||!o.isAbsolute(e))return[];const t=[e];try{const s=m.realpathSync(e);t.push(s,o.dirname(s),o.dirname(o.dirname(s)));let a=o.dirname(s);for(let l=0;l<8;l+=1){const r=o.join(a,"package.json");try{if(JSON.parse(m.readFileSync(r,"utf8")).name==="shennian"){t.push(a);break}}catch{}const i=o.dirname(a);if(i===a)break;a=i}}catch{}return[...new Set(t)].sort()}S("claude",()=>new N);export{N as ClaudeAdapter,_ as normalizeClaudeModelId,T as normalizeClaudeReasoningEffort,Q as resetClaudeCapabilityCacheForTests,D as resolveClaudeManagedInternalAccess,F as resolveClaudeShennianCliReadPaths,M as shouldForwardClaudeStderrLine,O as supportsClaudeReasoningEffort};
@@ -30,6 +30,7 @@ export declare class CursorAdapter extends AgentAdapter {
30
30
  send(text: string, modelId?: string, _reasoningEffort?: string, _attachments?: ChatAttachmentMeta[], agentSystemPrompt?: string | null, context?: AgentSendContext): Promise<void>;
31
31
  resume(agentSessionId: string): Promise<void>;
32
32
  stop(): Promise<void>;
33
+ private buildSendArgs;
33
34
  private spawnAndParse;
34
35
  private prepareManagedLaunch;
35
36
  private disposeManagedConfig;
@@ -1 +1 @@
1
- import{createInterface as d}from"node:readline";import{randomUUID as l}from"node:crypto";import{AgentAdapter as c,registerAgent as h}from"./adapter.js";import{resolveBuiltinCommand as u,spawnResolvedCommand as m}from"./command-spec.js";import{buildAgentProcessEnv as g,buildManagedAgentProcessEnv as f}from"../agent-env.js";import{buildInlineChannelPreamble as p}from"./platform-instructions.js";import{resolveManagedAgentAdapterEnforcement as v}from"../security/managed-agent-policy.js";import{registerManagedAgentRuntimeSecurity as E}from"../security/managed-agent-runtime.js";import{compileCursorManagedPermissions as w,createCursorManagedConfigDirectory as y,CURSOR_MANAGED_RUNTIME_SECURITY as C,disposeCursorManagedConfigDirectory as k}from"./cursor-managed-permissions.js";E("cursor",C);class x extends c{type="cursor";get recoveryCapabilities(){return{inspectTerminal:!1,reattachRun:!1,resumeRun:!1,resumeSession:!0}}process=null;chatId=null;workDir=null;seq=0;runId="";emittedText="";externalChannel=null;shennianSessionId=null;extraEnv={};managedAgentPolicy=null;managedConfigDir=null;configure(t){this.shennianSessionId=t.sessionId??null,this.externalChannel=t.externalChannel??null,this.extraEnv=t.env??{},"managedAgentPolicy"in t&&(this.managedAgentPolicy=t.managedAgentPolicy??null),this.managedAgentPolicy&&(this.workDir=this.managedAgentPolicy.adapterPolicy.workDir)}async start(t,s,e){this.workDir=this.managedAgentPolicy?.adapterPolicy.workDir??s,this.seq=0,e&&(this.chatId=e)}async send(t,s,e,n,o,r){if(r?.source==="room_activation"&&!this.managedAgentPolicy)throw new Error("managed_agent_policy_missing");await this.killProcess(),this.runId=l(),this.resetRunState();const a=["-p",`${p(this.workDir??process.cwd(),this.externalChannel,this.shennianSessionId??void 0,o)}${t}`,"--output-format","stream-json","--stream-partial-output"];s&&a.push("--model",s),this.chatId&&a.push("--continue"),await this.spawnAndParse(a)}async resume(t){await this.killProcess(),this.chatId=t,this.runId=l(),this.resetRunState(),await this.spawnAndParse(["--resume",t,"--output-format","stream-json","--stream-partial-output"])}async stop(){await this.killProcess()}async spawnAndParse(t){const s=u("cursor");if(!s){this.emit("error",new Error('Cursor Agent CLI not found. Expected "agent" or "cursor agent".'));return}const e=await this.prepareManagedLaunch();let n;try{n=m(s,[...t,...e.args],{cwd:this.workDir??void 0,stdio:["ignore","pipe","pipe"],env:e.env})}catch(i){throw await this.disposeManagedConfig(e.configDir),i}this.process=n,this.managedConfigDir=e.configDir,d({input:n.stdout}).on("line",i=>{if(i.trim())try{const a=JSON.parse(i);this.handleStreamEvent(a)}catch{this.emitEvent({state:"delta",text:i})}});let r="";n.stderr?.on("data",i=>{r+=i.toString()}),n.on("close",i=>{if(this.process===n&&(this.process=null,this.disposeManagedConfig(e.configDir),i!==0&&i!==null)){const a=r.trim()||`cursor agent exited with code ${i}`;this.emitEvent({state:"error",message:a})}}),n.on("error",i=>{this.process===n&&(this.process=null,this.disposeManagedConfig(e.configDir),i.code==="ENOENT"?this.emit("error",new Error('Command "cursor" not found. Is Cursor CLI installed?')):this.emit("error",i))})}async prepareManagedLaunch(){const t=this.managedAgentPolicy;if(!t)return{args:["--trust"],env:g(this.extraEnv),configDir:null};const s=w(v(t),t.adapterPolicy.workDir),e=await y(s),n=f({agentType:"cursor",providerEnv:this.extraEnv,sessionEnv:this.extraEnv,runtimeEnv:{NO_COLOR:"1"}});return n.CURSOR_CONFIG_DIR=e,{args:s.args,env:n,configDir:e}}async disposeManagedConfig(t){t&&(this.managedConfigDir===t&&(this.managedConfigDir=null),await k(t).catch(()=>{}))}handleStreamEvent(t){switch(t.session_id&&!this.chatId&&(this.chatId=t.session_id),t.chat_id&&!this.chatId&&(this.chatId=t.chat_id),t.type??t.event){case"system":break;case"assistant":{if(t.message?.content){if(!this.isLiveAssistantDelta(t))break;for(const e of t.message.content)e.type==="text"&&e.text?this.emitTextDelta(e.text):e.type==="thinking"&&e.thinking?this.emitEvent({state:"delta",text:e.thinking,thinking:!0}):e.type==="tool_use"&&this.emitEvent({state:"tool-call",name:e.name,args:e.input});break}(t.delta??t.text)&&this.emitEvent({state:"delta",text:t.delta??t.text});break}case"user":break;case"result":{if(t.subtype==="success"||!t.is_error){this.emitFinalResultDelta(t.result);const e=t.usage;this.emitEvent({state:"final",agentSessionId:this.chatId??void 0,usage:e?{inputTokens:e.inputTokens??e.input_tokens??0,outputTokens:e.outputTokens??e.output_tokens??0}:void 0})}else this.emitEvent({state:"error",message:t.result??t.error??"unknown error"});break}case"error":{this.emitEvent({state:"error",message:t.error??"unknown error"});break}case"text":case"delta":case"content":{const e=t.delta??t.text;e&&this.emitEvent({state:"delta",text:e});break}case"done":{this.emitEvent({state:"final",agentSessionId:this.chatId??void 0});break}default:t.done?this.emitEvent({state:"final",agentSessionId:this.chatId??void 0}):(t.delta??t.text)&&this.emitEvent({state:"delta",text:t.delta??t.text})}}emitEvent(t){const s={...t,runId:this.runId,seq:this.seq++};this.emit("agentEvent",s)}async killProcess(){const t=this.process;if(!t){await this.disposeManagedConfig(this.managedConfigDir);return}this.process=null,t.kill("SIGTERM"),await new Promise(s=>{t.on("close",s),setTimeout(()=>{t.kill("SIGKILL"),s()},3e3)}),await this.disposeManagedConfig(this.managedConfigDir)}resetRunState(){this.seq=0,this.emittedText=""}isLiveAssistantDelta(t){return t.timestamp_ms!=null&&!t.model_call_id}emitTextDelta(t){t&&(this.emittedText+=t,this.emitEvent({state:"delta",text:t}))}emitFinalResultDelta(t){if(!t)return;if(!this.emittedText){this.emitTextDelta(t);return}if(!t.startsWith(this.emittedText))return;const s=t.slice(this.emittedText.length);s&&this.emitTextDelta(s)}}h("cursor",()=>new x);export{x as CursorAdapter};
1
+ import{createInterface as d}from"node:readline";import{randomUUID as l}from"node:crypto";import{AgentAdapter as c,registerAgent as h}from"./adapter.js";import{resolveBuiltinCommand as u,spawnResolvedCommand as m}from"./command-spec.js";import{buildAgentProcessEnv as g,buildManagedAgentProcessEnv as f}from"../agent-env.js";import{buildInlineChannelPreamble as p}from"./platform-instructions.js";import{resolveManagedAgentAdapterEnforcement as v}from"../security/managed-agent-policy.js";import{registerManagedAgentRuntimeSecurity as E}from"../security/managed-agent-runtime.js";import{compileCursorManagedPermissions as w,createCursorManagedConfigDirectory as y,CURSOR_MANAGED_RUNTIME_SECURITY as C,disposeCursorManagedConfigDirectory as k}from"./cursor-managed-permissions.js";E("cursor",C);class x extends c{type="cursor";get recoveryCapabilities(){return{inspectTerminal:!1,reattachRun:!1,resumeRun:!1,resumeSession:!0}}process=null;chatId=null;workDir=null;seq=0;runId="";emittedText="";externalChannel=null;shennianSessionId=null;extraEnv={};managedAgentPolicy=null;managedConfigDir=null;configure(t){this.shennianSessionId=t.sessionId??null,this.externalChannel=t.externalChannel??null,this.extraEnv=t.env??{},"managedAgentPolicy"in t&&(this.managedAgentPolicy=t.managedAgentPolicy??null),this.managedAgentPolicy&&(this.workDir=this.managedAgentPolicy.adapterPolicy.workDir)}async start(t,s,e){this.workDir=this.managedAgentPolicy?.adapterPolicy.workDir??s,this.seq=0,e&&(this.chatId=e)}async send(t,s,e,n,o,r){if(r?.source==="room_activation"&&!this.managedAgentPolicy)throw new Error("managed_agent_policy_missing");await this.killProcess(),this.runId=l(),this.resetRunState();const i=`${p(this.workDir??process.cwd(),this.externalChannel,this.shennianSessionId??void 0,o)}${t}`;await this.spawnAndParse(this.buildSendArgs(i,s))}async resume(t){await this.killProcess(),this.chatId=t,this.runId=l(),this.resetRunState(),await this.spawnAndParse(["--resume",t,"--output-format","stream-json","--stream-partial-output"])}async stop(){await this.killProcess()}buildSendArgs(t,s){const e=["-p",t,"--output-format","stream-json","--stream-partial-output"];return s&&e.push("--model",s),this.chatId&&e.push("--resume",this.chatId),e}async spawnAndParse(t){const s=u("cursor");if(!s){this.emit("error",new Error('Cursor Agent CLI not found. Expected "agent" or "cursor agent".'));return}const e=await this.prepareManagedLaunch();let n;try{n=m(s,[...t,...e.args],{cwd:this.workDir??void 0,stdio:["ignore","pipe","pipe"],env:e.env})}catch(i){throw await this.disposeManagedConfig(e.configDir),i}this.process=n,this.managedConfigDir=e.configDir,d({input:n.stdout}).on("line",i=>{if(i.trim())try{const a=JSON.parse(i);this.handleStreamEvent(a)}catch{this.emitEvent({state:"delta",text:i})}});let r="";n.stderr?.on("data",i=>{r+=i.toString()}),n.on("close",i=>{if(this.process===n&&(this.process=null,this.disposeManagedConfig(e.configDir),i!==0&&i!==null)){const a=r.trim()||`cursor agent exited with code ${i}`;this.emitEvent({state:"error",message:a})}}),n.on("error",i=>{this.process===n&&(this.process=null,this.disposeManagedConfig(e.configDir),i.code==="ENOENT"?this.emit("error",new Error('Command "cursor" not found. Is Cursor CLI installed?')):this.emit("error",i))})}async prepareManagedLaunch(){const t=this.managedAgentPolicy;if(!t)return{args:["--trust"],env:g(this.extraEnv),configDir:null};const s=w(v(t),t.adapterPolicy.workDir),e=await y(s),n=f({agentType:"cursor",providerEnv:this.extraEnv,sessionEnv:this.extraEnv,runtimeEnv:{NO_COLOR:"1"}});return n.CURSOR_CONFIG_DIR=e,{args:s.args,env:n,configDir:e}}async disposeManagedConfig(t){t&&(this.managedConfigDir===t&&(this.managedConfigDir=null),await k(t).catch(()=>{}))}handleStreamEvent(t){switch(t.session_id&&!this.chatId&&(this.chatId=t.session_id),t.chat_id&&!this.chatId&&(this.chatId=t.chat_id),t.type??t.event){case"system":break;case"assistant":{if(t.message?.content){if(!this.isLiveAssistantDelta(t))break;for(const e of t.message.content)e.type==="text"&&e.text?this.emitTextDelta(e.text):e.type==="thinking"&&e.thinking?this.emitEvent({state:"delta",text:e.thinking,thinking:!0}):e.type==="tool_use"&&this.emitEvent({state:"tool-call",name:e.name,args:e.input});break}(t.delta??t.text)&&this.emitEvent({state:"delta",text:t.delta??t.text});break}case"user":break;case"result":{if(t.subtype==="success"||!t.is_error){this.emitFinalResultDelta(t.result);const e=t.usage;this.emitEvent({state:"final",agentSessionId:this.chatId??void 0,usage:e?{inputTokens:e.inputTokens??e.input_tokens??0,outputTokens:e.outputTokens??e.output_tokens??0}:void 0})}else this.emitEvent({state:"error",message:t.result??t.error??"unknown error"});break}case"error":{this.emitEvent({state:"error",message:t.error??"unknown error"});break}case"text":case"delta":case"content":{const e=t.delta??t.text;e&&this.emitEvent({state:"delta",text:e});break}case"done":{this.emitEvent({state:"final",agentSessionId:this.chatId??void 0});break}default:t.done?this.emitEvent({state:"final",agentSessionId:this.chatId??void 0}):(t.delta??t.text)&&this.emitEvent({state:"delta",text:t.delta??t.text})}}emitEvent(t){const s={...t,runId:this.runId,seq:this.seq++};this.emit("agentEvent",s)}async killProcess(){const t=this.process;if(!t){await this.disposeManagedConfig(this.managedConfigDir);return}this.process=null,t.kill("SIGTERM"),await new Promise(s=>{t.on("close",s),setTimeout(()=>{t.kill("SIGKILL"),s()},3e3)}),await this.disposeManagedConfig(this.managedConfigDir)}resetRunState(){this.seq=0,this.emittedText=""}isLiveAssistantDelta(t){return t.timestamp_ms!=null&&!t.model_call_id}emitTextDelta(t){t&&(this.emittedText+=t,this.emitEvent({state:"delta",text:t}))}emitFinalResultDelta(t){if(!t)return;if(!this.emittedText){this.emitTextDelta(t);return}if(!t.startsWith(this.emittedText))return;const s=t.slice(this.emittedText.length);s&&this.emitTextDelta(s)}}h("cursor",()=>new x);export{x as CursorAdapter};
@@ -84,6 +84,7 @@ export declare function recordStartedDaemon(childPid: number | undefined): void;
84
84
  export declare function findRunningDaemonProcessIds(excludePid?: number): number[];
85
85
  export declare function isIsolatedDaemonE2eInstance(env?: NodeJS.ProcessEnv, shennianDir?: string, tempDir?: string, defaultDir?: string): boolean;
86
86
  export declare function shouldDiscoverGlobalDaemonProcesses(env?: NodeJS.ProcessEnv, shennianDir?: string): boolean;
87
+ export declare function shouldInstallPlatformService(env?: NodeJS.ProcessEnv, shennianDir?: string): boolean;
87
88
  export declare function getDaemonStatus(opts?: {
88
89
  cleanupStale?: boolean;
89
90
  }): DaemonStatus;
@@ -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 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>
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 Se}from"node:child_process";import{randomUUID as F}from"node:crypto";import{fileURLToPath as he}from"node:url";import{getShennianDir as ge,loadConfig as B,resolveShennianPath as S,saveConfig as ye}from"../config/index.js";import{buildAugmentedPath as we}from"../env-path.js";import{buildWindowsLauncherCommand as Ee,buildWindowsScheduledTaskCommands as ve,buildWindowsScheduledTaskXml as Pe,buildWindowsStartupVbs as Ae,escapeXml as x,isWindowsCmdScript as Ne,quoteCmdArg as V,quoteSystemdArg as Ie,suspendWindowsScheduledTask as De}from"./daemon-windows.js";import{waitForDaemonReadyStatus as T}from"./daemon-start-wait.js";import{findRunningWindowsDaemonProcessIds as ke,isShennianRunServiceCommand as _e}from"./daemon-process.js";import{getCurrentVersion as G}from"../upgrade/engine.js";import{buildWindowsLauncherCommand as Rn,buildWindowsScheduledTaskCommands as xn,buildWindowsScheduledTaskSuspendCommands as Tn,buildWindowsScheduledTaskXml as Ln,buildWindowsStartupVbs as On,suspendWindowsScheduledTask as Cn}from"./daemon-windows.js";import{isShennianRunServiceCommand as $n}from"./daemon-process.js";const be=a.dirname(he(import.meta.url)),h=ge(),y=S("daemon.pid"),m=S("daemon.log"),L=S("remote-access.disabled"),k=S("daemon-launcher.json"),K=a.resolve(be,"../../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 Re(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 xe(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 Te(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 A(){try{return Te(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 Le(){return o.existsSync(L)}function Oe(){try{o.unlinkSync(L)}catch{}}function Ce(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 He(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(Ce(r,n),!s)return{disabledAt:n,autoStartDisabled:!0};try{return i(t==="darwin"?`launchctl disable "${P()}"`: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 $e(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 "${P()}"`: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 Me(){return`${process.pid}-${Date.now()}-${F()}`}function Ue(e,n,r={}){o.mkdirSync(h,{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 Dn(e,n){const r=A();if(!(r?.pid!==e||r.instanceId!==n))try{o.unlinkSync(y)}catch{}}function z(e,n=Y(),r){o.mkdirSync(h,{recursive:!0}),o.writeFileSync(k,JSON.stringify({pid:e,launcher:n,...r?{instanceId:r}:{},updatedAt:new Date().toISOString()},null,2))}function je(e){if(e)try{if(JSON.parse(o.readFileSync(k,"utf-8")).instanceId!==e)return}catch{return}try{o.unlinkSync(k)}catch{}}function We(e){e&&z(e)}function Fe(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 ke(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:_e(i)?s:null}).filter(r=>typeof r=="number")}catch{return[]}}function Z(e=process.env,n=h,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=h){return!Z(e,n)}function Ve(e=process.env,n=h){return!Z(e,n)}function f(e={}){let n=A(),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=Me();Ue(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:Le(),launcher:Fe(r,t),platform:p(),shennianDir:h,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 ee(e){const n=e?.trim();if(!n)return;const r=B();r.serverUrl=n,ye(r)}function ne(e){return e?.trim()||process.env.SHENNIAN_DESKTOP_SERVER_URL?.trim()||void 0}const H="com.shennian.agent",g=a.join(d.homedir(),"Library/LaunchAgents",`${H}.plist`),te=S("Shennian.app"),Ge=a.join(d.homedir(),"AppData","Roaming","Microsoft","Windows","Start Menu","Programs","Startup"),_=S("autostart.cmd"),b=S("autostart.vbs"),R=S("autostart.xml"),Ke=a.join(Ge,"ShennianAgent.vbs");function re(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 Je(){const e=p()==="win32"?"npx.cmd":"npx",n=a.join(a.dirname(J),e);return o.existsSync(n)?n:re("npx")}function N(){return xe({nodeExec:J,scriptPath:K,shennianCommandPath:re("shennian"),npxPath:Je(),desktopBridgePath:process.env.SHENNIAN_DESKTOP_CLI_BRIDGE})}function oe(){try{u(`schtasks /delete /tn "${D}" /f`,{stdio:"pipe",windowsHide:!0})}catch{}}function se(){try{o.unlinkSync(Ke)}catch{}}function qe(){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 Xe(e){if(p()==="win32"&&Ne(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 Ye(e,n,r=process.env){return{detached:!0,stdio:["ignore",n,n],env:r,windowsHide:!0,...e.windowsVerbatimArguments?{windowsVerbatimArguments:!0}:{}}}function ze(){const e=N();o.writeFileSync(_,Ee(e,m)),o.writeFileSync(b,Ae(_)),se(),oe();const n=Pe({userId:qe(),command:a.join(process.env.SystemRoot||"C:\\Windows","System32","wscript.exe"),arguments:[V(b)]});o.writeFileSync(R,n,"utf8");const r=ve({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 Qe(){De(D,e=>{u(e,{stdio:"pipe",windowsHide:!0})})}function ie(){const e={};for(const n of q)process.env[n]&&(e[n]=process.env[n]);if(e.HOME??=d.homedir(),e.PATH=we({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 Ze(e={}){const n=ie(),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 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]
30
+ </plist>`}function ce(e=te){o.rmSync(e,{recursive:!0,force:!0})}function en(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 nn(){const e=ie(),n=N(),r=Object.entries(e).map(([s,i])=>`Environment=${s}=${i}`).join(`
31
+ `),t=[n.command,...n.args].map(Ie).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 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(`
44
+ WantedBy=default.target`}const D="ShennianAgent";function tn(e){u(e,{stdio:"pipe",windowsHide:!0})}function rn(){const e=process.getuid?.();if(typeof e=="number")return e;const n=d.userInfo();return typeof n.uid=="number"?n.uid:n.username}function P(e=rn()){return`gui/${e}/${H}`}function on(e=tn){try{return e(`launchctl enable "${P()}"; launchctl unload "${g}" 2>/dev/null; launchctl load -w "${g}"`),e(`launchctl kickstart -k "${P()}"`),!0}catch{try{return e(`launchctl enable "${P()}"`),e(`launchctl load -w "${g}"`),e(`launchctl kickstart -k "${P()}"`),!0}catch{return!1}}}function sn(){o.mkdirSync(h,{recursive:!0});const e={};for(const[n,r]of Object.entries(process.env))r!==void 0&&Re(n)&&(e[n]=r);o.writeFileSync(S("env.json"),JSON.stringify(e,null,2))}function ae(e={}){o.mkdirSync(h,{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}ue(n);const r=o.openSync(m,"a"),t=Xe(N()),s=Se(t.command,t.args,Ye(t,r));s.unref(),o.closeSync(r),We(s.pid),e.quiet||(console.log(c.green(`\u2713 Background service started (PID ${s.pid})`)),console.log(c.gray(` Logs: ${m}`)))}function $(e,n=A(),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(g)&&!o.existsSync(te);case"linux":return o.existsSync(I);case"win32":return o.existsSync(_)&&o.existsSync(b)&&o.existsSync(R)}}function cn(e,n,r,t=A(),s=G()){return!n.api?.trim()&&e.pid!==null&&e.running&&!e.stale&&!e.remoteAccessDisabled&&r&&!$(e,t,s)}function ue(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{A()?.pid===e.pid&&o.unlinkSync(y)}catch{}}catch{}}function de(e={}){if(o.mkdirSync(h,{recursive:!0}),!Ve())return!1;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(g);o.mkdirSync(t,{recursive:!0}),ce(),o.writeFileSync(g,Ze());const s=on(),i=en(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,nn());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 ze()}}function an(){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 le(e=5e3){const n=an();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{}je();for(const t of Q())if(t!==n.stoppedPid)try{process.kill(t,"SIGTERM"),await j(t,2e3)}catch{}return n}function un(e={}){ee(ne(e.api)),Oe(),ue(),de({quiet:e.json})||ae({quiet:e.json}),e.json?w(T(()=>f({cleanupStale:!0}))):console.log(c.green("\u2713 Background service started"))}async function dn(e={}){const n=He(),r=$e(),t=await le(),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 pe(e){const n=f({cleanupStale:!0});if(cn(n,e,M())){e.json?w(n):console.log(c.green(`\u2713 Background service already running (PID ${n.pid})`));return}sn(),un(e)}async function U(e={}){await dn(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 ln(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}ee(ne(e.api));const r=p();r==="win32"&&Qe();const t=await le();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")&&de()){e.json&&w(T(()=>f({cleanupStale:!0})));return}ae({quiet:e.json}),e.json&&w(T(()=>f({cleanupStale:!0})))}function me(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 fe(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(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};
46
+ `))}}async function pn(){switch(await U(),p()){case"darwin":{if(o.existsSync(g)){try{u(`launchctl unload -w "${g}"`,{stdio:"pipe",windowsHide:!0})}catch{}o.unlinkSync(g)}ce(),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":{oe();try{o.unlinkSync(_)}catch{}try{o.unlinkSync(b)}catch{}try{o.unlinkSync(R)}catch{}se(),console.log(c.green("\u2713 Auto-start uninstalled (Task Scheduler)"));break}}}function kn(e){e.command("start").description("Start the background service").option("--json","Print machine-readable status JSON").option("--api <url>","Server URL override").action(t=>pe(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=>me(t)),e.command("logs").description("Show recent logs").option("-n, --lines <n>","Number of lines","50").action(t=>fe({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),pe(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),ln(t)}),n.command("status").description("Show background service status").option("--json","Print machine-readable status JSON").action(t=>{r("status",t.json),me(t)}),n.command("logs").description("Show recent logs").option("-n, --lines <n>","Number of lines","50").action(t=>{r("logs"),fe({lines:parseInt(t.lines,10)})}),n.command("uninstall").description("Uninstall auto-start service").action(pn)}export{Ye as buildDaemonSpawnOptions,Ze as buildMacLaunchdPlist,nn as buildSystemdUnit,Rn as buildWindowsLauncherCommand,xn as buildWindowsScheduledTaskCommands,Tn as buildWindowsScheduledTaskSuspendCommands,Ln as buildWindowsScheduledTaskXml,On as buildWindowsStartupVbs,cn as canReuseRunningDaemonForStart,ie as captureEnvForService,je as clearDaemonLauncher,Dn as clearDaemonPidIfOwner,Oe as clearRemoteAccessDisabled,Me as createDaemonInstanceId,Q as findRunningDaemonProcessIds,Y as getCurrentProcessDaemonLauncher,f as getDaemonStatus,P as getLaunchdServiceTarget,en as getMacBackgroundItemNotice,de as installService,M as isAutoStartServicePresent,O as isEphemeralCliPath,Z as isIsolatedDaemonE2eInstance,Le as isRemoteAccessDisabled,Re as isSafeSnapshotEnvKey,$n as isShennianRunServiceCommand,He as prepareRemoteAccessPause,We as recordStartedDaemon,kn as registerDaemonCommand,on as reloadLaunchdAgent,ce as removeLegacyMacBackgroundApp,xe as resolveServiceLaunchSpec,sn as saveEnvSnapshot,Be as shouldDiscoverGlobalDaemonProcesses,Ve as shouldInstallPlatformService,$ as shouldReplaceRunningDaemon,ae as startDaemonProcess,$e as stopPreparedRemoteAccessService,Cn as suspendWindowsScheduledTask,z as writeDaemonLauncher,Ue as writeDaemonPid};
@@ -1,3 +1,3 @@
1
- import w from"node:crypto";import I from"node:net";import{resolveDaemonIpcPaths as D}from"./paths.js";import{DAEMON_IPC_MAX_REQUEST_BYTES as f,DAEMON_IPC_VERSION as u,DaemonIpcError as o}from"./protocol.js";import{readDaemonIpcSecret as g}from"./secret.js";async function C(t){const m=t.paths??D(),h=g(m.secretFile),c=w.randomUUID(),d=`${JSON.stringify({version:u,requestId:c,auth:h,method:t.method,params:t.params})}
2
- `;if(Buffer.byteLength(d,"utf8")>f)throw new o("request_too_large","Daemon IPC request is too large.");const n=await new Promise((l,r)=>{const e=I.createConnection(m.endpoint);let s="";const _=setTimeout(()=>{e.destroy(),r(new o("ipc_timeout","Shennian daemon did not respond."))},t.timeoutMs??5e3),a=i=>{clearTimeout(_),e.destroy(),i()};e.setEncoding("utf8"),e.once("connect",()=>e.write(d)),e.on("data",i=>{if(s+=i,Buffer.byteLength(s,"utf8")>f){a(()=>r(new o("response_too_large","Daemon IPC response is too large.")));return}const p=s.indexOf(`
3
- `);p<0||a(()=>{try{l(JSON.parse(s.slice(0,p)))}catch{r(new o("invalid_response","Daemon IPC response is invalid."))}})}),e.once("error",()=>a(()=>r(new o("daemon_unavailable","Shennian daemon is not running."))))});if(n.requestId!==c||n.version!==u)throw new o("response_mismatch","Daemon IPC response did not match the request.");if(!n.ok)throw new o(n.error.code,n.error.message);return n.result}export{C as callDaemonIpc};
1
+ import l from"node:crypto";import I from"node:net";import{resolveDaemonIpcPaths as w}from"./paths.js";import{DAEMON_IPC_MAX_REQUEST_BYTES as D,DAEMON_IPC_MAX_RESPONSE_BYTES as E,DAEMON_IPC_VERSION as f,DaemonIpcError as o}from"./protocol.js";import{readDaemonIpcSecret as S}from"./secret.js";async function v(t){const m=t.paths??w(),u=S(m.secretFile),c=l.randomUUID(),d=`${JSON.stringify({version:f,requestId:c,auth:u,method:t.method,params:t.params})}
2
+ `;if(Buffer.byteLength(d,"utf8")>D)throw new o("request_too_large","Daemon IPC request is too large.");const n=await new Promise((_,r)=>{const e=I.createConnection(m.endpoint);let s="";const h=setTimeout(()=>{e.destroy(),r(new o("ipc_timeout","Shennian daemon did not respond."))},t.timeoutMs??5e3),a=i=>{clearTimeout(h),e.destroy(),i()};e.setEncoding("utf8"),e.once("connect",()=>e.write(d)),e.on("data",i=>{if(s+=i,Buffer.byteLength(s,"utf8")>E){a(()=>r(new o("response_too_large","Daemon IPC response is too large.")));return}const p=s.indexOf(`
3
+ `);p<0||a(()=>{try{_(JSON.parse(s.slice(0,p)))}catch{r(new o("invalid_response","Daemon IPC response is invalid."))}})}),e.once("error",()=>a(()=>r(new o("daemon_unavailable","Shennian daemon is not running."))))});if(n.requestId!==c||n.version!==f)throw new o("response_mismatch","Daemon IPC response did not match the request.");if(!n.ok)throw new o(n.error.code,n.error.message);return n.result}export{v as callDaemonIpc};
@@ -1,6 +1,7 @@
1
1
  import type { JsonValue } from './json.js';
2
2
  export declare const DAEMON_IPC_VERSION: 1;
3
3
  export declare const DAEMON_IPC_MAX_REQUEST_BYTES: number;
4
+ export declare const DAEMON_IPC_MAX_RESPONSE_BYTES: number;
4
5
  export type DaemonIpcRequest = {
5
6
  version: typeof DAEMON_IPC_VERSION;
6
7
  requestId: string;
@@ -1 +1 @@
1
- const t=1,c=1048576;class s extends Error{code;constructor(r,o){super(o),this.code=r,this.name="DaemonIpcError"}}export{c as DAEMON_IPC_MAX_REQUEST_BYTES,t as DAEMON_IPC_VERSION,s as DaemonIpcError};
1
+ const e=1,t=1048576,c=1048576;class s extends Error{code;constructor(o,r){super(r),this.code=o,this.name="DaemonIpcError"}}export{t as DAEMON_IPC_MAX_REQUEST_BYTES,c as DAEMON_IPC_MAX_RESPONSE_BYTES,e as DAEMON_IPC_VERSION,s as DaemonIpcError};
@@ -1,3 +1,3 @@
1
- import a from"node:crypto";import o from"node:fs";import{DaemonIpcError as c}from"./protocol.js";function f(t,n){l(t);try{const e=o.lstatSync(n);if(!e.isFile()||e.isSymbolicLink())throw new c("unsafe_ipc_secret","Daemon IPC secret is not a regular file.");process.platform!=="win32"&&(e.mode&63)!==0&&o.chmodSync(n,384);const s=o.readFileSync(n,"utf8").trim();if(s.length<40||s.length>128)throw new c("invalid_ipc_secret","Daemon IPC secret is invalid.");return s}catch(e){if(!d(e))throw e}const i=a.randomBytes(32).toString("base64url");if(process.platform==="win32"){try{o.writeFileSync(n,`${i}
2
- `,{encoding:"utf8",mode:384,flag:"wx"})}catch(e){if(!m(e))throw e}return f(t,n)}const r=`${n}.tmp-${process.pid}-${a.randomBytes(6).toString("hex")}`;try{o.writeFileSync(r,`${i}
3
- `,{encoding:"utf8",mode:384,flag:"wx"}),o.chmodSync(r,384);try{o.linkSync(r,n)}catch(e){if(!m(e))throw e}}finally{try{o.unlinkSync(r)}catch{}}return f(t,n)}function h(t){const n=o.lstatSync(t);if(!n.isFile()||n.isSymbolicLink())throw new c("unsafe_ipc_secret","Daemon IPC secret is not a regular file.");const i=o.readFileSync(t,"utf8").trim();if(i.length<40||i.length>128)throw new c("invalid_ipc_secret","Daemon IPC secret is invalid.");return i}function l(t){o.mkdirSync(t,{recursive:!0,mode:448});const n=o.lstatSync(t);if(!n.isDirectory()||n.isSymbolicLink())throw new c("unsafe_runtime_directory","Daemon runtime path is not a directory.");process.platform!=="win32"&&o.chmodSync(t,448)}function d(t){return typeof t=="object"&&t!==null&&"code"in t&&t.code==="ENOENT"}function m(t){return typeof t=="object"&&t!==null&&"code"in t&&t.code==="EEXIST"}export{l as ensurePrivateRuntimeDirectory,f as loadOrCreateDaemonIpcSecret,h as readDaemonIpcSecret};
1
+ import a from"node:crypto";import o from"node:fs";import{DaemonIpcError as r}from"./protocol.js";function f(t,n){l(t);try{const e=o.lstatSync(n);if(!e.isFile()||e.isSymbolicLink())throw new r("unsafe_ipc_secret","Daemon IPC secret is not a regular file.");process.platform!=="win32"&&(e.mode&63)!==0&&o.chmodSync(n,384);const s=o.readFileSync(n,"utf8").trim();if(s.length<40||s.length>128)throw new r("invalid_ipc_secret","Daemon IPC secret is invalid.");return s}catch(e){if(!p(e))throw e}const i=a.randomBytes(32).toString("base64url");if(process.platform==="win32"){try{o.writeFileSync(n,`${i}
2
+ `,{encoding:"utf8",mode:384,flag:"wx"})}catch(e){if(!m(e))throw e}return f(t,n)}const c=`${n}.tmp-${process.pid}-${a.randomBytes(6).toString("hex")}`;try{o.writeFileSync(c,`${i}
3
+ `,{encoding:"utf8",mode:384,flag:"wx"}),o.chmodSync(c,384);try{o.linkSync(c,n)}catch(e){if(!m(e))throw e}}finally{try{o.unlinkSync(c)}catch{}}return f(t,n)}function w(t){const n=o.lstatSync(t);if(!n.isFile()||n.isSymbolicLink())throw new r("unsafe_ipc_secret","Daemon IPC secret is not a regular file.");if(process.platform!=="win32"&&(n.mode&63)!==0)throw new r("unsafe_ipc_secret_permissions","Daemon IPC secret permissions are not private.");const i=o.readFileSync(t,"utf8").trim();if(i.length<40||i.length>128)throw new r("invalid_ipc_secret","Daemon IPC secret is invalid.");return i}function l(t){o.mkdirSync(t,{recursive:!0,mode:448});const n=o.lstatSync(t);if(!n.isDirectory()||n.isSymbolicLink())throw new r("unsafe_runtime_directory","Daemon runtime path is not a directory.");process.platform!=="win32"&&o.chmodSync(t,448)}function p(t){return typeof t=="object"&&t!==null&&"code"in t&&t.code==="ENOENT"}function m(t){return typeof t=="object"&&t!==null&&"code"in t&&t.code==="EEXIST"}export{l as ensurePrivateRuntimeDirectory,f as loadOrCreateDaemonIpcSecret,w as readDaemonIpcSecret};
@@ -12,3 +12,9 @@ export declare function startDaemonIpcServer(options: {
12
12
  handlers: Record<string, DaemonIpcHandler>;
13
13
  paths?: DaemonIpcPaths;
14
14
  }): Promise<DaemonIpcServer>;
15
+ export declare function timingSafeSecretEqual(value: unknown, secret: string): boolean;
16
+ export declare function daemonIpcListenOptions(paths: DaemonIpcPaths): string | {
17
+ path: string;
18
+ readableAll: false;
19
+ writableAll: false;
20
+ };
@@ -1,3 +1,4 @@
1
- import _ from"node:crypto";import u from"node:fs";import h from"node:net";import{resolveDaemonIpcPaths as w}from"./paths.js";import{DAEMON_IPC_MAX_REQUEST_BYTES as S,DAEMON_IPC_VERSION as l,DaemonIpcError as f}from"./protocol.js";import{ensurePrivateRuntimeDirectory as I,loadOrCreateDaemonIpcSecret as P}from"./secret.js";function v(n,e=process.platform){const r=e==="darwin"?104:e==="linux"?107:null;if(r!==null&&Buffer.byteLength(n,"utf8")>r)throw new f("ipc_endpoint_too_long",`Daemon IPC endpoint exceeds the ${e} Unix socket path limit.`)}async function L(n){const e=n.paths??w();I(e.runtimeDirectory);const r=P(e.runtimeDirectory,e.secretFile);e.transport==="unix_socket"&&(v(e.endpoint),await k(e.endpoint));const o=h.createServer(t=>{t.setEncoding("utf8");let s="",c=!1;t.on("data",d=>{if(c)return;if(s+=d,Buffer.byteLength(s,"utf8")>S){c=!0,m(t,a("unknown","request_too_large","Daemon IPC request is too large."));return}const p=s.indexOf(`
2
- `);p<0||(c=!0,C(s.slice(0,p),r,n.handlers).then(y=>m(t,y)).catch(()=>m(t,a("unknown","internal_error","Daemon IPC request failed."))))}),t.on("error",()=>{})});await new Promise((t,s)=>{const c=d=>s(d);o.once("error",c),o.listen(e.endpoint,()=>{o.off("error",c),t()})});let i=null;if(e.transport==="unix_socket")try{u.chmodSync(e.endpoint,384);const t=u.lstatSync(e.endpoint);if(!t.isSocket()||t.isSymbolicLink())throw new f("unsafe_ipc_endpoint","Daemon IPC endpoint was not created as a Unix socket.");i={dev:t.dev,ino:t.ino}}catch(t){throw await new Promise(s=>o.close(()=>s())),t}return{paths:e,close:async()=>{if(await new Promise(t=>o.close(()=>t())),e.transport==="unix_socket"&&i)try{const t=u.lstatSync(e.endpoint);t.isSocket()&&t.dev===i.dev&&t.ino===i.ino&&u.unlinkSync(e.endpoint)}catch{}}}}async function k(n){try{const e=u.lstatSync(n);if(!e.isSocket()||e.isSymbolicLink())throw new f("unsafe_ipc_endpoint","Refusing to replace a non-socket IPC endpoint.");if(await g(n))throw new f("daemon_ipc_in_use","Another Shennian daemon IPC server is already running.");u.unlinkSync(n)}catch(e){if(x(e))return;throw e}}function g(n){return new Promise(e=>{const r=h.createConnection(n),o=i=>{r.destroy(),e(i)};r.once("connect",()=>o(!0)),r.once("error",()=>o(!1)),r.setTimeout(300,()=>o(!1))})}async function C(n,e,r){let o;try{o=JSON.parse(n)}catch{return a("unknown","invalid_json","Daemon IPC request must be JSON.")}const i=typeof o.requestId=="string"?o.requestId.slice(0,128):"unknown";if(o.version!==l)return a(i,"unsupported_version","Unsupported daemon IPC version.");if(!D(o.auth,e))return a(i,"unauthorized","Daemon IPC authentication failed.");if(typeof o.method!="string"||!/^[a-z][a-z0-9_.-]{0,127}$/.test(o.method))return a(i,"invalid_method","Invalid daemon IPC method.");const t=r[o.method];if(!t)return a(i,"method_not_found","Daemon IPC method is not supported.");try{const s=await t(o.params,{requestId:i});return{version:l,requestId:i,ok:!0,result:s}}catch(s){return s instanceof f?a(i,s.code,s.message):a(i,"method_failed","Daemon IPC method failed.")}}function D(n,e){if(typeof n!="string")return!1;const r=Buffer.from(n),o=Buffer.from(e);return r.length===o.length&&_.timingSafeEqual(r,o)}function a(n,e,r){return{version:l,requestId:n,ok:!1,error:{code:e,message:r}}}function m(n,e){n.end(`${JSON.stringify(e)}
3
- `)}function x(n){return typeof n=="object"&&n!==null&&"code"in n&&n.code==="ENOENT"}export{v as assertUnixSocketEndpointLength,L as startDaemonIpcServer};
1
+ import y from"node:crypto";import u from"node:fs";import h from"node:net";import{resolveDaemonIpcPaths as S}from"./paths.js";import{DAEMON_IPC_MAX_REQUEST_BYTES as w,DAEMON_IPC_MAX_RESPONSE_BYTES as I,DAEMON_IPC_VERSION as l,DaemonIpcError as f}from"./protocol.js";import{ensurePrivateRuntimeDirectory as g,loadOrCreateDaemonIpcSecret as P}from"./secret.js";function k(n,e=process.platform){const r=e==="darwin"?104:e==="linux"?107:null;if(r!==null&&Buffer.byteLength(n,"utf8")>r)throw new f("ipc_endpoint_too_long",`Daemon IPC endpoint exceeds the ${e} Unix socket path limit.`)}async function R(n){const e=n.paths??S();g(e.runtimeDirectory);const r=P(e.runtimeDirectory,e.secretFile);e.transport==="unix_socket"&&(k(e.endpoint),await v(e.endpoint));const o=h.createServer(t=>{t.setEncoding("utf8");let s="",c=!1;t.on("data",d=>{if(c)return;if(s+=d,Buffer.byteLength(s,"utf8")>w){c=!0,p(t,a("unknown","request_too_large","Daemon IPC request is too large."));return}const m=s.indexOf(`
2
+ `);m<0||(c=!0,D(s.slice(0,m),r,n.handlers).then(_=>p(t,_)).catch(()=>p(t,a("unknown","internal_error","Daemon IPC request failed."))))}),t.on("error",()=>{})});await new Promise((t,s)=>{const c=d=>s(d);o.once("error",c),o.listen(x(e),()=>{o.off("error",c),t()})});let i=null;if(e.transport==="unix_socket")try{u.chmodSync(e.endpoint,384);const t=u.lstatSync(e.endpoint);if(!t.isSocket()||t.isSymbolicLink())throw new f("unsafe_ipc_endpoint","Daemon IPC endpoint was not created as a Unix socket.");i={dev:t.dev,ino:t.ino}}catch(t){throw await new Promise(s=>o.close(()=>s())),t}return{paths:e,close:async()=>{if(await new Promise(t=>o.close(()=>t())),e.transport==="unix_socket"&&i)try{const t=u.lstatSync(e.endpoint);t.isSocket()&&t.dev===i.dev&&t.ino===i.ino&&u.unlinkSync(e.endpoint)}catch{}}}}async function v(n){try{const e=u.lstatSync(n);if(!e.isSocket()||e.isSymbolicLink())throw new f("unsafe_ipc_endpoint","Refusing to replace a non-socket IPC endpoint.");if(await C(n))throw new f("daemon_ipc_in_use","Another Shennian daemon IPC server is already running.");u.unlinkSync(n)}catch(e){if(q(e))return;throw e}}function C(n){return new Promise(e=>{const r=h.createConnection(n),o=i=>{r.destroy(),e(i)};r.once("connect",()=>o(!0)),r.once("error",()=>o(!1)),r.setTimeout(300,()=>o(!1))})}async function D(n,e,r){let o;try{o=JSON.parse(n)}catch{return a("unknown","invalid_json","Daemon IPC request must be JSON.")}const i=typeof o.requestId=="string"?o.requestId.slice(0,128):"unknown";if(o.version!==l)return a(i,"unsupported_version","Unsupported daemon IPC version.");if(!E(o.auth,e))return a(i,"unauthorized","Daemon IPC authentication failed.");if(typeof o.method!="string"||!/^[a-z][a-z0-9_.-]{0,127}$/.test(o.method))return a(i,"invalid_method","Invalid daemon IPC method.");const t=r[o.method];if(!t)return a(i,"method_not_found","Daemon IPC method is not supported.");try{const s=await t(o.params,{requestId:i});return{version:l,requestId:i,ok:!0,result:s}}catch(s){return s instanceof f?a(i,s.code,s.message):a(i,"method_failed","Daemon IPC method failed.")}}function E(n,e){if(typeof n!="string")return!1;const r=Buffer.from(n),o=Buffer.from(e),i=Buffer.alloc(o.length);return r.copy(i,0,0,o.length),y.timingSafeEqual(i,o)&&r.length===o.length}function a(n,e,r){return{version:l,requestId:n,ok:!1,error:{code:e,message:r}}}function p(n,e){let r=JSON.stringify(e);Buffer.byteLength(`${r}
3
+ `,"utf8")>I&&(r=JSON.stringify(a(e.requestId,"response_too_large","Daemon IPC response is too large."))),n.end(`${r}
4
+ `)}function x(n){return n.transport==="unix_socket"?n.endpoint:{path:n.endpoint,readableAll:!1,writableAll:!1}}function q(n){return typeof n=="object"&&n!==null&&"code"in n&&n.code==="ENOENT"}export{k as assertUnixSocketEndpointLength,x as daemonIpcListenOptions,R as startDaemonIpcServer,E as timingSafeSecretEqual};
@@ -1 +1 @@
1
- const c="[redacted]";function f(t,e=[]){let o=t;for(const i of e)i.length>=6&&(o=o.split(i).join(c));return o.replace(/https?:\/\/[^\s"'<>]+/gi,i=>i.replace(/[?#].*$/,"")).replace(/\b(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)\s+(\/[^\s?#]*)(?:[?#][^\s]*)/gi,"$1 $2").replace(/\b(Authorization\s*[:=]\s*)(?:(?:Bearer|Basic)\s+)?[^\s,;}"']+/gi,`$1${c}`).replace(/\b((?:Cookie|Set-Cookie)\s*[:=]\s*)[^\r\n]+/gi,`$1${c}`).replace(/Bearer\s+[^\s,"'}]+/gi,`Bearer ${c}`).replace(/([?&](?:token|access|invite|code|signature|ticket|randstr|csrf)=)[^&#\s]+/gi,`$1${c}`).replace(/(["']?(?:token|tokenPreview|credentialPreview|secretPreview|accessToken|refreshToken|machineToken|pairToken|pushToken|shareToken|idToken|apiKey|clientSecret|deviceSecret|deviceSecretHash|browserState|inviteToken|inviteCode|shareCode|signature|code|smsCode|verificationCode|oneTimeCode|otp|ticket|randstr|csrf|csrfToken|xsrfToken|password|credentials?)["']?\s*[:=]\s*)(["']?)[^"'\s,;}]+\2/gi,`$1$2${c}$2`).replace(/\bsk-[A-Za-z0-9_-]{8,}\b/g,c).replace(/\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b/g,c).replace(/[A-Za-z0-9_-]{80,}/g,c)}function g(t){const e=t.replace(/[^a-z0-9]/gi,"").toLowerCase();return e==="apikey"||e==="apikeys"||e==="credentials"||e==="tokenpreview"||e==="credentialpreview"||e==="secretpreview"||e==="authorization"||e==="cookie"||e==="code"||e==="smscode"||e==="verificationcode"||e==="onetimecode"||e==="otp"||e==="invitecode"||e==="sharecode"||e==="ticket"||e==="randstr"||e==="csrf"||e==="csrftoken"||e==="xsrftoken"||e==="devicesecrethash"||e.endsWith("token")||e.endsWith("tokens")||e.endsWith("secret")||e.endsWith("secrets")||e.endsWith("password")||e.endsWith("signature")}function d(t,e=[],o="",i=0,s=new WeakSet){if(o&&g(o))return c;if(typeof t=="string")return f(t,e);if(t instanceof Error){const r=new Error(f(t.message,e));return r.name=t.name,r.stack=t.stack?f(t.stack,e):void 0,r}if(t&&typeof t=="object"){if(i>=10)return"[max-depth]";if(s.has(t))return"[circular]";if(s.add(t),Array.isArray(t)){const n=t.map(a=>d(a,e,"",i+1,s));return s.delete(t),n}const r=Object.fromEntries(Object.entries(t).map(([n,a])=>[n,d(a,e,n,i+1,s)]));return s.delete(t),r}return t}function h(t,e){const o=new Set,i=/(token|secret|password|api[_-]?key|authorization|cookie|signature)/i;for(const[r,n]of Object.entries(t))n&&n.length>=6&&i.test(r)&&o.add(n);const s=(r,n="",a=0)=>{if(!(a>8||r==null)){if(typeof r=="string"){r.length>=6&&i.test(n)&&o.add(r);return}if(typeof r=="object")for(const[k,l]of Object.entries(r))s(l,k,a+1)}};return s(e),[...o]}let p=!1;function T(t){if(!p){p=!0;for(const e of["log","info","warn","error"]){const o=console[e].bind(console);console[e]=(...i)=>{o(...i.map(s=>d(s,t)))}}}}export{h as collectKnownLogSecrets,T as installCliLogRedaction,f as redactCliLogText,d as sanitizeCliLogValue};
1
+ const c="[redacted]";function f(t,e=[]){let o=t;for(const i of e)i.length>=6&&(o=o.split(i).join(c));return o.replace(/https?:\/\/[^\s"'<>]+/gi,i=>i.replace(/[?#].*$/,"")).replace(/\b(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)\s+(\/[^\s?#]*)(?:[?#][^\s]*)/gi,"$1 $2").replace(/\b(Authorization\s*[:=]\s*)(?:(?:Bearer|Basic)\s+)?[^\s,;}"']+/gi,`$1${c}`).replace(/\b((?:Cookie|Set-Cookie)\s*[:=]\s*)[^\r\n]+/gi,`$1${c}`).replace(/Bearer\s+[^\s,"'}]+/gi,`Bearer ${c}`).replace(/([?&](?:token|access|invite|code|signature|ticket|randstr|csrf)=)[^&#\s]+/gi,`$1${c}`).replace(/(["']?(?:auth|token|secret|ipcSecret|daemonIpcSecret|tokenPreview|credentialPreview|secretPreview|accessToken|refreshToken|machineToken|pairToken|pushToken|shareToken|idToken|apiKey|clientSecret|deviceSecret|deviceSecretHash|browserState|inviteToken|inviteCode|shareCode|signature|code|smsCode|verificationCode|oneTimeCode|otp|ticket|randstr|csrf|csrfToken|xsrfToken|password|credentials?)["']?\s*[:=]\s*)(["']?)[^"'\s,;}]+\2/gi,`$1$2${c}$2`).replace(/\bsk-[A-Za-z0-9_-]{8,}\b/g,c).replace(/\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b/g,c).replace(/[A-Za-z0-9_-]{80,}/g,c)}function g(t){const e=t.replace(/[^a-z0-9]/gi,"").toLowerCase();return e==="apikey"||e==="apikeys"||e==="credentials"||e==="tokenpreview"||e==="credentialpreview"||e==="secretpreview"||e==="authorization"||e==="cookie"||e==="code"||e==="smscode"||e==="verificationcode"||e==="onetimecode"||e==="otp"||e==="invitecode"||e==="sharecode"||e==="ticket"||e==="randstr"||e==="csrf"||e==="csrftoken"||e==="xsrftoken"||e==="devicesecrethash"||e.endsWith("token")||e.endsWith("tokens")||e.endsWith("secret")||e.endsWith("secrets")||e.endsWith("password")||e.endsWith("signature")}function d(t,e=[],o="",i=0,s=new WeakSet){if(o&&g(o))return c;if(typeof t=="string")return f(t,e);if(t instanceof Error){const r=new Error(f(t.message,e));return r.name=t.name,r.stack=t.stack?f(t.stack,e):void 0,r}if(t&&typeof t=="object"){if(i>=10)return"[max-depth]";if(s.has(t))return"[circular]";if(s.add(t),Array.isArray(t)){const n=t.map(a=>d(a,e,"",i+1,s));return s.delete(t),n}const r=Object.fromEntries(Object.entries(t).map(([n,a])=>[n,d(a,e,n,i+1,s)]));return s.delete(t),r}return t}function h(t,e){const o=new Set,i=/(token|secret|password|api[_-]?key|authorization|cookie|signature)/i;for(const[r,n]of Object.entries(t))n&&n.length>=6&&i.test(r)&&o.add(n);const s=(r,n="",a=0)=>{if(!(a>8||r==null)){if(typeof r=="string"){r.length>=6&&i.test(n)&&o.add(r);return}if(typeof r=="object")for(const[k,l]of Object.entries(r))s(l,k,a+1)}};return s(e),[...o]}let p=!1;function T(t){if(!p){p=!0;for(const e of["log","info","warn","error"]){const o=console[e].bind(console);console[e]=(...i)=>{o(...i.map(s=>d(s,t)))}}}}export{h as collectKnownLogSecrets,T as installCliLogRedaction,f as redactCliLogText,d as sanitizeCliLogValue};
@@ -6,6 +6,14 @@ type WorkerDeps = {
6
6
  kill?: (pid: number, signal: NodeJS.Signals) => void;
7
7
  sleep?: (ms: number) => Promise<void>;
8
8
  execFile?: typeof execFile;
9
+ spawnDetached?: (command: string, args: string[], options: {
10
+ cwd: string;
11
+ detached: true;
12
+ stdio: 'ignore';
13
+ windowsHide: true;
14
+ }) => {
15
+ unref(): void;
16
+ };
9
17
  };
10
18
  export declare function runUpdaterJob(jobPath: string, deps?: WorkerDeps): Promise<void>;
11
19
  export {};
@@ -1,2 +1,2 @@
1
- import a from"node:fs";import k from"node:os";import o from"node:path";import{execFile as D}from"node:child_process";import{promisify as P}from"node:util";import{fileURLToPath as V}from"node:url";const S=P(D),_=1,A="https://registry.npmjs.org",E=30*6e4;function p(e){return(e instanceof Error?e.message:String(e)).replace(/[A-Za-z]:\\[^\s]+|\/(?:[^\s/]+\/)+[^\s]+/g,"<path>").replace(/(token|password|secret|authorization)\s*[=:]\s*[^\s]+/gi,"$1=<redacted>").replace(/\s+/g," ").slice(0,500)}function v(e,r){a.mkdirSync(o.dirname(e),{recursive:!0,mode:448});const t=`${e}.tmp-${process.pid}-${Date.now()}`;try{a.writeFileSync(t,`${JSON.stringify(r,null,2)}
2
- `,{mode:384,flag:"wx"});const n=a.openSync(t,process.platform==="win32"?"r+":"r");try{a.fsyncSync(n)}finally{a.closeSync(n)}a.renameSync(t,e)}finally{try{a.unlinkSync(t)}catch{}}}function N(e){const r=a.lstatSync(e);if(r.isSymbolicLink()||!r.isFile())throw new Error("invalid_job");const t=JSON.parse(a.readFileSync(e,"utf8")),n=u=>{if(typeof t[u]!="string"||!String(t[u]).trim())throw new Error("invalid_job");return String(t[u])},c=/^[0-9]+\.[0-9]+\.[0-9]+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/,l=n("fromVersion"),i=n("targetVersion");if(t.protocolVersion!==_||t.packageName!=="shennian")throw new Error("invalid_job");if(!c.test(l)||!c.test(i))throw new Error("invalid_job");if(!Number.isFinite(Date.parse(n("createdAt"))))throw new Error("invalid_job");if(!/^[A-Za-z0-9_-]{1,128}$/.test(n("jobId")))throw new Error("invalid_job");if(!["darwin","linux","win32"].includes(n("platform")))throw new Error("invalid_job");const d=t.oldDaemonPid==null?null:Number(t.oldDaemonPid),w=Number(t.daemonExitTimeoutMs),s=Number(t.commandTimeoutMs);if(d!=null&&(!Number.isSafeInteger(d)||d<1))throw new Error("invalid_job");if(!Number.isSafeInteger(w)||w<1e3)throw new Error("invalid_job");if(!Number.isSafeInteger(s)||s<1e4)throw new Error("invalid_job");return{protocolVersion:1,jobId:n("jobId"),fromVersion:l,targetVersion:i,npmPrefix:n("npmPrefix"),npmExecutable:n("npmExecutable"),nodeExecutable:n("nodeExecutable"),packageName:"shennian",operation:t.operation==="rollback"?"rollback":"upgrade",oldDaemonPid:d,platform:n("platform"),createdAt:n("createdAt"),daemonExitTimeoutMs:w,commandTimeoutMs:s,resultPath:n("resultPath"),attemptPath:n("attemptPath"),isolatedDaemonStart:t.isolatedDaemonStart===!0}}function F(e,r){return r==="win32"?o.win32.join(e,"node_modules","shennian"):o.posix.join(e,"lib","node_modules","shennian")}function h(e){try{return process.kill(e,0),!0}catch{return!1}}async function O(e,r){if(!e.oldDaemonPid||!(r.isAlive??h)(e.oldDaemonPid))return;const t=r.kill??((l,i)=>process.kill(l,i)),n=r.sleep??(l=>new Promise(i=>setTimeout(i,l)));t(e.oldDaemonPid,"SIGTERM");const c=(r.now??Date.now)()+e.daemonExitTimeoutMs;for(;(r.now??Date.now)()<c;){if(!(r.isAlive??h)(e.oldDaemonPid))return;await n(100)}if(r.kill)t(e.oldDaemonPid,"SIGKILL");else if(e.platform==="win32")try{await(r.execFile??S)("taskkill",["/PID",String(e.oldDaemonPid),"/T","/F"],{timeout:15e3,windowsHide:!0,shell:!0})}catch{}else try{process.kill(-e.oldDaemonPid,"SIGKILL")}catch{try{process.kill(e.oldDaemonPid,"SIGKILL")}catch{}}if(await n(250),(r.isAlive??h)(e.oldDaemonPid))throw new Error("daemon_exit_timeout")}function f(e,r=e.commandTimeoutMs){return{timeout:r,windowsHide:!0,shell:e.platform==="win32"}}async function x(e,r,t){const n=["install","-g",`shennian@${r}`,"--prefix",e.npmPrefix];try{await t(e.npmExecutable,n,f(e))}catch{await t(e.npmExecutable,[...n,"--registry",A],f(e))}}async function y(e,r,t){const n=F(e.npmPrefix,e.platform),c=o.join(n,"dist","bin","shennian.js"),{stdout:l}=await t(e.nodeExecutable,[c,"--version"],f(e,1e4));if(l.trim()!==r)throw new Error("verification_failed");return await t(e.npmExecutable,["ls","-g",`shennian@${r}`,"--omit=dev","--depth=0","--prefix",e.npmPrefix],f(e,3e4)),c}async function g(e,r,t){try{if(!e.isolatedDaemonStart&&e.platform==="darwin"){const n=typeof process.getuid=="function"?process.getuid():k.userInfo().uid;await t("launchctl",["kickstart","-k",`gui/${n}/com.shennian.agent`],f(e,15e3));return}if(!e.isolatedDaemonStart&&e.platform==="linux"){await t("systemctl",["--user","restart","shennian"],f(e,15e3));return}if(!e.isolatedDaemonStart&&e.platform==="win32"){await t("schtasks",["/Run","/TN","ShennianAgent"],f(e,15e3));return}}catch{}await t(e.nodeExecutable,[r,"start","--json"],f(e,3e4))}function T(e){return(r,t={})=>v(e.resultPath,{protocolVersion:_,jobId:e.jobId,state:r,fromVersion:e.fromVersion,targetVersion:e.targetVersion,operation:e.operation,...t,updatedAt:new Date().toISOString()})}function L(e,r){const t=o.dirname(o.dirname(e)),n=o.join(t,"updater.lock");a.mkdirSync(t,{recursive:!0,mode:448});try{const c=a.openSync(n,"wx",384);a.writeFileSync(c,JSON.stringify({jobId:r.jobId,pid:process.pid,createdAt:Date.now()})),a.closeSync(c)}catch(c){if(c.code!=="EEXIST")throw c;try{const i=JSON.parse(a.readFileSync(n,"utf8"));if(i.jobId!==r.jobId&&i.pid&&h(i.pid)&&Date.now()-Number(i.createdAt)<E)throw new Error("upgrade_locked")}catch(i){if(i instanceof Error&&i.message==="upgrade_locked")throw i}a.rmSync(n,{force:!0});const l=a.openSync(n,"wx",384);a.writeFileSync(l,JSON.stringify({jobId:r.jobId,pid:process.pid,createdAt:Date.now()})),a.closeSync(l)}return()=>{try{a.unlinkSync(n)}catch{}}}async function $(e,r={}){let t;try{t=N(e)}catch{throw new Error("invalid_job")}if((r.now??Date.now)()-Date.parse(t.createdAt)>E)throw new Error("job_expired");const n=o.dirname(o.dirname(o.resolve(e)));if(o.basename(o.dirname(o.resolve(e)))!=="jobs"||o.basename(o.resolve(e))!==`${t.jobId}.json`)throw new Error("invalid_job");if(o.dirname(o.resolve(t.resultPath))!==o.join(n,"results"))throw new Error("invalid_job");if(o.basename(o.resolve(t.resultPath))!==`${t.jobId}.json`)throw new Error("invalid_job");const c=o.dirname(o.dirname(n));if(o.resolve(t.attemptPath)!==o.join(c,"upgrade-attempt.json"))throw new Error("invalid_job");const l=L(e,t),i=T(t),d=r.execFile??S;let w=!1;try{let s=null;try{s=JSON.parse(a.readFileSync(t.resultPath,"utf8"))}catch{}if(s?.jobId===t.jobId&&s.targetVersion===t.targetVersion&&["healthy","rolled_back","failed"].includes(s.state??""))return;if(s?.jobId===t.jobId&&s.state==="starting"){const m=await y(t,t.targetVersion,d);await g(t,m,d);return}i("waiting_for_daemon"),await(r.sleep??(m=>new Promise(b=>setTimeout(b,m))))(250),await O(t,r),i("installing"),w=!0;try{await x(t,t.targetVersion,d)}catch(m){throw Object.assign(new Error(p(m)),{code:"npm_install_failed"})}i("verifying");let u;try{u=await y(t,t.targetVersion,d)}catch(m){throw Object.assign(new Error(p(m)),{code:"verification_failed"})}v(t.attemptPath,{from:t.fromVersion,to:t.targetVersion,attemptCount:0,attemptAt:Date.now(),jobId:t.jobId,operation:t.operation}),i("starting"),await g(t,u,d)}catch(s){const u=s.code??(s instanceof Error?s.message:"internal_error");if(!w){i("failed",{errorCode:u,errorSummary:p(s)});return}try{await x(t,t.fromVersion,d);const m=await y(t,t.fromVersion,d);await g(t,m,d),i("rolled_back",{effectiveVersion:t.fromVersion,errorCode:u,errorSummary:p(s)})}catch(m){throw i("failed",{errorCode:"rollback_failed",errorSummary:p(m)}),m}}finally{l()}}function I(e){try{return a.realpathSync.native(e)}catch{return o.resolve(e)}}const j=process.argv[1]?I(process.argv[1]):"",J=I(V(import.meta.url));if(j===J){const e=process.argv[2];e?$(e).catch(()=>{process.exitCode=1}):process.exitCode=2}export{$ as runUpdaterJob};
1
+ import a from"node:fs";import k from"node:os";import o from"node:path";import{execFile as D,spawn as P}from"node:child_process";import{promisify as V}from"node:util";import{fileURLToPath as A}from"node:url";const S=V(D),E=1,N="https://registry.npmjs.org",_=30*6e4;function p(e){return(e instanceof Error?e.message:String(e)).replace(/[A-Za-z]:\\[^\s]+|\/(?:[^\s/]+\/)+[^\s]+/g,"<path>").replace(/(token|password|secret|authorization)\s*[=:]\s*[^\s]+/gi,"$1=<redacted>").replace(/\s+/g," ").slice(0,500)}function x(e,r){a.mkdirSync(o.dirname(e),{recursive:!0,mode:448});const t=`${e}.tmp-${process.pid}-${Date.now()}`;try{a.writeFileSync(t,`${JSON.stringify(r,null,2)}
2
+ `,{mode:384,flag:"wx"});const n=a.openSync(t,process.platform==="win32"?"r+":"r");try{a.fsyncSync(n)}finally{a.closeSync(n)}a.renameSync(t,e)}finally{try{a.unlinkSync(t)}catch{}}}function F(e){const r=a.lstatSync(e);if(r.isSymbolicLink()||!r.isFile())throw new Error("invalid_job");const t=JSON.parse(a.readFileSync(e,"utf8")),n=u=>{if(typeof t[u]!="string"||!String(t[u]).trim())throw new Error("invalid_job");return String(t[u])},s=/^[0-9]+\.[0-9]+\.[0-9]+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/,l=n("fromVersion"),i=n("targetVersion");if(t.protocolVersion!==E||t.packageName!=="shennian")throw new Error("invalid_job");if(!s.test(l)||!s.test(i))throw new Error("invalid_job");if(!Number.isFinite(Date.parse(n("createdAt"))))throw new Error("invalid_job");if(!/^[A-Za-z0-9_-]{1,128}$/.test(n("jobId")))throw new Error("invalid_job");if(!["darwin","linux","win32"].includes(n("platform")))throw new Error("invalid_job");const d=t.oldDaemonPid==null?null:Number(t.oldDaemonPid),w=Number(t.daemonExitTimeoutMs),c=Number(t.commandTimeoutMs);if(d!=null&&(!Number.isSafeInteger(d)||d<1))throw new Error("invalid_job");if(!Number.isSafeInteger(w)||w<1e3)throw new Error("invalid_job");if(!Number.isSafeInteger(c)||c<1e4)throw new Error("invalid_job");return{protocolVersion:1,jobId:n("jobId"),fromVersion:l,targetVersion:i,npmPrefix:n("npmPrefix"),npmExecutable:n("npmExecutable"),nodeExecutable:n("nodeExecutable"),packageName:"shennian",operation:t.operation==="rollback"?"rollback":"upgrade",oldDaemonPid:d,platform:n("platform"),createdAt:n("createdAt"),daemonExitTimeoutMs:w,commandTimeoutMs:c,resultPath:n("resultPath"),attemptPath:n("attemptPath"),isolatedDaemonStart:t.isolatedDaemonStart===!0}}function O(e,r){return r==="win32"?o.win32.join(e,"node_modules","shennian"):o.posix.join(e,"lib","node_modules","shennian")}function h(e){try{return process.kill(e,0),!0}catch{return!1}}async function T(e,r){if(!e.oldDaemonPid||!(r.isAlive??h)(e.oldDaemonPid))return;const t=r.kill??((l,i)=>process.kill(l,i)),n=r.sleep??(l=>new Promise(i=>setTimeout(i,l)));t(e.oldDaemonPid,"SIGTERM");const s=(r.now??Date.now)()+e.daemonExitTimeoutMs;for(;(r.now??Date.now)()<s;){if(!(r.isAlive??h)(e.oldDaemonPid))return;await n(100)}if(r.kill)t(e.oldDaemonPid,"SIGKILL");else if(e.platform==="win32")try{await(r.execFile??S)("taskkill",["/PID",String(e.oldDaemonPid),"/T","/F"],{timeout:15e3,windowsHide:!0,shell:!0})}catch{}else try{process.kill(-e.oldDaemonPid,"SIGKILL")}catch{try{process.kill(e.oldDaemonPid,"SIGKILL")}catch{}}if(await n(250),(r.isAlive??h)(e.oldDaemonPid))throw new Error("daemon_exit_timeout")}function f(e,r=e.commandTimeoutMs){return{timeout:r,windowsHide:!0,shell:e.platform==="win32"}}async function v(e,r,t){const n=["install","-g",`shennian@${r}`,"--prefix",e.npmPrefix];try{await t(e.npmExecutable,n,f(e))}catch{await t(e.npmExecutable,[...n,"--registry",N],f(e))}}async function y(e,r,t){const n=O(e.npmPrefix,e.platform),s=o.join(n,"dist","bin","shennian.js"),{stdout:l}=await t(e.nodeExecutable,[s,"--version"],f(e,1e4));if(l.trim()!==r)throw new Error("verification_failed");return await t(e.npmExecutable,["ls","-g",`shennian@${r}`,"--omit=dev","--depth=0","--prefix",e.npmPrefix],f(e,3e4)),s}async function g(e,r,t,n=P){if(e.isolatedDaemonStart){n(e.nodeExecutable,[r,"run-service"],{cwd:e.npmPrefix,detached:!0,stdio:"ignore",windowsHide:!0}).unref();return}try{if(e.platform==="darwin"){const s=typeof process.getuid=="function"?process.getuid():k.userInfo().uid;await t("launchctl",["kickstart","-k",`gui/${s}/com.shennian.agent`],f(e,15e3));return}if(e.platform==="linux"){await t("systemctl",["--user","restart","shennian"],f(e,15e3));return}if(e.platform==="win32"){await t("schtasks",["/Run","/TN","ShennianAgent"],f(e,15e3));return}}catch{}await t(e.nodeExecutable,[r,"start","--json"],f(e,3e4))}function L(e){return(r,t={})=>x(e.resultPath,{protocolVersion:E,jobId:e.jobId,state:r,fromVersion:e.fromVersion,targetVersion:e.targetVersion,operation:e.operation,...t,updatedAt:new Date().toISOString()})}function $(e,r){const t=o.dirname(o.dirname(e)),n=o.join(t,"updater.lock");a.mkdirSync(t,{recursive:!0,mode:448});try{const s=a.openSync(n,"wx",384);a.writeFileSync(s,JSON.stringify({jobId:r.jobId,pid:process.pid,createdAt:Date.now()})),a.closeSync(s)}catch(s){if(s.code!=="EEXIST")throw s;try{const i=JSON.parse(a.readFileSync(n,"utf8"));if(i.jobId!==r.jobId&&i.pid&&h(i.pid)&&Date.now()-Number(i.createdAt)<_)throw new Error("upgrade_locked")}catch(i){if(i instanceof Error&&i.message==="upgrade_locked")throw i}a.rmSync(n,{force:!0});const l=a.openSync(n,"wx",384);a.writeFileSync(l,JSON.stringify({jobId:r.jobId,pid:process.pid,createdAt:Date.now()})),a.closeSync(l)}return()=>{try{a.unlinkSync(n)}catch{}}}async function j(e,r={}){let t;try{t=F(e)}catch{throw new Error("invalid_job")}if((r.now??Date.now)()-Date.parse(t.createdAt)>_)throw new Error("job_expired");const n=o.dirname(o.dirname(o.resolve(e)));if(o.basename(o.dirname(o.resolve(e)))!=="jobs"||o.basename(o.resolve(e))!==`${t.jobId}.json`)throw new Error("invalid_job");if(o.dirname(o.resolve(t.resultPath))!==o.join(n,"results"))throw new Error("invalid_job");if(o.basename(o.resolve(t.resultPath))!==`${t.jobId}.json`)throw new Error("invalid_job");const s=o.dirname(o.dirname(n));if(o.resolve(t.attemptPath)!==o.join(s,"upgrade-attempt.json"))throw new Error("invalid_job");const l=$(e,t),i=L(t),d=r.execFile??S;let w=!1;try{let c=null;try{c=JSON.parse(a.readFileSync(t.resultPath,"utf8"))}catch{}if(c?.jobId===t.jobId&&c.targetVersion===t.targetVersion&&["healthy","rolled_back","failed"].includes(c.state??""))return;if(c?.jobId===t.jobId&&c.state==="starting"){const m=await y(t,t.targetVersion,d);await g(t,m,d,r.spawnDetached);return}i("waiting_for_daemon"),await(r.sleep??(m=>new Promise(b=>setTimeout(b,m))))(250),await T(t,r),i("installing"),w=!0;try{await v(t,t.targetVersion,d)}catch(m){throw Object.assign(new Error(p(m)),{code:"npm_install_failed"})}i("verifying");let u;try{u=await y(t,t.targetVersion,d)}catch(m){throw Object.assign(new Error(p(m)),{code:"verification_failed"})}x(t.attemptPath,{from:t.fromVersion,to:t.targetVersion,attemptCount:0,attemptAt:Date.now(),jobId:t.jobId,operation:t.operation}),i("starting"),await g(t,u,d,r.spawnDetached)}catch(c){const u=c.code??(c instanceof Error?c.message:"internal_error");if(!w){i("failed",{errorCode:u,errorSummary:p(c)});return}try{await v(t,t.fromVersion,d);const m=await y(t,t.fromVersion,d);await g(t,m,d,r.spawnDetached),i("rolled_back",{effectiveVersion:t.fromVersion,errorCode:u,errorSummary:p(c)})}catch(m){throw i("failed",{errorCode:"rollback_failed",errorSummary:p(m)}),m}}finally{l()}}function I(e){try{return a.realpathSync.native(e)}catch{return o.resolve(e)}}const J=process.argv[1]?I(process.argv[1]):"",C=I(A(import.meta.url));if(J===C){const e=process.argv[2];e?j(e).catch(()=>{process.exitCode=1}):process.exitCode=2}export{j as runUpdaterJob};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shennian",
3
- "version": "0.4.17",
3
+ "version": "0.4.19",
4
4
  "description": "Shennian — AI Agent Control Plane CLI",
5
5
  "type": "module",
6
6
  "bin": {