shennian 0.3.73 → 0.3.74

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.
Files changed (89) hide show
  1. package/dist/publish-build-manifest.json +122 -57
  2. package/dist/src/agent-env.d.ts +30 -0
  3. package/dist/src/agent-env.js +4 -4
  4. package/dist/src/agents/adapter.d.ts +5 -0
  5. package/dist/src/agents/claude-managed-permissions.d.ts +33 -0
  6. package/dist/src/agents/claude-managed-permissions.js +1 -0
  7. package/dist/src/agents/claude.d.ts +11 -2
  8. package/dist/src/agents/claude.js +5 -5
  9. package/dist/src/agents/codex-managed-permissions.d.ts +32 -0
  10. package/dist/src/agents/codex-managed-permissions.js +1 -0
  11. package/dist/src/agents/codex-managed-room-tools.d.ts +40 -0
  12. package/dist/src/agents/codex-managed-room-tools.js +1 -0
  13. package/dist/src/agents/codex.d.ts +20 -2
  14. package/dist/src/agents/codex.js +11 -10
  15. package/dist/src/agents/command-spec.d.ts +8 -0
  16. package/dist/src/agents/command-spec.js +3 -2
  17. package/dist/src/agents/cursor-managed-permissions.d.ts +28 -0
  18. package/dist/src/agents/cursor-managed-permissions.js +2 -0
  19. package/dist/src/agents/cursor.d.ts +8 -2
  20. package/dist/src/agents/cursor.js +1 -1
  21. package/dist/src/agents/custom-managed-runtime.d.ts +37 -0
  22. package/dist/src/agents/custom-managed-runtime.js +2 -0
  23. package/dist/src/agents/custom.d.ts +12 -2
  24. package/dist/src/agents/custom.js +4 -4
  25. package/dist/src/agents/detect.d.ts +3 -0
  26. package/dist/src/agents/detect.js +1 -1
  27. package/dist/src/agents/model-registry/service.js +1 -1
  28. package/dist/src/agents/opencode-managed-permissions.d.ts +25 -0
  29. package/dist/src/agents/opencode-managed-permissions.js +2 -0
  30. package/dist/src/agents/opencode.d.ts +8 -2
  31. package/dist/src/agents/opencode.js +1 -1
  32. package/dist/src/agents/workbuddy-managed-permissions.d.ts +37 -0
  33. package/dist/src/agents/workbuddy-managed-permissions.js +2 -0
  34. package/dist/src/agents/workbuddy.d.ts +8 -0
  35. package/dist/src/agents/workbuddy.js +2 -2
  36. package/dist/src/commands/daemon.d.ts +29 -0
  37. package/dist/src/commands/daemon.js +14 -14
  38. package/dist/src/commands/room.js +3 -3
  39. package/dist/src/commands/runtime.js +5 -5
  40. package/dist/src/config/index.d.ts +22 -0
  41. package/dist/src/daemon-ipc/paths.d.ts +8 -0
  42. package/dist/src/daemon-ipc/paths.js +1 -1
  43. package/dist/src/index.js +3 -3
  44. package/dist/src/log-redaction.d.ts +4 -0
  45. package/dist/src/log-redaction.js +1 -0
  46. package/dist/src/relay/client.d.ts +1 -0
  47. package/dist/src/relay/client.js +1 -1
  48. package/dist/src/room/activation-service.d.ts +23 -1
  49. package/dist/src/room/activation-service.js +7 -7
  50. package/dist/src/room/attachment-cache.d.ts +3 -0
  51. package/dist/src/room/attachment-cache.js +2 -2
  52. package/dist/src/room/attachment-upload.js +1 -1
  53. package/dist/src/room/managed-prompt-composer.d.ts +16 -3
  54. package/dist/src/room/managed-prompt-composer.js +26 -13
  55. package/dist/src/room/managed-room-binding.d.ts +3 -3
  56. package/dist/src/room/managed-room-binding.js +1 -1
  57. package/dist/src/room/managed-room-ipc.d.ts +32 -0
  58. package/dist/src/room/managed-room-ipc.js +3 -1
  59. package/dist/src/room/room-action-client.d.ts +5 -1
  60. package/dist/src/room/room-action-client.js +1 -1
  61. package/dist/src/security/managed-agent-credential-paths.d.ts +6 -0
  62. package/dist/src/security/managed-agent-credential-paths.js +1 -0
  63. package/dist/src/security/managed-agent-executable.d.ts +19 -0
  64. package/dist/src/security/managed-agent-executable.js +1 -0
  65. package/dist/src/security/managed-agent-policy.d.ts +55 -0
  66. package/dist/src/security/managed-agent-policy.js +1 -0
  67. package/dist/src/security/managed-agent-runtime.d.ts +44 -0
  68. package/dist/src/security/managed-agent-runtime.js +1 -0
  69. package/dist/src/session/handlers/chat.d.ts +8 -1
  70. package/dist/src/session/handlers/chat.js +2 -2
  71. package/dist/src/session/handlers/remote-access.d.ts +4 -0
  72. package/dist/src/session/handlers/remote-access.js +1 -0
  73. package/dist/src/session/handlers/room-managed.d.ts +1 -1
  74. package/dist/src/session/handlers/room-managed.js +1 -1
  75. package/dist/src/session/history-subscriptions.d.ts +6 -0
  76. package/dist/src/session/history-subscriptions.js +1 -1
  77. package/dist/src/session/manager.d.ts +7 -2
  78. package/dist/src/session/manager.js +1 -1
  79. package/dist/src/session/queue.d.ts +4 -0
  80. package/dist/src/session/queue.js +4 -4
  81. package/dist/src/session/types.d.ts +14 -0
  82. package/node_modules/@shennian/wire/dist/agent-workspace.d.ts +70 -0
  83. package/node_modules/@shennian/wire/dist/agent-workspace.js +111 -1
  84. package/node_modules/@shennian/wire/dist/machine.d.ts +2 -0
  85. package/node_modules/@shennian/wire/dist/methods.d.ts +1 -1
  86. package/node_modules/@shennian/wire/dist/session-sync.d.ts +53 -3
  87. package/node_modules/@shennian/wire/dist/session-sync.js +5 -1
  88. package/node_modules/@shennian/wire/dist/session.d.ts +2 -0
  89. package/package.json +3 -1
@@ -1,4 +1,4 @@
1
- import{createInterface as d}from"node:readline";import{randomUUID as o}from"node:crypto";import{AgentAdapter as l,registerAgent as p}from"./adapter.js";import{spawnCommandString as c}from"./command-spec.js";import{buildAgentProcessEnv as h}from"../agent-env.js";import{buildInlineChannelPreamble as m}from"./platform-instructions.js";const u=3e3;class w extends l{type;command;mode;proactive;sessionId=null;agentSessionId=null;workDir=null;runId="";seq=0;externalChannel=null;shennianSessionId=null;extraEnv={};stdioProcess=null;spawnProcess=null;dispatchGuard=null;dispatchObservedEvent=!1;constructor(s,t){super(),this.type=`custom:${s}`,this.command=t.command,this.mode=t.caps.mode,this.proactive=t.caps.proactive??!1}async start(s,t,r){this.sessionId=s,this.workDir=t,this.seq=0,r&&(this.agentSessionId=r),this.mode==="stdio"&&await this.startStdioProcess()}configure(s){this.shennianSessionId=s.sessionId??null,this.externalChannel=s.externalChannel??null,this.extraEnv=s.env??{}}async send(s,t,r,i,e){this.runId=o(),this.seq=0;const n=`${m(this.workDir??process.cwd(),this.externalChannel,this.shennianSessionId??void 0,e)}${s}`;this.mode==="stdio"?await this.sendStdio(n,t):await this.runSpawn(n,t)}async resume(s){if(this.mode==="stdio"){const t=JSON.stringify({method:"resume",id:o(),params:{sessionId:this.sessionId,agentSessionId:s}});this.stdioProcess?.stdin?.write(t+`
2
- `)}}async stop(){this.mode==="stdio"?await this.stopStdioProcess():await this.killSpawnProcess()}async runSpawn(s,t){await this.killSpawnProcess();const r=this.startDispatchGuard();this.dispatchObservedEvent=!1;const i=["/run","--workdir",this.workDir??process.cwd()];this.sessionId&&i.push("--session",this.sessionId),this.agentSessionId&&i.push("--resume",this.agentSessionId),t&&i.push("--model",t);const e=c(this.command,i,{cwd:this.workDir??void 0,stdio:["pipe","pipe","pipe"],env:h(this.extraEnv)});this.spawnProcess=e,this.attachOutputHandlers(e,()=>{this.spawnProcess===e&&(this.spawnProcess=null)}),e.stdin?.once("error",n=>this.rejectDispatch(n)),e.stdin?.write(s),e.stdin?.end(),await r.promise}async killSpawnProcess(){const s=this.spawnProcess;s&&(this.spawnProcess=null,s.kill("SIGTERM"),await new Promise(t=>{s.on("close",t),setTimeout(()=>{s.kill("SIGKILL"),t()},3e3)}))}async startStdioProcess(){const s=c(this.command,["/start","--workdir",this.workDir??process.cwd()],{cwd:this.workDir??void 0,stdio:["pipe","pipe","pipe"],env:h(this.extraEnv)});this.stdioProcess=s,this.attachOutputHandlers(s,()=>{this.stdioProcess===s&&(this.stdioProcess=null,this.emit("error",new Error("Custom agent process exited unexpectedly")))})}async sendStdio(s,t){this.stdioProcess||await this.startStdioProcess();const r=this.startDispatchGuard();this.dispatchObservedEvent=!1;const i=JSON.stringify({method:"send",id:o(),params:{sessionId:this.sessionId,text:s,modelId:t}});this.stdioProcess?.stdin?.once("error",e=>this.rejectDispatch(e)),this.stdioProcess?.stdin?.write(i+`
3
- `),await r.promise}async stopStdioProcess(){const s=this.stdioProcess;if(!s)return;const t=JSON.stringify({method:"stop",id:o()});s.stdin?.write(t+`
4
- `),await new Promise(r=>{const i=setTimeout(()=>{s.kill("SIGTERM"),setTimeout(()=>{s.kill("SIGKILL"),r()},3e3)},5e3);s.on("close",()=>{clearTimeout(i),r()})}),this.stdioProcess=null}attachOutputHandlers(s,t){d({input:s.stdout}).on("line",e=>{if(e.trim())try{const n=JSON.parse(e);this.handleProtocolEvent(n)}catch{}});let i="";s.stderr?.on("data",e=>{i+=e.toString()}),s.on("close",e=>{if(t(),e!==0&&e!==null){const n=i.trim()||`Agent exited with code ${e}`;this.rejectDispatch(new Error(n)),this.emitEvent({state:"error",message:n})}else this.dispatchObservedEvent?this.acceptDispatch():this.rejectDispatch(new Error("Custom agent exited without protocol events"))}),s.on("error",e=>{t(),this.rejectDispatch(e),this.emit("error",e)})}handleProtocolEvent(s){const{state:t}=s;if(t)switch(this.dispatchObservedEvent=!0,t){case"delta":this.acceptDispatch(),this.emitEvent({state:"delta",text:s.text??"",thinking:s.thinking});break;case"final":this.acceptDispatch(),s.agentSessionId&&(this.agentSessionId=s.agentSessionId),this.emitEvent({state:"final",usage:s.usage,agentSessionId:this.agentSessionId??void 0});break;case"error":this.rejectDispatch(new Error(s.message??"unknown error")),this.emitEvent({state:"error",message:s.message??"unknown error"});break;case"tool-call":this.acceptDispatch(),this.emitEvent({state:"tool-call",name:s.name??"",args:s.args});break;case"tool-result":this.acceptDispatch(),this.emitEvent({state:"tool-result",name:s.name??"",result:s.result??""});break;case"notify":this.acceptDispatch(),this.emitEvent({state:"notify",text:s.text??"",title:s.title,source:s.source});break}}startDispatchGuard(){this.acceptDispatch();let s;const t=new Promise((r,i)=>{s={settled:!1,timer:setTimeout(()=>this.acceptDispatch(s),u),resolve:r,reject:i,promise:Promise.resolve()}});return s.promise=t,this.dispatchGuard=s,s}acceptDispatch(s=this.dispatchGuard){!s||s.settled||(s.settled=!0,clearTimeout(s.timer),this.dispatchGuard===s&&(this.dispatchGuard=null),s.resolve())}rejectDispatch(s,t=this.dispatchGuard){!t||t.settled||(t.settled=!0,clearTimeout(t.timer),this.dispatchGuard===t&&(this.dispatchGuard=null),t.reject(s))}emitEvent(s){const t={...s,runId:this.runId,seq:this.seq++};this.emit("agentEvent",t)}}function E(a,s){const t=`custom:${a}`;p(t,()=>new w(a,s))}export{w as CustomAgentAdapter,E as registerCustomAgent};
1
+ import{createInterface as d}from"node:readline";import{createHash as l,randomUUID as o}from"node:crypto";import{AgentAdapter as p,registerAgent as u}from"./adapter.js";import{spawnCommandString as h}from"./command-spec.js";import{buildAgentProcessEnv as m,buildManagedAgentProcessEnv as g}from"../agent-env.js";import{buildInlineChannelPreamble as f}from"./platform-instructions.js";import{collectPolicyEnforcementGaps as w}from"../security/managed-agent-runtime.js";import{assessCustomRuntimeSecurity as P,createCustomManagedPolicyFile as y,CUSTOM_RUNTIME_POLICY_ENV as v,disposeCustomManagedPolicyFile as D,registerCustomRuntimeSecurity as S,resolveCustomRuntimeExecutableFingerprint as E}from"./custom-managed-runtime.js";const C=3e3;class I extends p{type;command;mode;proactive;entry;sessionId=null;agentSessionId=null;workDir=null;runId="";seq=0;externalChannel=null;shennianSessionId=null;extraEnv={};managedAgentPolicy=null;managedConfigDir=null;configurationRestartRequired=!1;stoppingStdioProcess=!1;stdioProcess=null;spawnProcess=null;dispatchGuard=null;dispatchObservedEvent=!1;constructor(t,s){super(),this.type=`custom:${t}`,this.entry=s,this.command=s.command,this.mode=s.caps.mode,this.proactive=s.caps.proactive??!1}async start(t,s,i){this.sessionId=t,this.workDir=this.managedAgentPolicy?.adapterPolicy.workDir??s,this.seq=0,i&&(this.agentSessionId=i),this.mode==="stdio"&&await this.startStdioProcess()}configure(t){const s=this.configurationFingerprint();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),s!==this.configurationFingerprint()&&(this.stdioProcess||this.spawnProcess)&&(this.configurationRestartRequired=!0)}async send(t,s,i,n,e,a){if(a?.source==="room_activation"&&!this.managedAgentPolicy)throw new Error("managed_agent_policy_missing");this.configurationRestartRequired&&(this.mode==="stdio"?await this.stopStdioProcess():await this.killSpawnProcess(),this.configurationRestartRequired=!1),this.runId=o(),this.seq=0;const r=`${f(this.workDir??process.cwd(),this.externalChannel,this.shennianSessionId??void 0,e)}${t}`;this.mode==="stdio"?await this.sendStdio(r,s):await this.runSpawn(r,s)}async resume(t){if(this.mode==="stdio"){const s=JSON.stringify({method:"resume",id:o(),params:{sessionId:this.sessionId,agentSessionId:t}});this.stdioProcess?.stdin?.write(s+`
2
+ `)}}async stop(){this.mode==="stdio"?await this.stopStdioProcess():await this.killSpawnProcess()}async runSpawn(t,s){await this.killSpawnProcess();const i=this.startDispatchGuard();this.dispatchObservedEvent=!1;const n=["/run","--workdir",this.workDir??process.cwd()];this.sessionId&&n.push("--session",this.sessionId),this.agentSessionId&&n.push("--resume",this.agentSessionId),s&&n.push("--model",s);const e=await this.prepareManagedLaunch();let a;try{a=h(this.command,n,{cwd:this.workDir??void 0,stdio:["pipe","pipe","pipe"],env:e.env})}catch(r){throw await this.disposeManagedConfig(e.configDir),r}this.spawnProcess=a,this.managedConfigDir=e.configDir,this.attachOutputHandlers(a,()=>{this.spawnProcess===a&&(this.spawnProcess=null),this.disposeManagedConfig(e.configDir)}),a.stdin?.once("error",r=>this.rejectDispatch(r)),a.stdin?.write(t),a.stdin?.end(),await i.promise}async killSpawnProcess(){const t=this.spawnProcess;if(!t){await this.disposeManagedConfig(this.managedConfigDir);return}this.spawnProcess=null,t.kill("SIGTERM"),await new Promise(s=>{t.on("close",s),setTimeout(()=>{t.kill("SIGKILL"),s()},3e3)}),await this.disposeManagedConfig(this.managedConfigDir)}async startStdioProcess(){const t=await this.prepareManagedLaunch();let s;try{s=h(this.command,["/start","--workdir",this.workDir??process.cwd()],{cwd:this.workDir??void 0,stdio:["pipe","pipe","pipe"],env:t.env})}catch(i){throw await this.disposeManagedConfig(t.configDir),i}this.stdioProcess=s,this.managedConfigDir=t.configDir,this.attachOutputHandlers(s,()=>{this.stdioProcess===s&&(this.stdioProcess=null,this.disposeManagedConfig(t.configDir),this.stoppingStdioProcess||this.emit("error",new Error("Custom agent process exited unexpectedly")))})}async sendStdio(t,s){this.stdioProcess||await this.startStdioProcess();const i=this.startDispatchGuard();this.dispatchObservedEvent=!1;const n=JSON.stringify({method:"send",id:o(),params:{sessionId:this.sessionId,text:t,modelId:s}});this.stdioProcess?.stdin?.once("error",e=>this.rejectDispatch(e)),this.stdioProcess?.stdin?.write(n+`
3
+ `),await i.promise}async stopStdioProcess(){const t=this.stdioProcess;if(!t){await this.disposeManagedConfig(this.managedConfigDir);return}this.stoppingStdioProcess=!0;const s=new Promise(n=>{const e=setTimeout(()=>{t.kill("SIGTERM"),setTimeout(()=>{t.kill("SIGKILL"),n()},3e3)},5e3);t.on("close",()=>{clearTimeout(e),n()})}),i=JSON.stringify({method:"stop",id:o()});t.stdin?.write(i+`
4
+ `),await s,this.stdioProcess=null,await this.disposeManagedConfig(this.managedConfigDir),this.stoppingStdioProcess=!1}async prepareManagedLaunch(){const t=this.managedAgentPolicy;if(!t)return{env:m(this.extraEnv),configDir:null};const s=E(this.entry);if(!t.enforcement.executableFingerprint||s!==t.enforcement.executableFingerprint)throw new Error("custom_managed_runtime_executable_changed");if(t.enforcement.level!=="unverified")throw new Error("custom_managed_runtime_protection_must_be_unverified");const i=P(this.entry);if(i.state==="declared"&&t.enforcement.runtimeVersion!==this.entry.caps.version)throw new Error("custom_managed_runtime_version_changed");if(i.state==="declared"){const a=i.declaration.platforms[t.enforcement.platform],r=w(t.requestedPolicy,a??null);if(r.unavailableCapabilities.length>0)throw new Error(`custom_managed_runtime_capability_unavailable:${r.unavailableCapabilities.join(",")}`)}const n=g({agentType:this.type,providerEnv:this.extraEnv,sessionEnv:this.extraEnv,runtimeEnv:{NO_COLOR:"1"}});if(i.state==="unverified")return{env:n,configDir:null};const e=await y(t);return n[v]=e.policyPath,{env:n,configDir:e.directory}}async disposeManagedConfig(t){t&&(this.managedConfigDir===t&&(this.managedConfigDir=null),await D(t).catch(()=>{}))}configurationFingerprint(){const t=Object.keys(this.extraEnv).sort().map(s=>[s,this.extraEnv[s]??null]);return l("sha256").update(JSON.stringify({sessionId:this.shennianSessionId,externalChannel:this.externalChannel,env:t,managedAgentPolicy:this.managedAgentPolicy})).digest("hex")}attachOutputHandlers(t,s){d({input:t.stdout}).on("line",e=>{if(e.trim())try{const a=JSON.parse(e);this.handleProtocolEvent(a)}catch{}});let n="";t.stderr?.on("data",e=>{n+=e.toString()}),t.on("close",e=>{if(s(),e!==0&&e!==null){const a=n.trim()||`Agent exited with code ${e}`;this.rejectDispatch(new Error(a)),this.emitEvent({state:"error",message:a})}else this.dispatchObservedEvent?this.acceptDispatch():this.rejectDispatch(new Error("Custom agent exited without protocol events"))}),t.on("error",e=>{s(),this.rejectDispatch(e),this.emit("error",e)})}handleProtocolEvent(t){const{state:s}=t;if(s)switch(this.dispatchObservedEvent=!0,s){case"delta":this.acceptDispatch(),this.emitEvent({state:"delta",text:t.text??"",thinking:t.thinking});break;case"final":this.acceptDispatch(),t.agentSessionId&&(this.agentSessionId=t.agentSessionId),this.emitEvent({state:"final",usage:t.usage,agentSessionId:this.agentSessionId??void 0});break;case"error":this.rejectDispatch(new Error(t.message??"unknown error")),this.emitEvent({state:"error",message:t.message??"unknown error"});break;case"tool-call":this.acceptDispatch(),this.emitEvent({state:"tool-call",name:t.name??"",args:t.args});break;case"tool-result":this.acceptDispatch(),this.emitEvent({state:"tool-result",name:t.name??"",result:t.result??""});break;case"notify":this.acceptDispatch(),this.emitEvent({state:"notify",text:t.text??"",title:t.title,source:t.source});break}}startDispatchGuard(){this.acceptDispatch();let t;const s=new Promise((i,n)=>{t={settled:!1,timer:setTimeout(()=>this.acceptDispatch(t),C),resolve:i,reject:n,promise:Promise.resolve()}});return t.promise=s,this.dispatchGuard=t,t}acceptDispatch(t=this.dispatchGuard){!t||t.settled||(t.settled=!0,clearTimeout(t.timer),this.dispatchGuard===t&&(this.dispatchGuard=null),t.resolve())}rejectDispatch(t,s=this.dispatchGuard){!s||s.settled||(s.settled=!0,clearTimeout(s.timer),this.dispatchGuard===s&&(this.dispatchGuard=null),s.reject(t))}emitEvent(t){const s={...t,runId:this.runId,seq:this.seq++};this.emit("agentEvent",s)}}function R(c,t){const s=`custom:${c}`;S(s,t),u(s,()=>new I(c,t))}export{I as CustomAgentAdapter,R as registerCustomAgent};
@@ -4,6 +4,9 @@ export type DetectedAgent = {
4
4
  command: string;
5
5
  args: string[];
6
6
  version?: string;
7
+ runtimePlatform?: NodeJS.Platform;
8
+ runtimeIsWsl?: boolean;
9
+ executableFingerprint?: string;
7
10
  models?: ModelInfo[];
8
11
  };
