@messenger-agent/client 0.25.0 → 0.26.0-alpha.0

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.
@@ -1 +1 @@
1
- import{spawn as P}from"node:child_process";import{randomUUID as T}from"node:crypto";import{createRequire as x}from"node:module";import{dirname as d,join as p}from"node:path";import{parse as S}from"yaml";import{readFile as M,unlink as y}from"node:fs/promises";import{readControlSocketPath as E,startControlServer as I}from"./control.js";import{MaintenanceScheduler as C}from"./maintenance.js";import{readUpgradeChannel as $}from"./config-file.js";import{currentBundledSkillsDir as b,defaultAgentHomes as k,installRuntime as N,syncBundledSkills as O}from"./runtime.js";import{darwinCommands as F,launchdLabel as v,linuxCommands as G,serviceName as f}from"./service.js";import{runCommand as l}from"./exec.js";import{AutoUpgradeScheduler as L}from"./auto-upgrade.js";import{isAgentActivityResponse as R}from"@messenger-agent/shared/agent-activity";const w=x(import.meta.url);function _(){return[{name:"codex",entry:w.resolve("@messenger-agent/codex-agent")},{name:"claude",entry:w.resolve("@messenger-agent/claude-agent")},{name:"workspace",entry:w.resolve("@messenger-agent/messenger-agent")}]}function B(s,t){let e=s;for(const r of t){if(!e||typeof e!="object"||Array.isArray(e))return;e=e[r]}return e}async function j(s){const t=S(await M(s,"utf8")),e=B(t,["workspaces"]);if(Array.isArray(e)){const r=e.find(i=>!!i&&typeof i=="object"&&!Array.isArray(i)&&typeof i.path=="string");if(typeof r?.path=="string"&&r.path.trim())return r.path}return d(s)}class q{options;stopping=!1;running;restartBaseMs;restartMaxMs;constructor(t){this.options=t,this.running=(t.agents??_()).map(e=>({definition:e,restartAttempts:0})),this.restartBaseMs=t.restartBaseMs??1e3,this.restartMaxMs=t.restartMaxMs??3e4}start(){for(const t of this.running)this.startAgent(t)}async stop(){this.stopping=!0;for(const t of this.running)t.restartTimer&&clearTimeout(t.restartTimer),t.process?.kill("SIGTERM");await Promise.all(this.running.map(t=>t.process).filter(t=>!!t&&t.exitCode===null&&!t.killed).map(t=>new Promise(e=>{const r=setTimeout(()=>{t.kill("SIGKILL"),e()},8e3);t.once("exit",()=>{clearTimeout(r),e()})})))}restartAgent(t){const e=this.running.find(({definition:r})=>r.name===t);return e?(e.restartPromise||(e.restartPromise=this.restartAgentProcess(e).finally(()=>{e.restartPromise=void 0})),e.restartPromise):Promise.reject(new Error(`Agent is not managed by this client: ${t}`))}async activityStatus(t=1e3){const[e,r]=await Promise.all([this.agentActivityStatus("codex",t),this.agentActivityStatus("claude",t)]),i=[e,r].filter(n=>n.available);return{active:i.reduce((n,a)=>n+a.active,0),waiting:i.reduce((n,a)=>n+a.waiting,0),agents:{codex:e,claude:r}}}agentActivityStatus(t,e){const r=this.running.find(({definition:i})=>i.name===t)?.process;return!r||!r.connected||!r.send?Promise.resolve({available:!1,error:`${t} agent is not connected`}):new Promise(i=>{const n=T(),a={type:"client.activity.request",requestId:n};let u=!1;const c=o=>{u||(u=!0,clearTimeout(A),r.off("message",m),i(o))},m=o=>{!R(o)||o.requestId!==n||o.agent!==t||c({available:!0,...o.snapshot})},A=setTimeout(()=>c({available:!1,error:`${t} agent did not respond`}),e);r.on("message",m),r.send(a,o=>{o&&c({available:!1,error:o.message})})})}async restartAgentProcess(t){t.restartTimer&&(clearTimeout(t.restartTimer),t.restartTimer=void 0);const e=t.process;if(e&&e.exitCode===null&&!e.killed&&(e.kill("SIGTERM"),await H(e)),this.stopping)throw new Error("Client service is stopping");t.restartAttempts=0,this.startAgent(t)}startAgent(t){const e={...process.env,AGENT_CONFIG_PATH:this.options.configPath};t.definition.name==="workspace"&&(delete e.OPENAI_API_KEY,delete e.CODEX_API_KEY,delete e.ANTHROPIC_API_KEY);const r=(this.options.spawnProcess??P)(process.execPath,[t.definition.entry],{cwd:this.options.workspacePath,env:e,stdio:["inherit","inherit","inherit","ipc"]});t.process=r,console.log(`[client] started ${t.definition.name}-agent pid=${r.pid}`),r.once("exit",(i,n)=>{if(t.process=void 0,this.stopping||t.restartPromise)return;const a=Math.min(this.restartBaseMs*2**t.restartAttempts,this.restartMaxMs);t.restartAttempts+=1,console.error(`[client] ${t.definition.name}-agent exited with code=${i??"null"} signal=${n??"null"}; restarting in ${a}ms`),t.restartTimer=setTimeout(()=>{t.restartTimer=void 0,this.startAgent(t)},a)})}}async function nt(s){await O(b(),k());const t=await j(s),e=new q({configPath:s,workspacePath:t}),r=await E(s),i=new C({statePath:p(d(r),"maintenance.json"),getActivity:()=>e.activityStatus(),execute:(c,m)=>U(c,s,e,m)});await i.start();const n=new L({statePath:p(d(r),"auto-upgrade.json"),maintenance:i,getChannel:()=>$(s)});await n.start();const a=await I(r,{restartAgent:c=>e.restartAgent(c),getActivity:()=>e.activityStatus(),maintenance:i});e.start();const u=async()=>{n.stop(),i.stop(),await a.close(),await e.stop(),process.exit(0)};process.once("SIGINT",()=>{u()}),process.once("SIGTERM",()=>{u()})}async function U(s,t,e,r){if(s.type==="upgrade"){await N({version:s.version,configPath:t}),await r("restarting"),await h(g().restart);return}if(s.type==="restart"&&s.agent){await e.restartAgent(s.agent);return}if(s.type==="restart"){await r("restarting"),await h(g().restart);return}if(s.type==="stop"){await r("stopping"),await h(g().stop);return}await r("stopping"),await K()}function g(){return process.platform==="darwin"?F():G()}async function h(s){const t=await l("/bin/sh",["-lc",s],{allowFailure:!0});if(t.status!==0)throw new Error(t.stderr||t.stdout||`Service command failed: ${s}`)}async function K(){const s=process.env.HOME??"";if(process.platform==="linux"){await l("systemctl",["--user","disable",`${f}.service`],{allowFailure:!0}),await y(p(s,".config","systemd","user",`${f}.service`)).catch(t=>{if(t.code!=="ENOENT")throw t}),await l("systemctl",["--user","daemon-reload"],{allowFailure:!0}),await l("systemctl",["--user","stop",`${f}.service`],{allowFailure:!0});return}if(process.platform==="darwin"){const t=process.getuid?.();if(t===void 0)throw new Error("Unable to determine current uid for launchctl");const e=p(s,"Library","LaunchAgents",`${v}.plist`);await y(e).catch(r=>{if(r.code!=="ENOENT")throw r}),await l("launchctl",["bootout",`gui/${t}/${v}`],{allowFailure:!0});return}throw new Error("Only Linux and macOS are supported")}function H(s){return new Promise(t=>{const e=setTimeout(()=>{s.kill("SIGKILL")},8e3);s.once("exit",()=>{clearTimeout(e),t()})})}export{q as AgentSupervisor,j as readWorkspacePathFromConfig,_ as resolveAgentEntries,nt as runSupervisor};
1
+ import{spawn as P}from"node:child_process";import{randomUUID as T}from"node:crypto";import{createRequire as x}from"node:module";import{dirname as m,join as d}from"node:path";import{parse as S}from"yaml";import{readFile as M,unlink as y}from"node:fs/promises";import{readControlSocketPath as E,startControlServer as I}from"./control.js";import{MaintenanceScheduler as k}from"./maintenance.js";import{readUpgradeChannel as b}from"./config-file.js";import{currentBundledSkillsDir as C,defaultAgentHomes as $,installRuntime as N,syncBundledSkills as O}from"./runtime.js";import{darwinCommands as F,launchdLabel as v,linuxCommands as G,serviceName as f}from"./service.js";import{runCommand as u}from"./exec.js";import{AutoUpgradeScheduler as L}from"./auto-upgrade.js";import{isAgentActivityResponse as R}from"@messenger-agent/shared/agent-activity";const w=x(import.meta.url);function _(){return[{name:"codex",entry:w.resolve("@messenger-agent/codex-agent")},{name:"claude",entry:w.resolve("@messenger-agent/claude-agent")},{name:"workspace",entry:w.resolve("@messenger-agent/messenger-agent")}]}function j(s,t){let e=s;for(const r of t){if(!e||typeof e!="object"||Array.isArray(e))return;e=e[r]}return e}async function B(s){const t=S(await M(s,"utf8")),e=j(t,["workspaces"]);if(Array.isArray(e)){const r=e.find(i=>!!i&&typeof i=="object"&&!Array.isArray(i)&&typeof i.path=="string");if(typeof r?.path=="string"&&r.path.trim())return r.path}return m(s)}class q{options;stopping=!1;running;restartBaseMs;restartMaxMs;constructor(t){this.options=t,this.running=(t.agents??_()).map(e=>({definition:e,restartAttempts:0})),this.restartBaseMs=t.restartBaseMs??1e3,this.restartMaxMs=t.restartMaxMs??3e4}start(){for(const t of this.running)this.startAgent(t)}async stop(){this.stopping=!0;for(const t of this.running)t.restartTimer&&clearTimeout(t.restartTimer),t.process?.kill("SIGTERM");await Promise.all(this.running.map(t=>t.process).filter(t=>!!t&&t.exitCode===null&&!t.killed).map(t=>new Promise(e=>{const r=setTimeout(()=>{t.kill("SIGKILL"),e()},8e3);t.once("exit",()=>{clearTimeout(r),e()})})))}restartAgent(t){const e=this.running.find(({definition:r})=>r.name===t);return e?(e.restartPromise||(e.restartPromise=this.restartAgentProcess(e).finally(()=>{e.restartPromise=void 0})),e.restartPromise):Promise.reject(new Error(`Agent is not managed by this client: ${t}`))}async activityStatus(t=1e3){const[e,r]=await Promise.all([this.agentActivityStatus("codex",t),this.agentActivityStatus("claude",t)]),i=[e,r].filter(n=>n.available);return{active:i.reduce((n,a)=>n+a.active,0),waiting:i.reduce((n,a)=>n+a.waiting,0),agents:{codex:e,claude:r}}}agentActivityStatus(t,e){const r=this.running.find(({definition:i})=>i.name===t)?.process;return!r||!r.connected||!r.send?Promise.resolve({available:!1,error:`${t} agent is not connected`}):new Promise(i=>{const n=T(),a={type:"client.activity.request",requestId:n};let l=!1;const c=o=>{l||(l=!0,clearTimeout(A),r.off("message",p),i(o))},p=o=>{!R(o)||o.requestId!==n||o.agent!==t||c({available:!0,...o.snapshot})},A=setTimeout(()=>c({available:!1,error:`${t} agent did not respond`}),e);r.on("message",p),r.send(a,o=>{o&&c({available:!1,error:o.message})})})}async restartAgentProcess(t){t.restartTimer&&(clearTimeout(t.restartTimer),t.restartTimer=void 0);const e=t.process;if(e&&e.exitCode===null&&!e.killed&&(e.kill("SIGTERM"),await H(e)),this.stopping)throw new Error("Client service is stopping");t.restartAttempts=0,this.startAgent(t)}startAgent(t){const e={...process.env,AGENT_CONFIG_PATH:this.options.configPath};t.definition.name==="workspace"&&(delete e.OPENAI_API_KEY,delete e.CODEX_API_KEY,delete e.ANTHROPIC_API_KEY);const r=(this.options.spawnProcess??P)(process.execPath,[t.definition.entry],{cwd:this.options.workspacePath,env:e,stdio:["inherit","inherit","inherit","ipc"]});t.process=r,t.definition.name==="workspace"&&r.on("message",i=>{i&&typeof i=="object"&&"type"in i&&i.type==="client.workspaces.changed"&&Promise.all([this.restartAgent("codex"),this.restartAgent("claude")]).catch(n=>{console.error("[client] failed to reload agents after workspace configuration changed",n)})}),console.log(`[client] started ${t.definition.name}-agent pid=${r.pid}`),r.once("exit",(i,n)=>{if(t.process=void 0,this.stopping||t.restartPromise)return;const a=Math.min(this.restartBaseMs*2**t.restartAttempts,this.restartMaxMs);t.restartAttempts+=1,console.error(`[client] ${t.definition.name}-agent exited with code=${i??"null"} signal=${n??"null"}; restarting in ${a}ms`),t.restartTimer=setTimeout(()=>{t.restartTimer=void 0,this.startAgent(t)},a)})}}async function nt(s){await O(C(),$());const t=await B(s),e=new q({configPath:s,workspacePath:t}),r=await E(s),i=new k({statePath:d(m(r),"maintenance.json"),getActivity:()=>e.activityStatus(),execute:(c,p)=>U(c,s,e,p)});await i.start();const n=new L({statePath:d(m(r),"auto-upgrade.json"),maintenance:i,getChannel:()=>b(s)});await n.start();const a=await I(r,{restartAgent:c=>e.restartAgent(c),getActivity:()=>e.activityStatus(),maintenance:i});e.start();const l=async()=>{n.stop(),i.stop(),await a.close(),await e.stop(),process.exit(0)};process.once("SIGINT",()=>{l()}),process.once("SIGTERM",()=>{l()})}async function U(s,t,e,r){if(s.type==="upgrade"){await N({version:s.version,configPath:t}),await r("restarting"),await h(g().restart);return}if(s.type==="restart"&&s.agent){await e.restartAgent(s.agent);return}if(s.type==="restart"){await r("restarting"),await h(g().restart);return}if(s.type==="stop"){await r("stopping"),await h(g().stop);return}await r("stopping"),await K()}function g(){return process.platform==="darwin"?F():G()}async function h(s){const t=await u("/bin/sh",["-lc",s],{allowFailure:!0});if(t.status!==0)throw new Error(t.stderr||t.stdout||`Service command failed: ${s}`)}async function K(){const s=process.env.HOME??"";if(process.platform==="linux"){await u("systemctl",["--user","disable",`${f}.service`],{allowFailure:!0}),await y(d(s,".config","systemd","user",`${f}.service`)).catch(t=>{if(t.code!=="ENOENT")throw t}),await u("systemctl",["--user","daemon-reload"],{allowFailure:!0}),await u("systemctl",["--user","stop",`${f}.service`],{allowFailure:!0});return}if(process.platform==="darwin"){const t=process.getuid?.();if(t===void 0)throw new Error("Unable to determine current uid for launchctl");const e=d(s,"Library","LaunchAgents",`${v}.plist`);await y(e).catch(r=>{if(r.code!=="ENOENT")throw r}),await u("launchctl",["bootout",`gui/${t}/${v}`],{allowFailure:!0});return}throw new Error("Only Linux and macOS are supported")}function H(s){return new Promise(t=>{const e=setTimeout(()=>{s.kill("SIGKILL")},8e3);s.once("exit",()=>{clearTimeout(e),t()})})}export{q as AgentSupervisor,B as readWorkspacePathFromConfig,_ as resolveAgentEntries,nt as runSupervisor};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@messenger-agent/client",
3
- "version": "0.25.0",
3
+ "version": "0.26.0-alpha.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -20,10 +20,10 @@
20
20
  "dependencies": {
21
21
  "cac": "^7.0.0",
22
22
  "yaml": "^2.9.0",
23
- "@messenger-agent/shared": "0.25.0",
24
- "@messenger-agent/messenger-agent": "0.25.0",
25
- "@messenger-agent/claude-agent": "0.25.0",
26
- "@messenger-agent/codex-agent": "0.25.0"
23
+ "@messenger-agent/shared": "0.26.0-alpha.0",
24
+ "@messenger-agent/messenger-agent": "0.26.0-alpha.0",
25
+ "@messenger-agent/claude-agent": "0.26.0-alpha.0",
26
+ "@messenger-agent/codex-agent": "0.26.0-alpha.0"
27
27
  },
28
28
  "scripts": {
29
29
  "build": "rm -rf dist && tsc -p tsconfig.json && node ../../scripts/copy-client-assets.mjs && chmod +x dist/index.js",