9
12
  export declare function detectAgents(): DetectedAgent[];
@@ -1 +1 @@
1
- import{AVAILABLE_BUILTIN_AGENT_TYPES as s}from"@shennian/wire";import{loadConfig as r}from"../config/index.js";import{getCommandVersion as c,resolveBuiltinCommand as d}from"./command-spec.js";const i=s.filter(o=>o!=="pi");function l(o){const n=o.models?.length?o.models:o.model?[o.model]:[],t=o.defaultModel??o.model;return n.map(e=>({id:e,name:e,isDefault:e===t}))}function g(){const o=[];for(const t of i){const e=d(t);if(!e)continue;const m=c(e);o.push({type:t,command:e.command,args:e.args,version:m})}const n=r();for(const[t,e]of Object.entries(n.customAgents??{}))o.push({type:`custom:${t}`,command:e.command,args:[],version:e.caps.version,models:l(e.caps)});return o}export{g as detectAgents};
1
+ import{AVAILABLE_BUILTIN_AGENT_TYPES as m}from"@shennian/wire";import{loadConfig as i}from"../config/index.js";import{getBuiltinAgentVersion as l,resolveBuiltinCommand as c}from"./command-spec.js";import{registerCustomRuntimeSecurity as u,resolveCustomRuntimeExecutableFingerprint as a}from"./custom-managed-runtime.js";import{resolveManagedAgentExecutableIdentity as f}from"../security/managed-agent-executable.js";const d=m.filter(t=>t!=="pi");function p(t){const r=t.models?.length?t.models:t.model?[t.model]:[],o=t.defaultModel??t.model;return r.map(e=>({id:e,name:e,isDefault:e===o}))}function E(){const t=[];for(const o of d){const e=c(o);if(!e)continue;const n=l(o,e),s=f(e);t.push({type:o,command:e.command,args:e.args,version:n,runtimePlatform:s.platform,runtimeIsWsl:s.isWsl,executableFingerprint:s.executableFingerprint})}const r=i();for(const[o,e]of Object.entries(r.customAgents??{})){const n=`custom:${o}`;u(n,e),t.push({type:n,command:e.command,args:[],version:e.caps.version,runtimePlatform:process.platform,runtimeIsWsl:process.platform==="linux"&&!!(process.env.WSL_DISTRO_NAME||process.env.WSL_INTEROP),executableFingerprint:a(e),models:p(e.caps)})}return t}export{E as detectAgents};
@@ -1 +1 @@
1
- import{getAgentConfigSummary as l}from"../config-status.js";import{isCacheFresh as g,readCache as f,writeCache as m}from"./cache.js";import{discoverModelsForAgent as A}from"./discovery.js";import{getAgentRuntimeSummary as y}from"../runtime-compatibility.js";function v(t){const s=new Set,e=[];for(const o of t)!o.id||s.has(o.id)||(s.add(o.id),e.push(o));return e}function h(t){return{supportsManagedAgent:t.type!=="pi"&&t.type!=="openclaw",supportsWorker:t.type!=="pi"&&t.type!=="openclaw",supportsManagerMode:t.type==="codex"||t.type==="claude"}}function I(t){const s=f();return t.map(e=>{const o=l(e.type),r=y(e);return{type:e.type,models:s.agents[e.type]?.models??e.models??[],...h(e),...o?{config:o}:{},...r?{runtime:r}:{}}})}async function q(t,s){const e=f();for(const o of t)delete e.agents[o.type];return m(e),w(t,s)}async function w(t,s){const e=f();let o=!1;const r=[];for(const n of t){const c=e.agents[n.type];let i;if(g(c,n.version))i=c.models;else{let p=null;try{p=v(await A(n,s)),i=p.length>0?p:c?.models??n.models??[]}catch{i=c?.models??n.models??[]}if(p&&p.length>0){const a={version:n.version,refreshedAt:Date.now(),models:i};JSON.stringify(c)!==JSON.stringify(a)&&(e.agents[n.type]=a,o=!0)}}const u=l(n.type),d=y(n);r.push({type:n.type,models:i,...h(n),...u?{config:u}:{},...d?{runtime:d}:{}})}return o&&m(e),r}export{I as getCachedAgentInfos,q as refreshAgentInfos,w as resolveAgentInfos};
1
+ import{getAgentConfigSummary as m}from"../config-status.js";import{isCacheFresh as h,readCache as a,writeCache as f}from"./cache.js";import{discoverModelsForAgent as A}from"./discovery.js";import{getAgentRuntimeSummary as y}from"../runtime-compatibility.js";import{resolveManagedAgentRuntimeProtection as v}from"../../security/managed-agent-runtime.js";function x(e){const r=new Set,t=[];for(const n of e)!n.id||r.has(n.id)||(r.add(n.id),t.push(n));return t}function g(e){const t=e.type!=="pi"&&e.type!=="openclaw"?v({agentType:e.type,runtimeVersion:e.version,platform:e.runtimePlatform,isWsl:e.runtimeIsWsl,executableFingerprint:e.executableFingerprint,requireExecutableFingerprint:!0}):null;return{supportsManagedAgent:t?.level==="hard",supportsWorker:e.type!=="pi"&&e.type!=="openclaw",supportsManagerMode:e.type==="codex"||e.type==="claude",...t?{managedAgentProtection:t}:{}}}function I(e){const r=a();return e.map(t=>{const n=m(t.type),s=y(t);return{type:t.type,models:r.agents[t.type]?.models??t.models??[],...g(t),...n?{config:n}:{},...s?{runtime:s}:{}}})}async function q(e,r){const t=a();for(const n of e)delete t.agents[n.type];return f(t),C(e,r)}async function C(e,r){const t=a();let n=!1;const s=[];for(const o of e){const i=t.agents[o.type];let c;if(h(i,o.version))c=i.models;else{let p=null;try{p=x(await A(o,r)),c=p.length>0?p:i?.models??o.models??[]}catch{c=i?.models??o.models??[]}if(p&&p.length>0){const d={version:o.version,refreshedAt:Date.now(),models:c};JSON.stringify(i)!==JSON.stringify(d)&&(t.agents[o.type]=d,n=!0)}}const u=m(o.type),l=y(o);s.push({type:o.type,models:c,...g(o),...u?{config:u}:{},...l?{runtime:l}:{}})}return n&&f(t),s}export{I as getCachedAgentInfos,q as refreshAgentInfos,C as resolveAgentInfos};
@@ -0,0 +1,25 @@
1
+ import type { ManagedAgentEnforcement } from '@shennian/wire';
2
+ import type { ManagedAgentRuntimeSecurityDeclaration } from '../security/managed-agent-runtime.js';
3
+ type JsonObject = Record<string, unknown>;
4
+ /**
5
+ * Evidence is intentionally limited to the locally exercised 1.18 release
6
+ * line. The declaration is partial: it describes tool dispatch checks, not an
7
+ * OS sandbox. Shell execution is disabled rather than presented as isolated.
8
+ */
9
+ export declare const OPENCODE_MANAGED_RUNTIME_SECURITY: ManagedAgentRuntimeSecurityDeclaration;
10
+ export type OpenCodeManagedPermissionConfig = {
11
+ args: string[];
12
+ config: JsonObject;
13
+ };
14
+ export type OpenCodeManagedConfigDirectory = {
15
+ directory: string;
16
+ configPath: string;
17
+ };
18
+ /**
19
+ * Compiles the effective policy into an invocation-scoped OpenCode config.
20
+ * The global deny rule is deliberate: unknown/custom tools stay disabled.
21
+ */
22
+ export declare function compileOpenCodeManagedPermissions(enforcement: ManagedAgentEnforcement, resolvedWorkDir: string): OpenCodeManagedPermissionConfig;
23
+ export declare function createOpenCodeManagedConfigDirectory(config: OpenCodeManagedPermissionConfig): Promise<OpenCodeManagedConfigDirectory>;
24
+ export declare function disposeOpenCodeManagedConfigDirectory(directory: string): Promise<void>;
25
+ export {};
@@ -0,0 +1,2 @@
1
+ import p from"node:fs";import r from"node:fs/promises";import c from"node:os";import n from"node:path";import{resolveProtectedShennianDirectories as w}from"../security/managed-agent-credential-paths.js";const d="shennian-opencode-managed-",u=[".env",".env.*","**/.env","**/.env.*","*.key","**/*.key","*.pem","**/*.pem","~/.ssh/**","$HOME/.ssh/**","~/.aws/**","$HOME/.aws/**","~/.azure/**","$HOME/.azure/**","~/.config/gcloud/**","$HOME/.config/gcloud/**","~/.netrc","$HOME/.netrc","~/.npmrc","$HOME/.npmrc","~/.shennian/**","$HOME/.shennian/**"],f={commandNetwork:{allow:!1,deny:!0},webSearch:{allow:!0,deny:!0},browser:{allow:!1,deny:!0},computerUse:{allow:!1,deny:!0},mcp:{allow:!1,deny:!0}},C={minimumRuntimeVersion:"1.18.0",maximumRuntimeVersionExclusive:"1.19.0",platforms:Object.fromEntries(["darwin","linux","wsl","win32"].map(a=>[a,{level:"partial",fileAccess:{"workspace-read":!0,"workspace-write":!0,"host-full":!0},externalCapabilities:f,knownGaps:["sandbox.toolPermissionOnly","shell.unavailableInManagedMode","config.globalAndAdministratorSourcesMayRemain"]}]))};function k(a,e){b(e);const o=a.effectivePolicy;if(o.externalCapabilities.commandNetwork)throw new Error("opencode_managed_command_network_unavailable");if(o.externalCapabilities.browser||o.externalCapabilities.computerUse)throw new Error("opencode_managed_interactive_tools_unavailable");if(o.externalCapabilities.mcp)throw new Error("opencode_managed_mcp_allowlist_unavailable");const t=o.fileAccess==="host-full",m=o.fileAccess==="workspace-write",i=g(),s={"*":"deny",read:l("allow",i),glob:"allow",grep:"allow",list:"allow",edit:m||t?l("allow",i):"deny",external_directory:t?l("allow",i):"deny",bash:"deny",task:"deny",skill:"deny",question:"allow",todoread:"allow",todowrite:"allow",webfetch:o.externalCapabilities.webSearch?"allow":"deny",websearch:o.externalCapabilities.webSearch?"allow":"deny",codesearch:o.externalCapabilities.webSearch?"allow":"deny"};return{args:["--pure","--auto"],config:{$schema:"https://opencode.ai/config.json",default_agent:"build",share:"disabled",subagent_depth:0,autoupdate:!1,formatter:!1,lsp:!1,permission:s,agent:{build:{permission:s}},plugin:[],mcp:{},command:{},instructions:[]}}}async function M(a){const e=await r.mkdtemp(n.join(c.tmpdir(),d)),o=n.join(e,"opencode.json");try{return await r.chmod(e,448),await r.writeFile(o,`${JSON.stringify(a.config)}
2
+ `,{encoding:"utf8",mode:384,flag:"wx"}),process.platform!=="win32"&&await r.chmod(o,384),{directory:e,configPath:o}}catch(t){throw await h(e).catch(()=>{}),new Error("opencode_managed_config_initialization_failed",{cause:t})}}async function h(a){const e=n.resolve(a),o=n.resolve(c.tmpdir());if(n.dirname(e)!==o||!n.basename(e).startsWith(d))throw new Error("opencode_managed_config_cleanup_refused");await r.rm(e,{recursive:!0,force:!0})}function l(a,e){return{"*":a,...Object.fromEntries(e.map(o=>[o,"deny"]))}}function _(a){return a.replaceAll("\\","/")}function g(){const a=w().map(e=>`${_(n.normalize(e))}/**`);return[...new Set([...u,...a])]}function b(a){const e=n.normalize(a.trim());if(!e||!n.isAbsolute(e))throw new Error("opencode_managed_workdir_invalid");let o;try{o=p.realpathSync(e)}catch{throw new Error("opencode_managed_workdir_unavailable")}if(n.normalize(o)!==e)throw new Error("opencode_managed_workdir_not_realpath");return e}export{C as OPENCODE_MANAGED_RUNTIME_SECURITY,k as compileOpenCodeManagedPermissions,M as createOpenCodeManagedConfigDirectory,h as disposeOpenCodeManagedConfigDirectory};
@@ -1,5 +1,6 @@
1
- import { AgentAdapter } from './adapter.js';
1
+ import { AgentAdapter, type AgentSendContext } from './adapter.js';
2
2
  import type { ChatAttachmentMeta, ExternalChannelSessionStatus } from '@shennian/wire';
3
+ import { type CompiledManagedAgentPolicy } from '../security/managed-agent-policy.js';
3
4
  export declare class OpenCodeAdapter extends AgentAdapter {
4
5
  readonly type: "opencode";
5
6
  private process;
@@ -11,17 +12,22 @@ export declare class OpenCodeAdapter extends AgentAdapter {
11
12
  private externalChannel;
12
13
  private shennianSessionId;
13
14
  private extraEnv;
15
+ private managedAgentPolicy;
16
+ private managedConfigDir;
14
17
  configure(options: {
15
18
  sessionId?: string;
16
19
  externalChannel?: ExternalChannelSessionStatus | null;
17
20
  env?: NodeJS.ProcessEnv;
21
+ managedAgentPolicy?: CompiledManagedAgentPolicy | null;
18
22
  }): void;
19
23
  start(_sessionId: string, workDir: string, agentSessionId?: string | null): Promise<void>;
20
- send(text: string, modelId?: string, _reasoningEffort?: string, _attachments?: ChatAttachmentMeta[], agentSystemPrompt?: string | null): Promise<void>;
24
+ send(text: string, modelId?: string, _reasoningEffort?: string, _attachments?: ChatAttachmentMeta[], agentSystemPrompt?: string | null, context?: AgentSendContext): Promise<void>;
21
25
  resume(agentSessionId: string): Promise<void>;
22
26
  stop(): Promise<void>;
23
27
  private resetRunState;
24
28
  private spawnAndParse;
29
+ private prepareManagedLaunch;
30
+ private disposeManagedConfig;
25
31
  private handleStreamEvent;
26
32
  private emitEvent;
27
33
  private emitFinalIfOpen;
@@ -1 +1 @@
1
- import{createInterface as h}from"node:readline";import{randomUUID as a}from"node:crypto";import{AgentAdapter as d,registerAgent as f}from"./adapter.js";import{resolveBuiltinCommand as m,spawnAgentCommand as c}from"./command-spec.js";import{buildAgentProcessEnv as g}from"../agent-env.js";import{buildInlineChannelPreamble as I}from"./platform-instructions.js";function S(s){if(!s)return;const t=s.input,e=s.output;if(!(typeof t!="number"&&typeof e!="number"))return{inputTokens:typeof t=="number"?t:0,outputTokens:typeof e=="number"?e:0}}function l(s){if(s==null)return"";if(typeof s=="string")return s;try{return JSON.stringify(s)}catch{return String(s)}}function u(s){if(!s)return"opencode request failed";if(typeof s=="string")return s;if(typeof s!="object")return String(s);const t=s;return typeof t.message=="string"?t.message:typeof t.name=="string"?t.name:"data"in t?u(t.data):l(s)||"opencode request failed"}class E extends d{type="opencode";process=null;agentSessionId=null;workDir=null;seq=0;runId="";terminalState="closed";externalChannel=null;shennianSessionId=null;extraEnv={};configure(t){this.shennianSessionId=t.sessionId??null,this.externalChannel=t.externalChannel??null,this.extraEnv=t.env??{}}async start(t,e,i){this.workDir=e,i&&(this.agentSessionId=i),this.resetRunState()}async send(t,e,i,p,o){await this.killProcess(),this.runId=a(),this.resetRunState();const n=["run","--format","json","--dangerously-skip-permissions","--dir",this.workDir??process.cwd()];this.agentSessionId&&n.push("--session",this.agentSessionId),e&&n.push("--model",e);const r=`${I(this.workDir??process.cwd(),this.externalChannel,this.shennianSessionId??void 0,o)}${t}`;n.push(r),this.spawnAndParse(n)}async resume(t){await this.killProcess(),this.agentSessionId=t,this.runId=a(),this.resetRunState(),this.emitEvent({state:"start",agentSessionId:t}),this.emitFinalIfOpen({state:"final",agentSessionId:t})}async stop(){await this.killProcess()}resetRunState(){this.seq=0,this.terminalState="open"}spawnAndParse(t){const e=m("opencode");if(!e){this.emit("error",new Error('Command "opencode" not found. Install it with "npm i -g opencode-ai@latest".'));return}const i=c(e,t,{cwd:this.workDir??void 0,stdio:["ignore","pipe","pipe"],env:g({NO_COLOR:"1",...this.extraEnv})});this.process=i,h({input:i.stdout}).on("line",n=>{if(n.trim())try{const r=JSON.parse(n);this.handleStreamEvent(r)}catch{this.emitEvent({state:"delta",text:n})}});let o="";i.stderr?.on("data",n=>{o+=n.toString()}),i.on("close",n=>{if(this.process!==i||(this.process=null,this.terminalState!=="open"))return;const r=o.trim();if(n!==0&&n!==null){this.emitErrorIfOpen({state:"error",message:r||`opencode exited with code ${n}`});return}this.emitFinalIfOpen({state:"final",agentSessionId:this.agentSessionId??void 0})}),i.on("error",n=>{this.process===i&&(this.process=null,n.code==="ENOENT"?this.emit("error",new Error('Command "opencode" not found. Install it with "npm i -g opencode-ai@latest".')):this.emit("error",n))})}handleStreamEvent(t){t.sessionID&&t.sessionID!==this.agentSessionId&&(this.agentSessionId=t.sessionID,this.emitEvent({state:"start",agentSessionId:t.sessionID}));const e=t.part;if(t.type==="text"&&e?.text){this.emitEvent({state:"delta",text:e.text});return}if(t.type==="reasoning"&&e?.text){this.emitEvent({state:"delta",text:e.text,thinking:!0});return}if(t.type==="tool_use"&&e?.type==="tool"){e.state?.status==="completed"||e.state?.status==="error"?this.emitEvent({state:"tool-result",name:e.tool??"tool",result:e.state.status==="error"?e.state.error??"tool failed":l(e.state?.output)}):this.emitEvent({state:"tool-call",name:e.tool??"tool",args:e.state?.input??{}});return}if(t.type==="error"){this.emitErrorIfOpen({state:"error",message:u(t.error)});return}t.type==="step_finish"&&this.emitFinalIfOpen({state:"final",agentSessionId:this.agentSessionId??t.sessionID,usage:S(e?.tokens)})}emitEvent(t){const e={...t,runId:this.runId,seq:this.seq++};this.emit("agentEvent",e)}emitFinalIfOpen(t){this.terminalState==="open"&&(this.terminalState="closed",this.emitEvent(t))}emitErrorIfOpen(t){this.terminalState==="open"&&(this.terminalState="closed",this.emitEvent(t))}async killProcess(){const t=this.process;t&&(this.process=null,t.kill("SIGTERM"),await new Promise(e=>{t.on("close",e),setTimeout(()=>{t.kill("SIGKILL"),e()},3e3)}))}}f("opencode",()=>new E);export{E as OpenCodeAdapter};
1
+ import{createInterface as h}from"node:readline";import{randomUUID as l}from"node:crypto";import{AgentAdapter as p,registerAgent as f}from"./adapter.js";import{resolveBuiltinCommand as u,spawnAgentCommand as m}from"./command-spec.js";import{buildAgentProcessEnv as E,buildManagedAgentProcessEnv as C}from"../agent-env.js";import{buildInlineChannelPreamble as I}from"./platform-instructions.js";import{resolveManagedAgentAdapterEnforcement as y}from"../security/managed-agent-policy.js";import{registerManagedAgentRuntimeSecurity as D}from"../security/managed-agent-runtime.js";import{compileOpenCodeManagedPermissions as O,createOpenCodeManagedConfigDirectory as S,disposeOpenCodeManagedConfigDirectory as w,OPENCODE_MANAGED_RUNTIME_SECURITY as P}from"./opencode-managed-permissions.js";D("opencode",P);function _(i){if(!i)return;const t=i.input,e=i.output;if(!(typeof t!="number"&&typeof e!="number"))return{inputTokens:typeof t=="number"?t:0,outputTokens:typeof e=="number"?e:0}}function c(i){if(i==null)return"";if(typeof i=="string")return i;try{return JSON.stringify(i)}catch{return String(i)}}function g(i){if(!i)return"opencode request failed";if(typeof i=="string")return i;if(typeof i!="object")return String(i);const t=i;return typeof t.message=="string"?t.message:typeof t.name=="string"?t.name:"data"in t?g(t.data):c(i)||"opencode request failed"}class A extends p{type="opencode";process=null;agentSessionId=null;workDir=null;seq=0;runId="";terminalState="closed";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,e,r){this.workDir=this.managedAgentPolicy?.adapterPolicy.workDir??e,r&&(this.agentSessionId=r),this.resetRunState()}async send(t,e,r,s,d,a){if(a?.source==="room_activation"&&!this.managedAgentPolicy)throw new Error("managed_agent_policy_missing");await this.killProcess(),this.runId=l(),this.resetRunState();const n=["run","--format","json","--dir",this.workDir??process.cwd()];this.agentSessionId&&n.push("--session",this.agentSessionId),e&&n.push("--model",e);const o=`${I(this.workDir??process.cwd(),this.externalChannel,this.shennianSessionId??void 0,d)}${t}`;n.push(o),await this.spawnAndParse(n)}async resume(t){await this.killProcess(),this.agentSessionId=t,this.runId=l(),this.resetRunState(),this.emitEvent({state:"start",agentSessionId:t}),this.emitFinalIfOpen({state:"final",agentSessionId:t})}async stop(){await this.killProcess()}resetRunState(){this.seq=0,this.terminalState="open"}async spawnAndParse(t){const e=u("opencode");if(!e){this.emit("error",new Error('Command "opencode" not found. Install it with "npm i -g opencode-ai@latest".'));return}const r=await this.prepareManagedLaunch();let s;try{s=m(e,[t[0],...r.args,...t.slice(1)],{cwd:this.workDir??void 0,stdio:["ignore","pipe","pipe"],env:r.env})}catch(n){throw await this.disposeManagedConfig(r.configDir),n}this.process=s,this.managedConfigDir=r.configDir,h({input:s.stdout}).on("line",n=>{if(n.trim())try{const o=JSON.parse(n);this.handleStreamEvent(o)}catch{this.emitEvent({state:"delta",text:n})}});let a="";s.stderr?.on("data",n=>{a+=n.toString()}),s.on("close",n=>{if(this.process!==s||(this.process=null,this.disposeManagedConfig(r.configDir),this.terminalState!=="open"))return;const o=a.trim();if(n!==0&&n!==null){this.emitErrorIfOpen({state:"error",message:o||`opencode exited with code ${n}`});return}this.emitFinalIfOpen({state:"final",agentSessionId:this.agentSessionId??void 0})}),s.on("error",n=>{this.process===s&&(this.process=null,this.disposeManagedConfig(r.configDir),n.code==="ENOENT"?this.emit("error",new Error('Command "opencode" not found. Install it with "npm i -g opencode-ai@latest".')):this.emit("error",n))})}async prepareManagedLaunch(){const t=this.managedAgentPolicy;if(!t)return{args:["--auto"],env:E({NO_COLOR:"1",...this.extraEnv}),configDir:null};const e=O(y(t),t.adapterPolicy.workDir),r=await S(e),s=C({agentType:"opencode",providerEnv:this.extraEnv,sessionEnv:this.extraEnv,runtimeEnv:{NO_COLOR:"1"}});return s.OPENCODE_CONFIG=r.configPath,s.OPENCODE_CONFIG_DIR=r.directory,s.OPENCODE_CONFIG_CONTENT=JSON.stringify(e.config),s.OPENCODE_DISABLE_PROJECT_CONFIG="true",s.OPENCODE_DISABLE_CLAUDE_CODE="1",s.OPENCODE_DISABLE_CLAUDE_CODE_PROMPT="1",s.OPENCODE_DISABLE_CLAUDE_CODE_SKILLS="1",{args:e.args,env:s,configDir:r.directory}}async disposeManagedConfig(t){t&&(this.managedConfigDir===t&&(this.managedConfigDir=null),await w(t).catch(()=>{}))}handleStreamEvent(t){t.sessionID&&t.sessionID!==this.agentSessionId&&(this.agentSessionId=t.sessionID,this.emitEvent({state:"start",agentSessionId:t.sessionID}));const e=t.part;if(t.type==="text"&&e?.text){this.emitEvent({state:"delta",text:e.text});return}if(t.type==="reasoning"&&e?.text){this.emitEvent({state:"delta",text:e.text,thinking:!0});return}if(t.type==="tool_use"&&e?.type==="tool"){e.state?.status==="completed"||e.state?.status==="error"?this.emitEvent({state:"tool-result",name:e.tool??"tool",result:e.state.status==="error"?e.state.error??"tool failed":c(e.state?.output)}):this.emitEvent({state:"tool-call",name:e.tool??"tool",args:e.state?.input??{}});return}if(t.type==="error"){this.emitErrorIfOpen({state:"error",message:g(t.error)});return}t.type==="step_finish"&&this.emitFinalIfOpen({state:"final",agentSessionId:this.agentSessionId??t.sessionID,usage:_(e?.tokens)})}emitEvent(t){const e={...t,runId:this.runId,seq:this.seq++};this.emit("agentEvent",e)}emitFinalIfOpen(t){this.terminalState==="open"&&(this.terminalState="closed",this.emitEvent(t))}emitErrorIfOpen(t){this.terminalState==="open"&&(this.terminalState="closed",this.emitEvent(t))}async killProcess(){const t=this.process;if(!t){await this.disposeManagedConfig(this.managedConfigDir);return}this.process=null,t.kill("SIGTERM"),await new Promise(e=>{t.on("close",e),setTimeout(()=>{t.kill("SIGKILL"),e()},3e3)}),await this.disposeManagedConfig(this.managedConfigDir)}}f("opencode",()=>new A);export{A as OpenCodeAdapter};
@@ -0,0 +1,37 @@
1
+ import type { ManagedAgentEnforcement } from '@shennian/wire';
2
+ import type { ManagedAgentRuntimeSecurityDeclaration } from '../security/managed-agent-runtime.js';
3
+ type JsonObject = Record<string, unknown>;
4
+ /**
5
+ * Evidence baseline: WorkBuddy / CodeBuddy 2.115.0 exposes invocation-scoped
6
+ * settings, tool allow/deny rules, strict MCP isolation, and the native Bash
7
+ * sandbox backed by Seatbelt on macOS and bubblewrap on Linux.
8
+ *
9
+ * The profile is deliberately partial. CodeBuddy's sandbox starts from
10
+ * host-readable defaults and a runtime write allowlist, so it cannot express a
11
+ * complete "only this workspace is readable" boundary. Windows has no native
12
+ * Bash/PowerShell OS sandbox. Browser and isolated opt-in MCP configuration are
13
+ * not available through the current non-interactive adapter.
14
+ */
15
+ export declare const WORKBUDDY_MANAGED_RUNTIME_SECURITY: ManagedAgentRuntimeSecurityDeclaration;
16
+ export type WorkBuddyManagedPermissionOptions = {
17
+ /** Exact local daemon socket required by the scoped managed Room CLI. */
18
+ internalUnixSockets?: readonly string[];
19
+ };
20
+ export type WorkBuddyManagedPermissionConfig = {
21
+ settings: JsonObject;
22
+ toolNames: string[];
23
+ runtimeEnv: NodeJS.ProcessEnv;
24
+ };
25
+ export type WorkBuddyManagedConfigFiles = {
26
+ directory: string;
27
+ settingsPath: string;
28
+ };
29
+ /**
30
+ * Builds one invocation-only WorkBuddy policy. User/project/local settings are
31
+ * excluded by the adapter, so their hooks, trusted allow rules, plugins, and
32
+ * sandbox exceptions cannot widen this configuration.
33
+ */
34
+ export declare function compileWorkBuddyManagedPermissions(enforcement: ManagedAgentEnforcement, resolvedWorkDir: string, options?: WorkBuddyManagedPermissionOptions): WorkBuddyManagedPermissionConfig;
35
+ export declare function createWorkBuddyManagedConfigFiles(config: WorkBuddyManagedPermissionConfig): Promise<WorkBuddyManagedConfigFiles>;
36
+ export declare function disposeWorkBuddyManagedConfigFiles(directory: string): Promise<void>;
37
+ export {};
@@ -0,0 +1,2 @@
1
+ import O from"node:fs";import u from"node:fs/promises";import A from"node:os";import o from"node:path";import{resolveProtectedShennianDirectories as R}from"../security/managed-agent-credential-paths.js";const D="shennian-workbuddy-managed-",B=["Read","Glob","Grep","LSP","Bash","TaskOutput","TaskStop"],_=["Edit","Write","MultiEdit","NotebookEdit"],E=["WebSearch","WebFetch"],b=["ComputerUse"],g=[".env",".env.*","**/.env","**/.env.*","*.key","**/*.key","*.pem","**/*.pem","~/.ssh/**","~/.aws/**","~/.azure/**","~/.config/gcloud/**","~/.netrc","~/.npmrc","~/.workbuddy/**","~/.codebuddy/**","~/.claude/**","~/.codex/**","~/.shennian/**"],w={browser:{allow:!1,deny:!0},mcp:{allow:!1,deny:!0}},z={minimumRuntimeVersion:"2.115.0",maximumRuntimeVersionExclusive:"2.143.0",platforms:{darwin:{level:"partial",fileAccess:{"workspace-read":!1,"workspace-write":!1,"host-full":!0},externalCapabilities:{commandNetwork:{allow:!0,deny:!0},webSearch:{allow:!0,deny:!0},...w,computerUse:{allow:!0,deny:!0}},knownGaps:["fileAccess.outsideWorkspaceRead","fileAccess.sandboxRuntimeWriteAllowlist","fileAccess.hostFullSensitiveFilesViaShell"]},linux:{level:"partial",fileAccess:{"workspace-read":!1,"workspace-write":!1,"host-full":!0},externalCapabilities:{commandNetwork:{allow:!0,deny:!0},webSearch:{allow:!0,deny:!0},...w,computerUse:{allow:!1,deny:!0}},knownGaps:["fileAccess.outsideWorkspaceRead","fileAccess.sandboxRuntimeWriteAllowlist","fileAccess.hostFullSensitiveFilesViaShell","internalTransport.unixSocketGranularity"]},wsl:{level:"partial",fileAccess:{"workspace-read":!1,"workspace-write":!1,"host-full":!0},externalCapabilities:{commandNetwork:{allow:!0,deny:!0},webSearch:{allow:!0,deny:!0},...w,computerUse:{allow:!1,deny:!0}},knownGaps:["fileAccess.outsideWorkspaceRead","fileAccess.sandboxRuntimeWriteAllowlist","fileAccess.hostFullSensitiveFilesViaShell","internalTransport.unixSocketGranularity"]},win32:{level:"partial",fileAccess:{"workspace-read":!1,"workspace-write":!1,"host-full":!0},externalCapabilities:{commandNetwork:{allow:!0,deny:!1},webSearch:{allow:!0,deny:!0},...w,computerUse:{allow:!1,deny:!0}},knownGaps:["sandbox.nativeWindowsUnavailable","fileAccess.shellUncontained","fileAccess.hostFullSensitiveFilesViaShell"]}}};function V(t,e,a={}){const s=P(e),r=t.effectivePolicy,i=t.platform;if(t.level==="unverified")throw new Error("workbuddy_managed_runtime_unverified");if(i!=="darwin"&&i!=="linux"&&i!=="wsl"&&i!=="win32")throw new Error("workbuddy_managed_platform_unsupported");if(r.externalCapabilities.browser)throw new Error("workbuddy_managed_browser_unavailable");if(r.externalCapabilities.mcp)throw new Error("workbuddy_managed_mcp_isolation_unavailable");if(r.externalCapabilities.computerUse&&i!=="darwin")throw new Error("workbuddy_managed_computer_use_unavailable");if(r.fileAccess==="host-full"&&!r.externalCapabilities.commandNetwork)throw new Error("workbuddy_managed_host_full_network_isolation_unavailable");const l=i!=="win32"&&r.fileAccess!=="host-full",c=r.fileAccess==="workspace-read",f=T(),h=[...B,...c?[]:_,...r.externalCapabilities.webSearch?E:[],...r.externalCapabilities.computerUse?b:[]],S=m(s),y=["Bash",...c?[]:_.map(k=>r.fileAccess==="host-full"?k:`${k}(${S}/**)`),...r.externalCapabilities.webSearch?E:[],...r.externalCapabilities.computerUse?b:[]],C=[...c?_:[],...r.externalCapabilities.webSearch?[]:E,...r.externalCapabilities.computerUse?[]:b,"Agent","TeamCreate","Workflow","Artifact","ArtifactControl","VideoGen","Skill","SlashCommand","Browser","Chrome","mcp__*",...N(f)],x=M(a.internalUnixSockets),p=r.externalCapabilities.commandNetwork,U=[...f,...c?[s]:[]];return{settings:{permissions:{defaultMode:"dontAsk",disableBypassPermissionsMode:"disable",additionalDirectories:[],allow:n(y),ask:[],deny:n(C)},sandbox:{enabled:l,autoAllowBashIfSandboxed:!0,excludedCommands:[],allowUnsandboxedCommands:!1,enableWeakerNestedSandbox:!1,network:{allowedDomains:p?["*"]:[],deniedDomains:p?[]:["*"],allowLocalBinding:p,allowUnixSockets:i==="darwin"?x:[],allowAllUnixSockets:i==="linux"||i==="wsl"},filesystem:{denyRead:[...f],allowWrite:r.fileAccess==="workspace-write"?[s]:[],denyWrite:n(U)}}},toolNames:n(h),runtimeEnv:{CODEBUDDY_DISABLE_SHELL_SNAPSHOT:"1",CODEBUDDY_SKIP_BUILTIN_MARKETPLACE:"1",CODEBUDDY_AUTO_UPDATE_THIRD_PARTY_MARKETPLACES:"0",CODEBUDDY_DISABLE_CRON:"1",CODEBUDDY_PUSH_NOTIFICATION_ENABLED:"0",CODEBUDDY_ARTIFACT_ENABLED:"0",CODEBUDDY_SHARE_LINK_ENABLED:"0",CODEBUDDY_WAIT_FOR_MCP_SERVERS_ENABLED:"0",CODEBUDDY_DISABLE_AUTO_MEMORY:"1",CODEBUDDY_MEMORY_ENABLED:"0",CODEBUDDY_TEAM_MEMORY_ENABLED:"0",CODEBUDDY_COMPUTER_USE_ENABLED:r.externalCapabilities.computerUse?"1":"0"}}}async function H(t){const e=await u.mkdtemp(o.join(A.tmpdir(),D)),a=o.join(e,"settings.json");try{await u.chmod(e,448);const s=W(t.settings,e);return await u.writeFile(a,`${JSON.stringify(s)}
2
+ `,{encoding:"utf8",mode:384,flag:"wx"}),{directory:e,settingsPath:a}}catch(s){throw await v(e).catch(()=>{}),new Error("workbuddy_managed_config_initialization_failed",{cause:s})}}async function v(t){const e=o.resolve(t),a=o.resolve(A.tmpdir());if(o.dirname(e)!==a||!o.basename(e).startsWith(D))throw new Error("workbuddy_managed_config_cleanup_refused");await u.rm(e,{recursive:!0,force:!0})}function W(t,e){const a=JSON.parse(JSON.stringify(t)),r=a.sandbox.filesystem,i=a.permissions,l=`${m(e)}/**`;return r.denyRead=n([...r.denyRead,l]),r.denyWrite=n([...r.denyWrite,l]),i.deny=n([...i.deny,`Read(${l})`,`Edit(${l})`,`Write(${l})`]),a}function N(t){return t.flatMap(e=>[`Read(${d(e)})`,`Edit(${d(e)})`,`Write(${d(e)})`,`MultiEdit(${d(e)})`,`NotebookEdit(${d(e)})`])}function T(){const t=R().map(e=>`${m(o.normalize(e))}/**`);return n([...g,...t])}function d(t){return o.isAbsolute(t)?t.startsWith("/")?`/${t}`:`//${t}`:t}function P(t){const e=o.normalize(t.trim());if(!e||!o.isAbsolute(e))throw new Error("workbuddy_managed_workdir_invalid");let a;try{a=O.realpathSync(e)}catch{throw new Error("workbuddy_managed_workdir_unavailable")}if(o.normalize(a)!==e)throw new Error("workbuddy_managed_workdir_not_realpath");return e}function M(t){return[...new Set(t??[])].map(e=>o.normalize(e.trim())).filter(e=>e&&o.isAbsolute(e)).map(m).sort()}function m(t){return t.replace(/\\/g,"/")}function n(t){return[...new Set(t)].sort()}export{z as WORKBUDDY_MANAGED_RUNTIME_SECURITY,V as compileWorkBuddyManagedPermissions,H as createWorkBuddyManagedConfigFiles,v as disposeWorkBuddyManagedConfigFiles};
@@ -1,6 +1,7 @@
1
1
  import { type ChildProcess } from 'node:child_process';
2
2
  import type { ChatAttachmentMeta, ExternalChannelSessionStatus, SessionRunPhase } from '@shennian/wire';
3
3
  import { AgentAdapter, type AgentSendContext, type AgentStartOptions } from './adapter.js';
4
+ import { type CompiledManagedAgentPolicy } from '../security/managed-agent-policy.js';
4
5
  export declare function normalizeWorkBuddyModelId(modelId?: string | null): string | undefined;
5
6
  export declare function normalizeWorkBuddyReasoningEffort(value?: string | null): string | undefined;
6
7
  export declare function workBuddySystemPromptArgs(systemPrompt: string | null, platform: NodeJS.Platform, promptFile?: string): string[];
@@ -20,10 +21,14 @@ export declare class WorkBuddyAdapter extends AgentAdapter {
20
21
  private externalChannel;
21
22
  private shennianSessionId;
22
23
  private extraEnv;
24
+ private managedAgentPolicy;
25
+ private managedConfigDir;
26
+ private managedProcessEnv;
23
27
  configure(options: {
24
28
  sessionId?: string;
25
29
  externalChannel?: ExternalChannelSessionStatus | null;
26
30
  env?: NodeJS.ProcessEnv;
31
+ managedAgentPolicy?: CompiledManagedAgentPolicy | null;
27
32
  }): void;
28
33
  start(_sessionId: string, workDir: string, agentSessionId?: string | null, options?: AgentStartOptions): Promise<void>;
29
34
  send(text: string, modelId?: string, reasoningEffort?: string, _attachments?: ChatAttachmentMeta[], agentSystemPrompt?: string | null, context?: AgentSendContext): Promise<void>;
@@ -45,5 +50,8 @@ export declare class WorkBuddyAdapter extends AgentAdapter {
45
50
  private emitEvent;
46
51
  private resetRunState;
47
52
  private killProcess;
53
+ private prepareManagedLaunch;
54
+ private buildProcessEnv;
55
+ private disposeManagedConfig;
48
56
  }
49
57
  export declare function sanitizeWorkBuddyError(value: string): string;
@@ -1,3 +1,3 @@
1
- import{spawnSync as g}from"node:child_process";import{createInterface as y}from"node:readline";import{randomUUID as S}from"node:crypto";import w from"node:path";import{AgentAdapter as k,registerAgent as I}from"./adapter.js";import{resolveBuiltinCommand as x,spawnAgentCommand as E}from"./command-spec.js";import{buildAgentProcessEnv as _}from"../agent-env.js";import{buildPlatformInstructions as B,ensurePlatformInstructionsFile as P}from"./platform-instructions.js";import{resolveWindowsSystemRoot as T}from"../windows-shell.js";const W=new Set(["minimal","low","medium","high","xhigh","max"]);function C(s){const t=s?.trim();return t&&t!=="auto"?t:void 0}function A(s){const t=s?.trim();if(t){if(W.has(t))return t;throw new Error(`Unsupported WorkBuddy reasoning effort "${t}". Supported values: minimal, low, medium, high, xhigh, max.`)}}function O(s,t,e){if(!s)return[];if(t==="win32"){if(!e)throw new Error("WorkBuddy system prompt file is required on Windows.");return["--system-prompt-file",e]}return["--append-system-prompt",s]}function q(s){const t=s.split(/\n\n(?=<[^>\n]+>\n)/);return(t[t.length-1]??s).replace(/^\[群消息\][ \t]+<[^>\n]+>\n/,"").replace(/^\[群消息\]\n?/,"").replace(/^<[^>\n]+>\n/,"").trim()}function p(s,t=process.platform,e=R){t==="win32"&&s.pid&&e(s.pid)||s.kill("SIGTERM")}function R(s){const t=w.win32.join(T(),"System32","taskkill.exe");return g(t,["/pid",String(s),"/T","/F"],{stdio:"ignore",windowsHide:!0,timeout:5e3}).status===0}class D extends k{type="workbuddy";process=null;agentSessionId=null;workDir=null;seq=0;runId="";terminalState="open";hasEmittedText=!1;runPhase=null;persistNativeSession=!0;externalChannel=null;shennianSessionId=null;extraEnv={};configure(t){this.shennianSessionId=t.sessionId??null,this.externalChannel=t.externalChannel??null,this.extraEnv=t.env??{}}async start(t,e,n,r){this.workDir=e,this.seq=0,this.persistNativeSession=r?.persistNativeSession!==!1,n&&(this.agentSessionId=n)}async send(t,e,n,r,a,i){await this.killProcess(),this.runId=S(),this.resetRunState();const u=i?.source==="room_activation",o=["-p",u?q(t):t,"--output-format","stream-json","--verbose","--permission-mode","bypassPermissions","--setting-sources","user,project,local"];this.persistNativeSession||o.push("--no-session-persistence");const d=C(e);d&&o.push("--model",d),o.push("--tools","default"),u&&o.push("--max-turns","8");const h=this.shennianSessionId?.startsWith("room-managed-")&&!this.agentSessionId&&!u?null:B(this.workDir??process.cwd(),this.externalChannel,this.shennianSessionId??void 0,a);if(h){const c=process.platform==="win32"?P(this.workDir??process.cwd(),this.externalChannel,this.shennianSessionId??void 0,a):void 0;o.push(...O(h,process.platform,c))}const m=A(n);m&&o.push("--effort",m),this.agentSessionId&&o.push("--resume",this.agentSessionId),this.spawnAndParse(o)}async resume(t){await this.killProcess(),this.agentSessionId=t,this.resetRunState()}async stop(){await this.killProcess()}async getStatus(){return{active:this.process!=null&&this.terminalState==="open",runId:this.runId||null,runPhase:this.runPhase,canStop:this.process!=null&&this.terminalState==="open"}}spawnAndParse(t){const e=x("workbuddy");if(!e){this.emit("error",new Error("WorkBuddy was not found. Install WorkBuddy Desktop or the CodeBuddy CLI, then retry."));return}const n=E(e,t,{cwd:this.workDir??void 0,stdio:["ignore","pipe","pipe"],env:_(this.extraEnv)});this.process=n,y({input:n.stdout}).on("line",i=>{if(i.trim())try{this.handleStreamEvent(JSON.parse(i))}catch{}});let a="";n.stderr?.on("data",i=>{a=`${a}${i.toString()}`.slice(-8e3)}),n.on("close",i=>{this.process===n&&(this.process=null,this.runPhase=null,this.handleProcessClose(i,a))}),n.on("error",i=>{if(this.process!==n)return;this.process=null,this.runPhase=null;const u=i.code==="ENOENT";this.emitErrorIfOpen({state:"error",message:u?"WorkBuddy was not found. Install WorkBuddy Desktop or the CodeBuddy CLI, then retry.":l(i.message)})})}handleStreamEvent(t){if(t.type==="system"&&t.subtype==="init"&&t.session_id){this.agentSessionId=t.session_id,this.runPhase="thinking",this.emitEvent({state:"start",agentSessionId:t.session_id});return}if(t.type==="control_request"&&t.request?.subtype==="can_use_tool"){this.runPhase="waiting_approval",this.emitEvent({state:"approval-pending",name:t.request.tool_name,args:t.request.input,approval:{title:`WorkBuddy \u8BF7\u6C42\u4F7F\u7528 ${t.request.tool_name||"\u5DE5\u5177"}`,source:"workbuddy",actionHint:"\u8BF7\u5728 WorkBuddy \u6216\u795E\u5FF5\u4E2D\u786E\u8BA4\u540E\u7EE7\u7EED\u3002"}});return}if(t.type==="assistant"){const n=t.message?.content;if(n?.length)for(const r of n)this.handleContentBlock(r);else t.text&&this.emitText(t.text,!!t.thinking);return}if(t.type!=="result")return;if(t.subtype==="tool_result"){this.runPhase="tool_running",this.emitEvent({state:"tool-result",name:t.name,result:f(t.content)});return}if(t.subtype==="error"||t.error){this.emitErrorIfOpen({state:"error",message:l(t.error||t.result||"WorkBuddy failed.")});return}!this.hasEmittedText&&t.result&&this.emitText(t.result,!1),t.session_id&&(this.agentSessionId=t.session_id);const e=t.usage??t.message?.usage;this.emitFinalIfOpen({state:"final",agentSessionId:this.agentSessionId??void 0,usage:e?{inputTokens:e.input_tokens??0,outputTokens:e.output_tokens??0}:void 0})}handleContentBlock(t){t.type==="text"&&t.text?this.emitText(t.text,!1):t.type==="thinking"&&t.thinking?this.emitText(t.thinking,!0):t.type==="tool_use"?(this.runPhase="tool_running",this.emitEvent({state:"tool-call",name:t.name,args:t.input})):t.type==="tool_result"&&this.emitEvent({state:"tool-result",name:t.name,result:f(t.content)})}emitText(t,e){this.runPhase=e?"thinking":"streaming_text";const n=!e&&this.hasEmittedText?`
1
+ import{spawnSync as y}from"node:child_process";import{createInterface as w}from"node:readline";import{randomUUID as k}from"node:crypto";import E from"node:path";import{AgentAdapter as S,registerAgent as P}from"./adapter.js";import{resolveBuiltinCommand as I,spawnAgentCommand as x}from"./command-spec.js";import{buildAgentProcessEnv as _,buildManagedAgentProcessEnv as B}from"../agent-env.js";import{buildPlatformInstructions as C,ensurePlatformInstructionsFile as A}from"./platform-instructions.js";import{resolveWindowsSystemRoot as v}from"../windows-shell.js";import{resolveManagedAgentAdapterEnforcement as W}from"../security/managed-agent-policy.js";import{registerManagedAgentRuntimeSecurity as D}from"../security/managed-agent-runtime.js";import{resolveManagedRoomIpcPaths as T}from"../daemon-ipc/paths.js";import{compileWorkBuddyManagedPermissions as M,createWorkBuddyManagedConfigFiles as O,disposeWorkBuddyManagedConfigFiles as R,WORKBUDDY_MANAGED_RUNTIME_SECURITY as N}from"./workbuddy-managed-permissions.js";D("workbuddy",N);const b=new Set(["minimal","low","medium","high","xhigh","max"]);function q(n){const e=n?.trim();return e&&e!=="auto"?e:void 0}function F(n){const e=n?.trim();if(e){if(b.has(e))return e;throw new Error(`Unsupported WorkBuddy reasoning effort "${e}". Supported values: minimal, low, medium, high, xhigh, max.`)}}function U(n,e,t){if(!n)return[];if(e==="win32"){if(!t)throw new Error("WorkBuddy system prompt file is required on Windows.");return["--system-prompt-file",t]}return["--append-system-prompt",n]}function $(n){const e=n.split(/\n\n(?=<[^>\n]+>\n)/);return(e[e.length-1]??n).replace(/^\[群消息\][ \t]+<[^>\n]+>\n/,"").replace(/^\[群消息\]\n?/,"").replace(/^<[^>\n]+>\n/,"").trim()}function c(n,e=process.platform,t=z){e==="win32"&&n.pid&&t(n.pid)||n.kill("SIGTERM")}function z(n){const e=E.win32.join(v(),"System32","taskkill.exe");return y(e,["/pid",String(n),"/T","/F"],{stdio:"ignore",windowsHide:!0,timeout:5e3}).status===0}class j extends S{type="workbuddy";process=null;agentSessionId=null;workDir=null;seq=0;runId="";terminalState="open";hasEmittedText=!1;runPhase=null;persistNativeSession=!0;externalChannel=null;shennianSessionId=null;extraEnv={};managedAgentPolicy=null;managedConfigDir=null;managedProcessEnv=null;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,r){this.workDir=this.managedAgentPolicy?.adapterPolicy.workDir??t,this.seq=0,this.persistNativeSession=r?.persistNativeSession!==!1,s&&(this.agentSessionId=s)}async send(e,t,s,r,o,i){if((i?.source==="room_activation"||this.shennianSessionId?.startsWith("room-managed-")===!0)&&!this.managedAgentPolicy)throw new Error("managed_agent_policy_missing");await this.killProcess(),this.runId=k(),this.resetRunState();const d=i?.source==="room_activation",a=["-p",d?$(e):e,"--output-format","stream-json","--verbose"];this.persistNativeSession||a.push("--no-session-persistence");const h=q(t);h&&a.push("--model",h),d&&a.push("--max-turns","8");const m=this.shennianSessionId?.startsWith("room-managed-")&&!this.agentSessionId&&!d?null:C(this.workDir??process.cwd(),this.externalChannel,this.shennianSessionId??void 0,o);if(m){const f=process.platform==="win32"?A(this.workDir??process.cwd(),this.externalChannel,this.shennianSessionId??void 0,o):void 0;a.push(...U(m,process.platform,f))}const p=F(s);p&&a.push("--effort",p),this.agentSessionId&&a.push("--resume",this.agentSessionId),a.push(...await this.prepareManagedLaunch()),this.spawnAndParse(a)}async resume(e){await this.killProcess(),this.agentSessionId=e,this.resetRunState()}async stop(){await this.killProcess()}async getStatus(){return{active:this.process!=null&&this.terminalState==="open",runId:this.runId||null,runPhase:this.runPhase,canStop:this.process!=null&&this.terminalState==="open"}}spawnAndParse(e){const t=I("workbuddy");if(!t){this.disposeManagedConfig(),this.emit("error",new Error("WorkBuddy was not found. Install WorkBuddy Desktop or the CodeBuddy CLI, then retry."));return}let s;try{s=x(t,e,{cwd:this.workDir??void 0,stdio:["ignore","pipe","pipe"],env:this.buildProcessEnv()})}catch(i){throw this.disposeManagedConfig(),i}this.process=s,w({input:s.stdout}).on("line",i=>{if(i.trim())try{this.handleStreamEvent(JSON.parse(i))}catch{}});let o="";s.stderr?.on("data",i=>{o=`${o}${i.toString()}`.slice(-8e3)}),s.on("close",i=>{this.process===s&&(this.process=null,this.runPhase=null,this.disposeManagedConfig(),this.handleProcessClose(i,o))}),s.on("error",i=>{if(this.process!==s)return;this.process=null,this.runPhase=null,this.disposeManagedConfig();const l=i.code==="ENOENT";this.emitErrorIfOpen({state:"error",message:l?"WorkBuddy was not found. Install WorkBuddy Desktop or the CodeBuddy CLI, then retry.":u(i.message)})})}handleStreamEvent(e){if(e.type==="system"&&e.subtype==="init"&&e.session_id){this.agentSessionId=e.session_id,this.runPhase="thinking",this.emitEvent({state:"start",agentSessionId:e.session_id});return}if(e.type==="control_request"&&e.request?.subtype==="can_use_tool"){this.runPhase="waiting_approval",this.emitEvent({state:"approval-pending",name:e.request.tool_name,args:e.request.input,approval:{title:`WorkBuddy \u8BF7\u6C42\u4F7F\u7528 ${e.request.tool_name||"\u5DE5\u5177"}`,source:"workbuddy",actionHint:"\u8BF7\u5728 WorkBuddy \u6216\u795E\u5FF5\u4E2D\u786E\u8BA4\u540E\u7EE7\u7EED\u3002"}});return}if(e.type==="assistant"){const s=e.message?.content;if(s?.length)for(const r of s)this.handleContentBlock(r);else e.text&&this.emitText(e.text,!!e.thinking);return}if(e.type!=="result")return;if(e.subtype==="tool_result"){this.runPhase="tool_running",this.emitEvent({state:"tool-result",name:e.name,result:g(e.content)});return}if(e.subtype==="error"||e.error){this.emitErrorIfOpen({state:"error",message:u(e.error||e.result||"WorkBuddy failed.")});return}!this.hasEmittedText&&e.result&&this.emitText(e.result,!1),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??0,outputTokens:t.output_tokens??0}:void 0})}handleContentBlock(e){e.type==="text"&&e.text?this.emitText(e.text,!1):e.type==="thinking"&&e.thinking?this.emitText(e.thinking,!0):e.type==="tool_use"?(this.runPhase="tool_running",this.emitEvent({state:"tool-call",name:e.name,args:e.input})):e.type==="tool_result"&&this.emitEvent({state:"tool-result",name:e.name,result:g(e.content)})}emitText(e,t){this.runPhase=t?"thinking":"streaming_text";const s=!t&&this.hasEmittedText?`
2
2
 
3
- `:"";this.emitEvent({state:"delta",text:n+t,thinking:e||void 0}),e||(this.hasEmittedText=!0)}handleProcessClose(t,e){if(t!==0&&t!==null){this.emitErrorIfOpen({state:"error",message:l(e)||`WorkBuddy exited with code ${t}.`});return}this.emitFinalIfOpen({state:"final",agentSessionId:this.agentSessionId??void 0})}emitFinalIfOpen(t){this.terminalState==="open"&&(this.terminalState="final",this.runPhase=null,this.emitEvent(t))}emitErrorIfOpen(t){this.terminalState==="open"&&(this.terminalState="error",this.runPhase=null,this.emitEvent(t))}emitEvent(t){this.emit("agentEvent",{...t,runId:this.runId,seq:this.seq++})}resetRunState(){this.seq=0,this.terminalState="open",this.hasEmittedText=!1,this.runPhase=null}async killProcess(){const t=this.process;t&&(this.process=null,this.runPhase=null,p(t),await new Promise(e=>{let n=!1;const r=()=>{n||(n=!0,e())};t.once("close",r),setTimeout(()=>{p(t),r()},3e3).unref?.()}))}}function f(s){if(typeof s=="string")return s;try{return JSON.stringify(s)}catch{return"[unserializable WorkBuddy tool result]"}}function l(s){return s.replace(/(Authorization\s*:\s*)Bearer\s+[A-Za-z0-9._~+/-]+/gi,"$1Bearer [redacted]").replace(/(api[-_ ]?key|access[-_ ]?token|refresh[-_ ]?token|connector[-_ ]?token)(\s*[:=]\s*)[^\s,;]+/gi,"$1$2[redacted]").trim().slice(0,2e3)}I("workbuddy",()=>new D);export{D as WorkBuddyAdapter,C as normalizeWorkBuddyModelId,A as normalizeWorkBuddyReasoningEffort,l as sanitizeWorkBuddyError,p as terminateWorkBuddyProcess,q as workBuddyRoomActivationText,O as workBuddySystemPromptArgs};
3
+ `:"";this.emitEvent({state:"delta",text:s+e,thinking:t||void 0}),t||(this.hasEmittedText=!0)}handleProcessClose(e,t){if(e!==0&&e!==null){this.emitErrorIfOpen({state:"error",message:u(t)||`WorkBuddy exited with code ${e}.`});return}this.emitFinalIfOpen({state:"final",agentSessionId:this.agentSessionId??void 0})}emitFinalIfOpen(e){this.terminalState==="open"&&(this.terminalState="final",this.runPhase=null,this.emitEvent(e))}emitErrorIfOpen(e){this.terminalState==="open"&&(this.terminalState="error",this.runPhase=null,this.emitEvent(e))}emitEvent(e){this.emit("agentEvent",{...e,runId:this.runId,seq:this.seq++})}resetRunState(){this.seq=0,this.terminalState="open",this.hasEmittedText=!1,this.runPhase=null}async killProcess(){const e=this.process;if(!e){await this.disposeManagedConfig();return}this.process=null,this.runPhase=null,c(e),await new Promise(t=>{let s=!1;const r=()=>{s||(s=!0,t())};e.once("close",r),setTimeout(()=>{c(e),r()},3e3).unref?.()}),await this.disposeManagedConfig()}async prepareManagedLaunch(){const e=this.managedAgentPolicy;if(!e)return this.managedProcessEnv=null,["--permission-mode","bypassPermissions","--setting-sources","user,project,local","--tools","default"];const t=T(),s=M(W(e),e.adapterPolicy.workDir,{internalUnixSockets:t.transport==="unix_socket"?[t.endpoint]:[]}),r=await O(s);this.managedConfigDir=r.directory;const o=B({agentType:"workbuddy",providerEnv:this.extraEnv,sessionEnv:this.extraEnv,runtimeEnv:{NO_COLOR:"1"}});return Object.assign(o,s.runtimeEnv),delete o.CODEBUDDY_IS_SANDBOX,this.managedProcessEnv=o,["--permission-mode","dontAsk","--subagent-permission-mode","dontAsk","--settings",r.settingsPath,"--setting-sources","none","--strict-mcp-config","--mcp-config","{}","--tools",s.toolNames.join(",")]}buildProcessEnv(){return this.managedProcessEnv??_(this.extraEnv)}async disposeManagedConfig(){const e=this.managedConfigDir;this.managedConfigDir=null,this.managedProcessEnv=null,e&&await R(e).catch(()=>{})}}function g(n){if(typeof n=="string")return n;try{return JSON.stringify(n)}catch{return"[unserializable WorkBuddy tool result]"}}function u(n){return n.replace(/(Authorization\s*:\s*)Bearer\s+[A-Za-z0-9._~+/-]+/gi,"$1Bearer [redacted]").replace(/(api[-_ ]?key|access[-_ ]?token|refresh[-_ ]?token|connector[-_ ]?token)(\s*[:=]\s*)[^\s,;]+/gi,"$1$2[redacted]").trim().slice(0,2e3)}P("workbuddy",()=>new j);export{j as WorkBuddyAdapter,q as normalizeWorkBuddyModelId,F as normalizeWorkBuddyReasoningEffort,u as sanitizeWorkBuddyError,c as terminateWorkBuddyProcess,$ as workBuddyRoomActivationText,U as workBuddySystemPromptArgs};
@@ -41,6 +41,34 @@ export declare function resolveServiceLaunchSpec(input: {
41
41
  }): ServiceLaunchSpec;
42
42
  export declare function isRemoteAccessDisabled(): boolean;
43
43
  export declare function clearRemoteAccessDisabled(): void;
44
+ export type RemoteAccessPausePreparation = {
45
+ disabledAt: string;
46
+ autoStartDisabled: boolean;
47
+ autoStartError?: string;
48
+ };
49
+ type RemoteAccessPauseOptions = {
50
+ disabledAt?: string;
51
+ markerFile?: string;
52
+ platform?: Platform;
53
+ servicePresent?: boolean;
54
+ execCommand?: (command: string) => void;
55
+ };
56
+ export type RemoteAccessServiceStopResult = {
57
+ serviceStopped: boolean;
58
+ error?: string;
59
+ };
60
+ /**
61
+ * Engage the same durable remote-access lock used by `shennian stop` without
62
+ * terminating the current daemon. Remote pause needs the live socket long
63
+ * enough to report the committed state and flush its response.
64
+ */
65
+ export declare function prepareRemoteAccessPause(options?: RemoteAccessPauseOptions): RemoteAccessPausePreparation;
66
+ /**
67
+ * Stop the already-running platform service after the remote response has been
68
+ * flushed. Autostart is disabled during preparation; this step prevents a
69
+ * KeepAlive/Restart policy from relaunching the current daemon in the interim.
70
+ */
71
+ export declare function stopPreparedRemoteAccessService(options?: Pick<RemoteAccessPauseOptions, 'platform' | 'servicePresent' | 'execCommand'>): RemoteAccessServiceStopResult;
44
72
  export declare function getCurrentProcessDaemonLauncher(): DaemonLauncher;
45
73
  export declare function createDaemonInstanceId(): string;
46
74
  export declare function writeDaemonPid(pid: number, instanceId: string, meta?: {
@@ -60,6 +88,7 @@ export declare function getDaemonStatus(opts?: {
60
88
  }): DaemonStatus;
61
89
  export declare function buildDaemonSpawnOptions(launch: DetachedLaunchSpec, logFd: number, env?: NodeJS.ProcessEnv): Parameters<typeof spawn>[2];
62
90
  export declare function captureEnvForService(): Record<string, string>;
91
+ export declare function buildSystemdUnit(): string;
63
92
  type LaunchdCommandExecutor = (command: string) => void;
64
93
  export declare function getLaunchdServiceTarget(uid?: number | string): string;
65
94
  export declare function reloadLaunchdAgent(execCommand?: LaunchdCommandExecutor): boolean;
@@ -1,13 +1,13 @@
1
- import c from"chalk";import i from"node:fs";import s from"node:path";import u from"node:os";import{execSync as a,spawn as de}from"node:child_process";import{randomUUID as le}from"node:crypto";import{fileURLToPath as pe}from"node:url";import{getShennianDir as me,loadConfig as j,resolveShennianPath as h,saveConfig as fe}from"../config/index.js";import{buildAugmentedPath as Se}from"../env-path.js";import{buildWindowsLauncherCommand as he,buildWindowsScheduledTaskCommands as ge,buildWindowsScheduledTaskXml as we,buildWindowsStartupVbs as ye,escapeXml as T,isWindowsCmdScript as ve,quoteCmdArg as W,quoteSystemdArg as Ee,suspendWindowsScheduledTask as Ae}from"./daemon-windows.js";import{waitForDaemonReadyStatus as L}from"./daemon-start-wait.js";import{findRunningWindowsDaemonProcessIds as Pe,isShennianRunServiceCommand as Ie}from"./daemon-process.js";import{getCurrentVersion as F}from"../upgrade/engine.js";import{buildWindowsLauncherCommand as Pn,buildWindowsScheduledTaskCommands as In,buildWindowsScheduledTaskSuspendCommands as kn,buildWindowsScheduledTaskXml as Nn,buildWindowsStartupVbs as Dn,suspendWindowsScheduledTask as bn}from"./daemon-windows.js";import{isShennianRunServiceCommand as Tn}from"./daemon-process.js";const ke=s.dirname(pe(import.meta.url)),S=me(),g=h("daemon.pid"),d=h("daemon.log"),x=h("remote-access.disabled"),N=h("daemon-launcher.json"),B=s.resolve(ke,"../../bin/shennian.js"),V=process.execPath,J=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","SHENNIAN_DESKTOP_SERVER_URL","SHENNIAN_WECHAT_CHANNEL_HELPER_DIR","SHENNIAN_HOME","SHENNIAN_ACCOUNT_NAMESPACE_ID","SHENNIAN_NATIVE_FUSION_DISABLED","SHENNIAN_WECHAT_DELIVERABILITY_TRACE"]);function Ne(e){return J.has(e)}function l(){const e=u.platform();return e==="darwin"||e==="linux"||e==="win32"?e:"linux"}function O(e){const n=e.replace(/\\/g,"/").toLowerCase(),t=u.tmpdir().replace(/\\/g,"/").toLowerCase();return n.includes("/_npx/")||n.includes("/npm-cache/_npx/")||n.includes("/pnpm/dlx/")||n.startsWith(t.endsWith("/")?t:`${t}/`)}function De(e){return i.existsSync(e.scriptPath)&&!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 be(e){const n=e.trim();if(!n)return null;if(n.startsWith("{"))try{const r=JSON.parse(n),o=Number(r.pid);return!Number.isInteger(o)||o<=0?null:{pid:o,...typeof r.instanceId=="string"?{instanceId:r.instanceId}:{},...typeof r.version=="string"?{version:r.version}:{},...r.launcher==="desktop-managed"||r.launcher==="global-cli"||r.launcher==="unknown"?{launcher:r.launcher}:{},...typeof r.startedAt=="string"?{startedAt:r.startedAt}:{},...r.adopted===!0?{adopted:!0}:{}}}catch{return null}const t=parseInt(n,10);return isNaN(t)?null:{pid:t}}function A(){try{return be(i.readFileSync(g,"utf-8"))}catch{return null}}function E(e){try{return process.kill(e,0),!0}catch{return!1}}function G(e,n=3e3){const t=Date.now();for(;Date.now()-t<n;){if(!E(e))return!0;Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,100)}return!E(e)}function Re(){return i.existsSync(x)}function Te(){try{i.unlinkSync(x)}catch{}}function K(){return"global-cli"}function Le(){return`${process.pid}-${Date.now()}-${le()}`}function xe(e,n,t={}){i.mkdirSync(S,{recursive:!0}),i.writeFileSync(g,JSON.stringify({pid:e,instanceId:n,...t.version?{version:t.version}:{},launcher:t.launcher??K(),...t.adopted?{adopted:!0}:{},startedAt:new Date().toISOString()},null,2))}function yn(e,n){const t=A();if(!(t?.pid!==e||t.instanceId!==n))try{i.unlinkSync(g)}catch{}}function X(e,n=K(),t){i.mkdirSync(S,{recursive:!0}),i.writeFileSync(N,JSON.stringify({pid:e,launcher:n,...t?{instanceId:t}:{},updatedAt:new Date().toISOString()},null,2))}function Oe(e){if(e)try{if(JSON.parse(i.readFileSync(N,"utf-8")).instanceId!==e)return}catch{return}try{i.unlinkSync(N)}catch{}}function He(e){e&&X(e)}function _e(e,n){if(!e||!n)return"unknown";try{const t=JSON.parse(i.readFileSync(N,"utf-8"));if(t.pid!==e)return"unknown";if(t.launcher==="desktop-managed"||t.launcher==="global-cli")return t.launcher}catch{}return H(e)}function H(e){if(l()==="win32")return"unknown";try{const n=a(`ps -p ${e} -o command=`,{encoding:"utf-8",stdio:["ignore","pipe","ignore"],timeout:1e3,windowsHide:!0}).replace(/\\/g,"/");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(l()==="win32")return Pe(e);try{return a("ps -axo pid=,command=",{encoding:"utf-8",stdio:["ignore","pipe","ignore"],timeout:1e3,windowsHide:!0}).split(/\r?\n/).map(t=>{const r=t.trim().match(/^(\d+)\s+(.+)$/);if(!r)return null;const o=Number(r[1]),f=r[2];return!Number.isInteger(o)||o===e?null:Ie(f)?o:null}).filter(t=>typeof t=="number")}catch{return[]}}function $e(e=process.env,n=S,t=u.tmpdir(),r=s.join(u.homedir(),".shennian")){if(e.SHENNIAN_E2E_ISOLATED_DAEMON!=="1"||!e.SHENNIAN_HOME?.trim()||s.resolve(n)===s.resolve(r))return!1;const o=s.relative(s.resolve(t),s.resolve(n));return o!==""&&o!==".."&&!o.startsWith(`..${s.sep}`)&&!s.isAbsolute(o)||s.resolve(n)===s.resolve(e.SHENNIAN_HOME)}function Ce(e=process.env,n=S){return!$e(e,n)}function m(e={}){let n=A(),t=n?.pid??null,r=t!==null&&E(t),o=t!==null&&!r,f=n?.adopted===!0;const v=j();if(o&&e.cleanupStale){try{i.unlinkSync(g)}catch{}n=null,t=null,r=!1,o=!1}if(!r&&Ce()){const w=q()[0];if(w&&(t=w,r=!0,o=!1,f=!0,e.cleanupStale)){const U=Le();xe(w,U,{launcher:H(w),adopted:!0}),X(w,H(w)),n={pid:w,instanceId:U,adopted:!0}}}return{running:r,pid:t,stale:o,remoteAccessDisabled:Re(),launcher:_e(t,r),platform:l(),shennianDir:S,pidFile:g,logFile:d,...n?.instanceId?{instanceId:n.instanceId}:{},...f?{adopted:!0}:{},...v.machineId?{machineId:v.machineId}:{},paired:!!(v.machineToken&&v.machineId),...v.serverUrl?{serverUrl:v.serverUrl}:{}}}function y(e){process.stdout.write(`${JSON.stringify(e,null,2)}
2
- `)}function Y(e){const n=e?.trim();if(!n)return;const t=j();t.serverUrl=n,fe(t)}function z(e){return e?.trim()||process.env.SHENNIAN_DESKTOP_SERVER_URL?.trim()||void 0}const _="com.shennian.agent",p=s.join(u.homedir(),"Library/LaunchAgents",`${_}.plist`),Me=s.join(u.homedir(),"AppData","Roaming","Microsoft","Windows","Start Menu","Programs","Startup"),D=h("autostart.cmd"),b=h("autostart.vbs"),R=h("autostart.xml"),Ue=s.join(Me,"ShennianAgent.vbs");function Q(e){const n=l()==="win32"?`where ${e}`:`command -v ${e}`;try{return a(n,{stdio:["ignore","pipe","pipe"],encoding:"utf-8",windowsHide:!0}).split(/\r?\n/).map(o=>o.trim()).find(Boolean)??null}catch{return null}}function je(){const e=l()==="win32"?"npx.cmd":"npx",n=s.join(s.dirname(V),e);return i.existsSync(n)?n:Q("npx")}function P(){return De({nodeExec:V,scriptPath:B,shennianCommandPath:Q("shennian"),npxPath:je()})}function Z(){try{a(`schtasks /delete /tn "${k}" /f`,{stdio:"pipe",windowsHide:!0})}catch{}}function ee(){try{i.unlinkSync(Ue)}catch{}}function We(){const e=process.env.USERDOMAIN?.trim(),n=process.env.COMPUTERNAME?.trim(),t=process.env.USERNAME?.trim()||u.userInfo().username;return e&&e.toUpperCase()!=="WORKGROUP"?`${e}\\${t}`:n?`${n}\\${t}`:t}function Fe(e){if(l()==="win32"&&ve(e.command)){const n=[e.command,...e.args].map(W).join(" ");return{command:process.env.ComSpec||"cmd.exe",args:["/d","/s","/c",`"${n}"`],windowsVerbatimArguments:!0}}return{command:e.command,args:e.args}}function Be(e,n,t=process.env){return{detached:!0,stdio:["ignore",n,n],env:t,windowsHide:!0,...e.windowsVerbatimArguments?{windowsVerbatimArguments:!0}:{}}}function Ve(){const e=P();i.writeFileSync(D,he(e,d)),i.writeFileSync(b,ye(D)),ee(),Z();const n=we({userId:We(),command:s.join(process.env.SystemRoot||"C:\\Windows","System32","wscript.exe"),arguments:[W(b)]});i.writeFileSync(R,n,"utf8");const t=ge({taskName:k,xmlPath:R});try{return a(t.create,{stdio:"pipe",windowsHide:!0}),a(t.run,{stdio:"pipe",windowsHide:!0}),!0}catch{return!1}}function Je(){Ae(k,e=>{a(e,{stdio:"pipe",windowsHide:!0})})}function ne(){const e={};for(const n of J)process.env[n]&&(e[n]=process.env[n]);if(e.HOME??=u.homedir(),e.PATH=Se({pathValue:e.PATH,env:e}),e.USER??=u.userInfo().username,l()==="win32"){const n=process.env.TEMP||process.env.TMP||u.tmpdir(),t=u.homedir();e.USERPROFILE??=t;const r=s.win32.parse(t);e.HOMEDRIVE??=r.root.replace(/[\\/]$/,"")||process.env.HOMEDRIVE||"",e.HOMEPATH??=t.slice((e.HOMEDRIVE||"").length)||process.env.HOMEPATH||"",e.USERNAME??=process.env.USERNAME||u.userInfo().username,e.COMPUTERNAME??=process.env.COMPUTERNAME||"",e.TMPDIR??=n,e.TEMP??=n,e.TMP??=n}else e.TMPDIR??="/tmp";return e}function Ge(){const e=ne(),n=P(),t=Object.entries(e).map(([o,f])=>` <key>${T(o)}</key>
3
- <string>${T(f)}</string>`).join(`
4
- `),r=[n.command,...n.args].map(o=>` <string>${T(o)}</string>`).join(`
1
+ import c from"chalk";import o from"node:fs";import s from"node:path";import d from"node:os";import{execSync as u,spawn as pe}from"node:child_process";import{randomUUID as F}from"node:crypto";import{fileURLToPath as me}from"node:url";import{getShennianDir as fe,loadConfig as B,resolveShennianPath as h,saveConfig as Se}from"../config/index.js";import{buildAugmentedPath as he}from"../env-path.js";import{buildWindowsLauncherCommand as ge,buildWindowsScheduledTaskCommands as ye,buildWindowsScheduledTaskXml as we,buildWindowsStartupVbs as ve,escapeXml as T,isWindowsCmdScript as Ee,quoteCmdArg as V,quoteSystemdArg as Pe,suspendWindowsScheduledTask as Ae}from"./daemon-windows.js";import{waitForDaemonReadyStatus as L}from"./daemon-start-wait.js";import{findRunningWindowsDaemonProcessIds as Ie,isShennianRunServiceCommand as De}from"./daemon-process.js";import{getCurrentVersion as J}from"../upgrade/engine.js";import{buildWindowsLauncherCommand as kn,buildWindowsScheduledTaskCommands as Nn,buildWindowsScheduledTaskSuspendCommands as bn,buildWindowsScheduledTaskXml as Rn,buildWindowsStartupVbs as xn,suspendWindowsScheduledTask as Tn}from"./daemon-windows.js";import{isShennianRunServiceCommand as On}from"./daemon-process.js";const ke=s.dirname(me(import.meta.url)),g=fe(),y=h("daemon.pid"),l=h("daemon.log"),O=h("remote-access.disabled"),N=h("daemon-launcher.json"),G=s.resolve(ke,"../../bin/shennian.js"),K=process.execPath,X=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","SHENNIAN_DESKTOP_SERVER_URL","SHENNIAN_WECHAT_CHANNEL_HELPER_DIR","SHENNIAN_HOME","SHENNIAN_ACCOUNT_NAMESPACE_ID","SHENNIAN_NATIVE_FUSION_DISABLED","SHENNIAN_WECHAT_DELIVERABILITY_TRACE"]);function Ne(e){return X.has(e)}function p(){const e=d.platform();return e==="darwin"||e==="linux"||e==="win32"?e:"linux"}function _(e){const n=e.replace(/\\/g,"/").toLowerCase(),t=d.tmpdir().replace(/\\/g,"/").toLowerCase();return n.includes("/_npx/")||n.includes("/npm-cache/_npx/")||n.includes("/pnpm/dlx/")||n.startsWith(t.endsWith("/")?t:`${t}/`)}function be(e){return o.existsSync(e.scriptPath)&&!_(e.scriptPath)?{command:e.nodeExec,args:[e.scriptPath,"run-service"],mode:"direct"}:e.shennianCommandPath&&!_(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 Re(e){const n=e.trim();if(!n)return null;if(n.startsWith("{"))try{const r=JSON.parse(n),i=Number(r.pid);return!Number.isInteger(i)||i<=0?null:{pid:i,...typeof r.instanceId=="string"?{instanceId:r.instanceId}:{},...typeof r.version=="string"?{version:r.version}:{},...r.launcher==="desktop-managed"||r.launcher==="global-cli"||r.launcher==="unknown"?{launcher:r.launcher}:{},...typeof r.startedAt=="string"?{startedAt:r.startedAt}:{},...r.adopted===!0?{adopted:!0}:{}}}catch{return null}const t=parseInt(n,10);return isNaN(t)?null:{pid:t}}function A(){try{return Re(o.readFileSync(y,"utf-8"))}catch{return null}}function E(e){try{return process.kill(e,0),!0}catch{return!1}}function q(e,n=3e3){const t=Date.now();for(;Date.now()-t<n;){if(!E(e))return!0;Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,100)}return!E(e)}function xe(){return o.existsSync(O)}function Te(){try{o.unlinkSync(O)}catch{}}function Le(e,n){const t=s.dirname(e),r=`${e}.${process.pid}.${F()}.tmp`;o.mkdirSync(t,{recursive:!0});let i=null;try{i=o.openSync(r,"w",384),o.writeSync(i,n,void 0,"utf8"),o.fsyncSync(i),o.closeSync(i),i=null,o.renameSync(r,e);try{const a=o.openSync(t,"r");try{o.fsyncSync(a)}finally{o.closeSync(a)}}catch{}}finally{i!==null&&o.closeSync(i);try{o.unlinkSync(r)}catch{}}}function Oe(e={}){const n=e.disabledAt??new Date().toISOString(),t=e.markerFile??O,r=e.platform??p(),i=e.servicePresent??M(r),a=e.execCommand??(m=>{u(m,{stdio:"pipe",windowsHide:!0})});if(Le(t,n),!i)return{disabledAt:n,autoStartDisabled:!0};try{return a(r==="darwin"?`launchctl disable "${P()}"`:r==="linux"?"systemctl --user disable shennian":`schtasks /change /tn "${k}" /disable`),{disabledAt:n,autoStartDisabled:!0}}catch(m){return{disabledAt:n,autoStartDisabled:!1,autoStartError:m instanceof Error?m.message:String(m)}}}function _e(e={}){const n=e.platform??p();if(!(e.servicePresent??M(n)))return{serviceStopped:!0};const r=e.execCommand??(i=>{u(i,{stdio:"pipe",windowsHide:!0})});try{return r(n==="darwin"?`launchctl bootout "${P()}"`:n==="linux"?"systemctl --user stop shennian":`schtasks /end /tn "${k}"`),{serviceStopped:!0}}catch(i){return{serviceStopped:!1,error:i instanceof Error?i.message:String(i)}}}function Y(){return"global-cli"}function $e(){return`${process.pid}-${Date.now()}-${F()}`}function He(e,n,t={}){o.mkdirSync(g,{recursive:!0}),o.writeFileSync(y,JSON.stringify({pid:e,instanceId:n,...t.version?{version:t.version}:{},launcher:t.launcher??Y(),...t.adopted?{adopted:!0}:{},startedAt:new Date().toISOString()},null,2))}function Pn(e,n){const t=A();if(!(t?.pid!==e||t.instanceId!==n))try{o.unlinkSync(y)}catch{}}function z(e,n=Y(),t){o.mkdirSync(g,{recursive:!0}),o.writeFileSync(N,JSON.stringify({pid:e,launcher:n,...t?{instanceId:t}:{},updatedAt:new Date().toISOString()},null,2))}function Ce(e){if(e)try{if(JSON.parse(o.readFileSync(N,"utf-8")).instanceId!==e)return}catch{return}try{o.unlinkSync(N)}catch{}}function Me(e){e&&z(e)}function Ue(e,n){if(!e||!n)return"unknown";try{const t=JSON.parse(o.readFileSync(N,"utf-8"));if(t.pid!==e)return"unknown";if(t.launcher==="desktop-managed"||t.launcher==="global-cli")return t.launcher}catch{}return $(e)}function $(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("/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 Ie(e);try{return u("ps -axo pid=,command=",{encoding:"utf-8",stdio:["ignore","pipe","ignore"],timeout:1e3,windowsHide:!0}).split(/\r?\n/).map(t=>{const r=t.trim().match(/^(\d+)\s+(.+)$/);if(!r)return null;const i=Number(r[1]),a=r[2];return!Number.isInteger(i)||i===e?null:De(a)?i:null}).filter(t=>typeof t=="number")}catch{return[]}}function je(e=process.env,n=g,t=d.tmpdir(),r=s.join(d.homedir(),".shennian")){if(e.SHENNIAN_E2E_ISOLATED_DAEMON!=="1"||!e.SHENNIAN_HOME?.trim()||s.resolve(n)===s.resolve(r))return!1;const i=s.relative(s.resolve(t),s.resolve(n));return i!==""&&i!==".."&&!i.startsWith(`..${s.sep}`)&&!s.isAbsolute(i)||s.resolve(n)===s.resolve(e.SHENNIAN_HOME)}function We(e=process.env,n=g){return!je(e,n)}function f(e={}){let n=A(),t=n?.pid??null,r=t!==null&&E(t),i=t!==null&&!r,a=n?.adopted===!0;const m=B();if(i&&e.cleanupStale){try{o.unlinkSync(y)}catch{}n=null,t=null,r=!1,i=!1}if(!r&&We()){const v=Q()[0];if(v&&(t=v,r=!0,i=!1,a=!0,e.cleanupStale)){const W=$e();He(v,W,{launcher:$(v),adopted:!0}),z(v,$(v)),n={pid:v,instanceId:W,adopted:!0}}}return{running:r,pid:t,stale:i,remoteAccessDisabled:xe(),launcher:Ue(t,r),platform:p(),shennianDir:g,pidFile:y,logFile:l,...n?.instanceId?{instanceId:n.instanceId}:{},...a?{adopted:!0}:{},...m.machineId?{machineId:m.machineId}:{},paired:!!(m.machineToken&&m.machineId),...m.serverUrl?{serverUrl:m.serverUrl}:{}}}function w(e){process.stdout.write(`${JSON.stringify(e,null,2)}
2
+ `)}function Z(e){const n=e?.trim();if(!n)return;const t=B();t.serverUrl=n,Se(t)}function ee(e){return e?.trim()||process.env.SHENNIAN_DESKTOP_SERVER_URL?.trim()||void 0}const H="com.shennian.agent",S=s.join(d.homedir(),"Library/LaunchAgents",`${H}.plist`),Fe=s.join(d.homedir(),"AppData","Roaming","Microsoft","Windows","Start Menu","Programs","Startup"),b=h("autostart.cmd"),R=h("autostart.vbs"),x=h("autostart.xml"),Be=s.join(Fe,"ShennianAgent.vbs");function ne(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(i=>i.trim()).find(Boolean)??null}catch{return null}}function Ve(){const e=p()==="win32"?"npx.cmd":"npx",n=s.join(s.dirname(K),e);return o.existsSync(n)?n:ne("npx")}function I(){return be({nodeExec:K,scriptPath:G,shennianCommandPath:ne("shennian"),npxPath:Ve()})}function te(){try{u(`schtasks /delete /tn "${k}" /f`,{stdio:"pipe",windowsHide:!0})}catch{}}function re(){try{o.unlinkSync(Be)}catch{}}function Je(){const e=process.env.USERDOMAIN?.trim(),n=process.env.COMPUTERNAME?.trim(),t=process.env.USERNAME?.trim()||d.userInfo().username;return e&&e.toUpperCase()!=="WORKGROUP"?`${e}\\${t}`:n?`${n}\\${t}`:t}function Ge(e){if(p()==="win32"&&Ee(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 Ke(e,n,t=process.env){return{detached:!0,stdio:["ignore",n,n],env:t,windowsHide:!0,...e.windowsVerbatimArguments?{windowsVerbatimArguments:!0}:{}}}function Xe(){const e=I();o.writeFileSync(b,ge(e,l)),o.writeFileSync(R,ve(b)),re(),te();const n=we({userId:Je(),command:s.join(process.env.SystemRoot||"C:\\Windows","System32","wscript.exe"),arguments:[V(R)]});o.writeFileSync(x,n,"utf8");const t=ye({taskName:k,xmlPath:x});try{return u(t.create,{stdio:"pipe",windowsHide:!0}),u(t.run,{stdio:"pipe",windowsHide:!0}),!0}catch{return!1}}function qe(){Ae(k,e=>{u(e,{stdio:"pipe",windowsHide:!0})})}function oe(){const e={};for(const n of X)process.env[n]&&(e[n]=process.env[n]);if(e.HOME??=d.homedir(),e.PATH=he({pathValue:e.PATH,env:e}),e.USER??=d.userInfo().username,p()==="win32"){const n=process.env.TEMP||process.env.TMP||d.tmpdir(),t=d.homedir();e.USERPROFILE??=t;const r=s.win32.parse(t);e.HOMEDRIVE??=r.root.replace(/[\\/]$/,"")||process.env.HOMEDRIVE||"",e.HOMEPATH??=t.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 Ye(){const e=oe(),n=I(),t=Object.entries(e).map(([i,a])=>` <key>${T(i)}</key>
3
+ <string>${T(a)}</string>`).join(`
4
+ `),r=[n.command,...n.args].map(i=>` <string>${T(i)}</string>`).join(`
5
5
  `);return`<?xml version="1.0" encoding="UTF-8"?>
6
6
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
7
7
  <plist version="1.0">
8
8
  <dict>
9
9
  <key>Label</key>
10
- <string>${_}</string>
10
+ <string>${H}</string>
11
11
  <key>ProgramArguments</key>
12
12
  <array>
13
13
  ${r}
@@ -23,24 +23,24 @@ ${t}
23
23
  <key>ThrottleInterval</key>
24
24
  <integer>5</integer>
25
25
  <key>StandardOutPath</key>
26
- <string>${d}</string>
26
+ <string>${l}</string>
27
27
  <key>StandardErrorPath</key>
28
- <string>${d}</string>
28
+ <string>${l}</string>
29
29
  </dict>
30
- </plist>`}const I=s.join(u.homedir(),".config/systemd/user/shennian.service");function Ke(){const e=ne(),n=P(),t=Object.entries(e).map(([o,f])=>`Environment=${o}=${f}`).join(`
31
- `),r=[n.command,...n.args].map(Ee).join(" ");return`[Unit]
30
+ </plist>`}const D=s.join(d.homedir(),".config/systemd/user/shennian.service");function ze(){const e=oe(),n=I(),t=Object.entries(e).map(([i,a])=>`Environment=${i}=${a}`).join(`
31
+ `),r=[n.command,...n.args].map(Pe).join(" ");return`[Unit]
32
32
  Description=Shennian Agent Daemon
33
33
  After=network.target
34
34
 
35
35
  [Service]
36
36
  ${t}
37
37
  ExecStart=${r}
38
- Restart=always
38
+ Restart=on-failure
39
39
  RestartSec=10
40
- StandardOutput=append:${d}
41
- StandardError=append:${d}
40
+ StandardOutput=append:${l}
41
+ StandardError=append:${l}
42
42
 
43
43
  [Install]
44
- WantedBy=default.target`}const k="ShennianAgent";function Xe(e){a(e,{stdio:"pipe",windowsHide:!0})}function qe(){const e=process.getuid?.();if(typeof e=="number")return e;const n=u.userInfo();return typeof n.uid=="number"?n.uid:n.username}function te(e=qe()){return`gui/${e}/${_}`}function Ye(e=Xe){try{return e(`launchctl unload "${p}" 2>/dev/null; launchctl load -w "${p}"`),e(`launchctl kickstart -k "${te()}"`),!0}catch{try{return e(`launchctl load -w "${p}"`),e(`launchctl kickstart -k "${te()}"`),!0}catch{return!1}}}function ze(){i.mkdirSync(S,{recursive:!0});const e={};for(const[n,t]of Object.entries(process.env))t!==void 0&&Ne(n)&&(e[n]=t);i.writeFileSync(h("env.json"),JSON.stringify(e,null,2))}function re(e={}){i.mkdirSync(S,{recursive:!0});const n=m({cleanupStale:!0});if(n.pid!==null&&n.running&&!$(n)){e.quiet||console.log(c.green(`\u2713 Background service already running (PID ${n.pid})`));return}ie(n);const t=i.openSync(d,"a"),r=Fe(P()),o=de(r.command,r.args,Be(r,t));o.unref(),i.closeSync(t),He(o.pid),e.quiet||(console.log(c.green(`\u2713 Background service started (PID ${o.pid})`)),console.log(c.gray(` Logs: ${d}`)))}function $(e,n=A(),t=F()){return e.pid===null||!e.running?!1:e.adopted===!0||n?.version!==t}function Qe(e=l()){switch(e){case"darwin":return i.existsSync(p);case"linux":return i.existsSync(I);case"win32":return i.existsSync(D)&&i.existsSync(b)&&i.existsSync(R)}}function Ze(e,n,t,r=A(),o=F()){return!n.api?.trim()&&e.pid!==null&&e.running&&!e.stale&&!e.remoteAccessDisabled&&t&&!$(e,r,o)}function ie(e=m({cleanupStale:!0})){if(!(!$(e)||e.pid===null))try{process.kill(e.pid,"SIGTERM"),G(e.pid,5e3)||(process.kill(e.pid,"SIGKILL"),G(e.pid,2e3));try{A()?.pid===e.pid&&i.unlinkSync(g)}catch{}}catch{}}function oe(){i.mkdirSync(S,{recursive:!0});const e=l();switch(P().mode==="direct"&&O(B)&&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.")),e){case"darwin":{const t=s.dirname(p);return i.mkdirSync(t,{recursive:!0}),i.writeFileSync(p,Ge()),Ye()}case"linux":{const t=s.dirname(I);i.mkdirSync(t,{recursive:!0}),i.writeFileSync(I,Ke());try{a("systemctl --user daemon-reload && systemctl --user enable shennian",{stdio:"pipe",windowsHide:!0})}catch{return!1}try{a(`loginctl enable-linger ${u.userInfo().username}`,{stdio:"pipe",windowsHide:!0})}catch{}try{return a("systemctl --user restart shennian",{stdio:"pipe",windowsHide:!0}),!0}catch{return!1}}case"win32":return Ve()}}function en(){const n=m({cleanupStale:!0}).pid;if(n===null)return{};if(!E(n)){try{i.unlinkSync(g)}catch{}return{stalePid:n}}try{return process.kill(n,"SIGTERM"),{stoppedPid:n}}catch(t){return{error:t instanceof Error?t.message:String(t)}}}async function se(e=5e3){const n=en();if(!n.stoppedPid)return n;if(!await M(n.stoppedPid,e))try{process.kill(n.stoppedPid,"SIGKILL"),await M(n.stoppedPid,2e3)}catch(r){return{...n,error:r instanceof Error?r.message:String(r)}}try{i.unlinkSync(g)}catch{}Oe();for(const r of q())if(r!==n.stoppedPid)try{process.kill(r,"SIGTERM"),await M(r,2e3)}catch{}return n}function nn(e={}){Y(z(e.api)),Te(),ie(),oe()||re({quiet:e.json}),e.json?y(L(()=>m({cleanupStale:!0}))):console.log(c.green("\u2713 Background service started"))}async function tn(e={}){switch(i.mkdirSync(S,{recursive:!0}),i.writeFileSync(x,new Date().toISOString()),l()){case"darwin":{if(i.existsSync(p))try{a(`launchctl unload -w "${p}"`,{stdio:"pipe",windowsHide:!0})}catch{}break}case"linux":{try{a("systemctl --user disable --now shennian",{stdio:"pipe",windowsHide:!0})}catch{}break}case"win32":{try{a(`schtasks /change /tn "${k}" /disable`,{stdio:"pipe",windowsHide:!0})}catch{}try{a(`schtasks /end /tn "${k}"`,{stdio:"pipe",windowsHide:!0})}catch{}break}}const t=await se(),r=m({cleanupStale:!0});if(e.json){y(t.error?{...r,error:t.error}:r);return}t.error?console.error(c.red(`\u2717 Failed to stop: ${t.error}`)):console.log(c.green("\u2713 Background service stopped"))}function ce(e){const n=m({cleanupStale:!0});if(Ze(n,e,Qe())){e.json?y(n):console.log(c.green(`\u2713 Background service already running (PID ${n.pid})`));return}ze(),nn(e)}async function C(e={}){await tn(e)}async function M(e,n=5e3){const t=Date.now();for(;Date.now()-t<n;){if(!E(e))return!0;await new Promise(r=>setTimeout(r,100))}return!E(e)}async function rn(e={}){Y(z(e.api));const n=l();n==="win32"&&Je();const t=await se();if(t.error){e.json?y({...m(),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")),(n==="linux"||n==="win32")&&oe()){e.json&&y(L(()=>m({cleanupStale:!0})));return}re({quiet:e.json}),e.json&&y(L(()=>m({cleanupStale:!0})))}function ae(e={}){const n=m({cleanupStale:!0});if(e.json){y(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: ${d}`))):console.log(c.yellow(`\u25CF Background service stopped (PID ${n.pid} is stale)`))}function ue(e){if(!i.existsSync(d)){console.log(c.gray("No logs yet"));return}try{const n=a(u.platform()==="win32"?`powershell Get-Content -Tail ${e.lines} "${d}"`:`tail -n ${e.lines} "${d}"`,{encoding:"utf-8",windowsHide:!0});process.stdout.write(n)}catch{const n=i.readFileSync(d,"utf-8").split(`
44
+ WantedBy=default.target`}const k="ShennianAgent";function Qe(e){u(e,{stdio:"pipe",windowsHide:!0})}function Ze(){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=Ze()){return`gui/${e}/${H}`}function en(e=Qe){try{return e(`launchctl enable "${P()}"; launchctl unload "${S}" 2>/dev/null; launchctl load -w "${S}"`),e(`launchctl kickstart -k "${P()}"`),!0}catch{try{return e(`launchctl enable "${P()}"`),e(`launchctl load -w "${S}"`),e(`launchctl kickstart -k "${P()}"`),!0}catch{return!1}}}function nn(){o.mkdirSync(g,{recursive:!0});const e={};for(const[n,t]of Object.entries(process.env))t!==void 0&&Ne(n)&&(e[n]=t);o.writeFileSync(h("env.json"),JSON.stringify(e,null,2))}function ie(e={}){o.mkdirSync(g,{recursive:!0});const n=f({cleanupStale:!0});if(n.pid!==null&&n.running&&!C(n)){e.quiet||console.log(c.green(`\u2713 Background service already running (PID ${n.pid})`));return}se(n);const t=o.openSync(l,"a"),r=Ge(I()),i=pe(r.command,r.args,Ke(r,t));i.unref(),o.closeSync(t),Me(i.pid),e.quiet||(console.log(c.green(`\u2713 Background service started (PID ${i.pid})`)),console.log(c.gray(` Logs: ${l}`)))}function C(e,n=A(),t=J()){return e.pid===null||!e.running?!1:e.adopted===!0||n?.version!==t}function M(e=p()){switch(e){case"darwin":return o.existsSync(S);case"linux":return o.existsSync(D);case"win32":return o.existsSync(b)&&o.existsSync(R)&&o.existsSync(x)}}function tn(e,n,t,r=A(),i=J()){return!n.api?.trim()&&e.pid!==null&&e.running&&!e.stale&&!e.remoteAccessDisabled&&t&&!C(e,r,i)}function se(e=f({cleanupStale:!0})){if(!(!C(e)||e.pid===null))try{process.kill(e.pid,"SIGTERM"),q(e.pid,5e3)||(process.kill(e.pid,"SIGKILL"),q(e.pid,2e3));try{A()?.pid===e.pid&&o.unlinkSync(y)}catch{}}catch{}}function ce(){o.mkdirSync(g,{recursive:!0});const e=p();switch(I().mode==="direct"&&_(G)&&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.")),e){case"darwin":{const t=s.dirname(S);return o.mkdirSync(t,{recursive:!0}),o.writeFileSync(S,Ye()),en()}case"linux":{const t=s.dirname(D);o.mkdirSync(t,{recursive:!0}),o.writeFileSync(D,ze());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 Xe()}}function rn(){const n=f({cleanupStale:!0}).pid;if(n===null)return{};if(!E(n)){try{o.unlinkSync(y)}catch{}return{stalePid:n}}try{return process.kill(n,"SIGTERM"),{stoppedPid:n}}catch(t){return{error:t instanceof Error?t.message:String(t)}}}async function ae(e=5e3){const n=rn();if(!n.stoppedPid)return n;if(!await j(n.stoppedPid,e))try{process.kill(n.stoppedPid,"SIGKILL"),await j(n.stoppedPid,2e3)}catch(r){return{...n,error:r instanceof Error?r.message:String(r)}}try{o.unlinkSync(y)}catch{}Ce();for(const r of Q())if(r!==n.stoppedPid)try{process.kill(r,"SIGTERM"),await j(r,2e3)}catch{}return n}function on(e={}){Z(ee(e.api)),Te(),se(),ce()||ie({quiet:e.json}),e.json?w(L(()=>f({cleanupStale:!0}))):console.log(c.green("\u2713 Background service started"))}async function sn(e={}){const n=Oe(),t=_e(),r=await ae(),i=f({cleanupStale:!0}),a=r.error??n.autoStartError??t.error;if(e.json){w(a?{...i,error:a}:i);return}a?console.error(c.red(`\u2717 Failed to stop completely: ${a}`)):console.log(c.green("\u2713 Background service stopped"))}function ue(e){const n=f({cleanupStale:!0});if(tn(n,e,M())){e.json?w(n):console.log(c.green(`\u2713 Background service already running (PID ${n.pid})`));return}nn(),on(e)}async function U(e={}){await sn(e)}async function j(e,n=5e3){const t=Date.now();for(;Date.now()-t<n;){if(!E(e))return!0;await new Promise(r=>setTimeout(r,100))}return!E(e)}async function cn(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 t=p();t==="win32"&&qe();const r=await ae();if(r.error){e.json?w({...f(),error:r.error}):console.error(c.red(`\u2717 Failed to stop: ${r.error}`));return}if(r.stoppedPid!==void 0?e.json||console.log(c.green(`\u2713 Background service stopped (PID ${r.stoppedPid})`)):r.stalePid!==void 0?e.json||console.log(c.yellow(`\u26A0 Service process no longer exists, cleaned up stale PID ${r.stalePid}`)):e.json||console.log(c.gray("\u25CF Background service not running, starting it now")),(t==="linux"||t==="win32")&&ce()){e.json&&w(L(()=>f({cleanupStale:!0})));return}ie({quiet:e.json}),e.json&&w(L(()=>f({cleanupStale:!0})))}function de(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: ${l}`))):console.log(c.yellow(`\u25CF Background service stopped (PID ${n.pid} is stale)`))}function le(e){if(!o.existsSync(l)){console.log(c.gray("No logs yet"));return}try{const n=u(d.platform()==="win32"?`powershell Get-Content -Tail ${e.lines} "${l}"`:`tail -n ${e.lines} "${l}"`,{encoding:"utf-8",windowsHide:!0});process.stdout.write(n)}catch{const n=o.readFileSync(l,"utf-8").split(`
45
45
  `).slice(-e.lines);console.log(n.join(`
46
- `))}}async function on(){switch(await C(),l()){case"darwin":{if(i.existsSync(p)){try{a(`launchctl unload -w "${p}"`,{stdio:"pipe",windowsHide:!0})}catch{}i.unlinkSync(p),console.log(c.green("\u2713 Auto-start uninstalled (launchd)"))}break}case"linux":{try{a("systemctl --user disable --now shennian",{stdio:"pipe",windowsHide:!0})}catch{}if(i.existsSync(I)){i.unlinkSync(I);try{a("systemctl --user daemon-reload",{stdio:"pipe",windowsHide:!0})}catch{}}console.log(c.green("\u2713 Auto-start uninstalled (systemd)"));break}case"win32":{Z();try{i.unlinkSync(D)}catch{}try{i.unlinkSync(b)}catch{}try{i.unlinkSync(R)}catch{}ee(),console.log(c.green("\u2713 Auto-start uninstalled (Task Scheduler)"));break}}}function vn(e){e.command("start").description("Start the background service").option("--json","Print machine-readable status JSON").option("--api <url>","Server URL override").action(r=>ce(r)),e.command("stop").description("Stop the background service").option("--json","Print machine-readable status JSON").action(r=>C(r)),e.command("status").description("Show background service status").option("--json","Print machine-readable status JSON").action(r=>ae(r)),e.command("logs").description("Show recent logs").option("-n, --lines <n>","Number of lines","50").action(r=>ue({lines:parseInt(r.lines,10)}));const n=e.command("daemon",{hidden:!0}).description("Deprecated: use top-level start/stop/status/logs"),t=(r,o)=>{o||console.error(c.yellow(`\u26A0 Deprecated command. Use \`shennian ${r}\` instead.`))};n.command("start").description("Start the background service").option("--json","Print machine-readable status JSON").option("--api <url>","Server URL override").action(r=>{t("start",r.json),ce(r)}),n.command("stop").description("Stop the background service").option("--json","Print machine-readable status JSON").action(async r=>{t("stop",r.json),await C(r)}),n.command("restart").description("Restart the background service").option("--json","Print machine-readable status JSON").option("--api <url>","Server URL override").action(r=>{t("stop && shennian start",r.json),rn(r)}),n.command("status").description("Show background service status").option("--json","Print machine-readable status JSON").action(r=>{t("status",r.json),ae(r)}),n.command("logs").description("Show recent logs").option("-n, --lines <n>","Number of lines","50").action(r=>{t("logs"),ue({lines:parseInt(r.lines,10)})}),n.command("uninstall").description("Uninstall auto-start service").action(on)}export{Be as buildDaemonSpawnOptions,Pn as buildWindowsLauncherCommand,In as buildWindowsScheduledTaskCommands,kn as buildWindowsScheduledTaskSuspendCommands,Nn as buildWindowsScheduledTaskXml,Dn as buildWindowsStartupVbs,Ze as canReuseRunningDaemonForStart,ne as captureEnvForService,Oe as clearDaemonLauncher,yn as clearDaemonPidIfOwner,Te as clearRemoteAccessDisabled,Le as createDaemonInstanceId,q as findRunningDaemonProcessIds,K as getCurrentProcessDaemonLauncher,m as getDaemonStatus,te as getLaunchdServiceTarget,oe as installService,Qe as isAutoStartServicePresent,O as isEphemeralCliPath,$e as isIsolatedDaemonE2eInstance,Re as isRemoteAccessDisabled,Ne as isSafeSnapshotEnvKey,Tn as isShennianRunServiceCommand,He as recordStartedDaemon,vn as registerDaemonCommand,Ye as reloadLaunchdAgent,De as resolveServiceLaunchSpec,ze as saveEnvSnapshot,Ce as shouldDiscoverGlobalDaemonProcesses,$ as shouldReplaceRunningDaemon,re as startDaemonProcess,bn as suspendWindowsScheduledTask,X as writeDaemonLauncher,xe as writeDaemonPid};
46
+ `))}}async function an(){switch(await U(),p()){case"darwin":{if(o.existsSync(S)){try{u(`launchctl unload -w "${S}"`,{stdio:"pipe",windowsHide:!0})}catch{}o.unlinkSync(S),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(D)){o.unlinkSync(D);try{u("systemctl --user daemon-reload",{stdio:"pipe",windowsHide:!0})}catch{}}console.log(c.green("\u2713 Auto-start uninstalled (systemd)"));break}case"win32":{te();try{o.unlinkSync(b)}catch{}try{o.unlinkSync(R)}catch{}try{o.unlinkSync(x)}catch{}re(),console.log(c.green("\u2713 Auto-start uninstalled (Task Scheduler)"));break}}}function An(e){e.command("start").description("Start the background service").option("--json","Print machine-readable status JSON").option("--api <url>","Server URL override").action(r=>ue(r)),e.command("stop").description("Stop the background service").option("--json","Print machine-readable status JSON").action(r=>U(r)),e.command("status").description("Show background service status").option("--json","Print machine-readable status JSON").action(r=>de(r)),e.command("logs").description("Show recent logs").option("-n, --lines <n>","Number of lines","50").action(r=>le({lines:parseInt(r.lines,10)}));const n=e.command("daemon",{hidden:!0}).description("Deprecated: use top-level start/stop/status/logs"),t=(r,i)=>{i||console.error(c.yellow(`\u26A0 Deprecated command. Use \`shennian ${r}\` instead.`))};n.command("start").description("Start the background service").option("--json","Print machine-readable status JSON").option("--api <url>","Server URL override").action(r=>{t("start",r.json),ue(r)}),n.command("stop").description("Stop the background service").option("--json","Print machine-readable status JSON").action(async r=>{t("stop",r.json),await U(r)}),n.command("restart").description("Restart the background service").option("--json","Print machine-readable status JSON").option("--api <url>","Server URL override").action(r=>{t("stop && shennian start",r.json),cn(r)}),n.command("status").description("Show background service status").option("--json","Print machine-readable status JSON").action(r=>{t("status",r.json),de(r)}),n.command("logs").description("Show recent logs").option("-n, --lines <n>","Number of lines","50").action(r=>{t("logs"),le({lines:parseInt(r.lines,10)})}),n.command("uninstall").description("Uninstall auto-start service").action(an)}export{Ke as buildDaemonSpawnOptions,ze as buildSystemdUnit,kn as buildWindowsLauncherCommand,Nn as buildWindowsScheduledTaskCommands,bn as buildWindowsScheduledTaskSuspendCommands,Rn as buildWindowsScheduledTaskXml,xn as buildWindowsStartupVbs,tn as canReuseRunningDaemonForStart,oe as captureEnvForService,Ce as clearDaemonLauncher,Pn as clearDaemonPidIfOwner,Te as clearRemoteAccessDisabled,$e as createDaemonInstanceId,Q as findRunningDaemonProcessIds,Y as getCurrentProcessDaemonLauncher,f as getDaemonStatus,P as getLaunchdServiceTarget,ce as installService,M as isAutoStartServicePresent,_ as isEphemeralCliPath,je as isIsolatedDaemonE2eInstance,xe as isRemoteAccessDisabled,Ne as isSafeSnapshotEnvKey,On as isShennianRunServiceCommand,Oe as prepareRemoteAccessPause,Me as recordStartedDaemon,An as registerDaemonCommand,en as reloadLaunchdAgent,be as resolveServiceLaunchSpec,nn as saveEnvSnapshot,We as shouldDiscoverGlobalDaemonProcesses,C as shouldReplaceRunningDaemon,ie as startDaemonProcess,_e as stopPreparedRemoteAccessService,Tn as suspendWindowsScheduledTask,z as writeDaemonLauncher,He as writeDaemonPid};