@messenger-agent/client 0.34.1 → 0.35.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.
@@ -27,6 +27,7 @@ export type SupervisorOptions = {
27
27
  agents?: ManagedAgent[];
28
28
  restartBaseMs?: number;
29
29
  restartMaxMs?: number;
30
+ workspaceReloadTimeoutMs?: number;
30
31
  spawnProcess?: typeof spawn;
31
32
  };
32
33
  export declare function resolveAgentEntries(): ManagedAgent[];
@@ -37,6 +38,7 @@ export declare class AgentSupervisor {
37
38
  private readonly running;
38
39
  private readonly restartBaseMs;
39
40
  private readonly restartMaxMs;
41
+ private readonly workspaceReloadTimeoutMs;
40
42
  constructor(options: SupervisorOptions);
41
43
  start(): void;
42
44
  stop(): Promise<void>;
@@ -44,6 +46,8 @@ export declare class AgentSupervisor {
44
46
  activityStatus(timeoutMs?: number): Promise<ClientActivityStatus>;
45
47
  private agentActivityStatus;
46
48
  private restartAgentProcess;
49
+ private reloadAgentWorkspaces;
50
+ private requestAgentWorkspacesReload;
47
51
  private startAgent;
48
52
  }
49
53
  export declare function runSupervisor(configPath: string): Promise<void>;
@@ -1 +1 @@
1
- import{spawn as b}from"node:child_process";import{randomUUID as M}from"node:crypto";import{createRequire as k}from"node:module";import{dirname as h,join as f,resolve as w}from"node:path";import{parse as C}from"yaml";import{readFile as E,unlink as y}from"node:fs/promises";import{readAgentServer as v,readClientPath as A,readUpgradeChannel as I,writeClientPath as $}from"./config-file.js";import{readControlSocketPath as L,startControlServer as N}from"./control.js";import{MaintenanceScheduler as F}from"./maintenance.js";import{currentBundledSkillsDir as O,currentPackageVersion as R,defaultAgentHomes as D,installRuntime as P,rollbackRuntime as S,syncBundledSkills as G}from"./runtime.js";import{launchdLabel as _,serviceCommandsForConfig as j,serviceName as B}from"./service.js";import{legacyLaunchdLabel as q,legacyServiceName as U}from"./identity.js";import{legacyCodingAgentPaths as T}from"./paths.js";import{runCommand as u}from"./exec.js";import{AutoUpgradeScheduler as H}from"./auto-upgrade.js";import{migrateLegacyClient as K}from"./migration.js";import{scheduleMigrationHandoff as Y,shouldAttemptAutomaticMigration as V}from"./install.js";import{isAgentActivityResponse as W}from"@messenger-agent/shared/agent-activity";const g=k(import.meta.url);function X(){return[{name:"codex",entry:g.resolve("@messenger-agent/codex-agent")},{name:"claude",entry:g.resolve("@messenger-agent/claude-agent")},{name:"workspace",entry:g.resolve("@messenger-agent/messenger-agent")}]}function z(i,t){let e=i;for(const r of t){if(!e||typeof e!="object"||Array.isArray(e))return;e=e[r]}return e}async function J(i){const t=C(await E(i,"utf8")),e=z(t,["workspaces"]);if(Array.isArray(e)){const r=e.find(s=>!!s&&typeof s=="object"&&!Array.isArray(s)&&typeof s.path=="string");if(typeof r?.path=="string"&&r.path.trim())return r.path}return h(i)}class Q{options;stopping=!1;running;restartBaseMs;restartMaxMs;constructor(t){this.options=t,this.running=(t.agents??X()).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)]),s=[e,r].filter(a=>a.available);return{active:s.reduce((a,n)=>a+n.active,0),waiting:s.reduce((a,n)=>a+n.waiting,0),agents:{codex:e,claude:r}}}agentActivityStatus(t,e){const r=this.running.find(({definition:s})=>s.name===t)?.process;return!r||!r.connected||!r.send?Promise.resolve({available:!1,error:`${t} agent is not connected`}):new Promise(s=>{const a=M(),n={type:"client.activity.request",requestId:a};let o=!1;const l=c=>{o||(o=!0,clearTimeout(x),r.off("message",m),s(c))},m=c=>{!W(c)||c.requestId!==a||c.agent!==t||l({available:!0,...c.snapshot})},x=setTimeout(()=>l({available:!1,error:`${t} agent did not respond`}),e);r.on("message",m),r.send(n,c=>{c&&l({available:!1,error:c.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 rt(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??b)(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",s=>{s&&typeof s=="object"&&"type"in s&&s.type==="client.workspaces.changed"&&Promise.all([this.restartAgent("codex"),this.restartAgent("claude")]).catch(a=>{console.error("[client] failed to reload agents after workspace configuration changed",a)})}),console.log(`[client] started ${t.definition.name}-agent pid=${r.pid}`),r.once("exit",(s,a)=>{if(t.process=void 0,this.stopping||t.restartPromise)return;const n=Math.min(this.restartBaseMs*2**t.restartAttempts,this.restartMaxMs);t.restartAttempts+=1,console.error(`[client] ${t.definition.name}-agent exited with code=${s??"null"} signal=${a??"null"}; restarting in ${n}ms`),t.restartTimer=setTimeout(()=>{t.restartTimer=void 0,this.startAgent(t)},n)})}}async function Pt(i){if(await Z(i))return;await G(O(),D());const t=await J(i),e=new Q({configPath:i,workspacePath:t}),r=await L(i),s=new F({statePath:f(h(r),"maintenance.json"),getActivity:()=>e.activityStatus(),execute:(l,m)=>tt(l,i,e,m)});await s.start();const a=new H({statePath:f(h(r),"auto-upgrade.json"),maintenance:s,getChannel:()=>I(i)});await a.start();const n=await N(r,{restartAgent:l=>e.restartAgent(l),getActivity:()=>e.activityStatus(),maintenance:s});e.start();const o=async()=>{a.stop(),s.stop(),await n.close(),await e.stop(),process.exit(0)};process.once("SIGINT",()=>{o()}),process.once("SIGTERM",()=>{o()})}async function Z(i,t={}){const e=T();if(w(i)!==w(e.configPath))return!1;const r=await(t.currentVersion??R)();if(!await(t.shouldAttempt??V)(e.rootDir,r))return!1;let s;try{const a=await(t.migrate??K)();if(!a.migrated)return!1;const n=await(t.readPath??A)(a.paths.configPath),o=await(t.readServer??v)(a.paths.configPath);return s=await(t.install??P)({version:r,configPath:a.paths.configPath,runtimeDir:a.paths.runtimeDir,binDir:a.paths.binDir,path:n,server:o}),await(t.handoff??Y)(a,s,a.paths.configPath,n),!0}catch(a){return s&&await(t.rollback??S)(s).catch(()=>{}),console.error(`[client] automatic legacy migration failed: ${a instanceof Error?a.message:String(a)}`),!1}}async function tt(i,t,e,r){if(i.type==="upgrade"){i.path&&await $(t,i.path);const s=await P({version:i.version,configPath:t,server:await v(t),path:i.path??await A(t)});await r("restarting");try{await d(p(t).restart)}catch(a){throw await S(s),await d(p(t).restart).catch(()=>{}),a}return}if(i.type==="restart"&&i.agent){await e.restartAgent(i.agent);return}if(i.type==="restart"){await r("restarting"),await d(p(t).restart);return}if(i.type==="stop"){await r("stopping"),await d(p(t).stop);return}await r("stopping"),await et(t)}function p(i){return j(i)}async function d(i){const t=await u("/bin/sh",["-lc",i],{allowFailure:!0});if(t.status!==0)throw new Error(t.stderr||t.stdout||`Service command failed: ${i}`)}async function et(i){const t=process.env.HOME??"",e=i===T().configPath,r=e?U:B,s=e?q:_;if(process.platform==="linux"){await u("systemctl",["--user","disable",`${r}.service`],{allowFailure:!0}),await y(f(t,".config","systemd","user",`${r}.service`)).catch(a=>{if(a.code!=="ENOENT")throw a}),await u("systemctl",["--user","daemon-reload"],{allowFailure:!0}),await u("systemctl",["--user","stop",`${r}.service`],{allowFailure:!0});return}if(process.platform==="darwin"){const a=process.getuid?.();if(a===void 0)throw new Error("Unable to determine current uid for launchctl");const n=f(t,"Library","LaunchAgents",`${s}.plist`);await y(n).catch(o=>{if(o.code!=="ENOENT")throw o}),await u("launchctl",["bootout",`gui/${a}/${s}`],{allowFailure:!0});return}throw new Error("Only Linux and macOS are supported")}function rt(i){return new Promise(t=>{const e=setTimeout(()=>{i.kill("SIGKILL")},8e3);i.once("exit",()=>{clearTimeout(e),t()})})}export{Q as AgentSupervisor,Z as migrateLegacyServiceAtStartup,J as readWorkspacePathFromConfig,X as resolveAgentEntries,Pt as runSupervisor};
1
+ import{spawn as x}from"node:child_process";import{randomUUID as y}from"node:crypto";import{createRequire as $}from"node:module";import{dirname as g,join as m,resolve as v}from"node:path";import{parse as b}from"yaml";import{readFile as E,unlink as A}from"node:fs/promises";import{readAgentServer as k,readClientPath as P,readUpgradeChannel as C,writeClientPath as I}from"./config-file.js";import{readControlSocketPath as R,startControlServer as L}from"./control.js";import{MaintenanceScheduler as q}from"./maintenance.js";import{currentBundledSkillsDir as N,currentPackageVersion as F,defaultAgentHomes as O,installRuntime as T,rollbackRuntime as S,syncBundledSkills as j}from"./runtime.js";import{launchdLabel as D,serviceCommandsForConfig as G,serviceName as W}from"./service.js";import{legacyLaunchdLabel as _,legacyServiceName as B}from"./identity.js";import{legacyCodingAgentPaths as M}from"./paths.js";import{runCommand as d}from"./exec.js";import{AutoUpgradeScheduler as U}from"./auto-upgrade.js";import{migrateLegacyClient as H}from"./migration.js";import{scheduleMigrationHandoff as K,shouldAttemptAutomaticMigration as Y}from"./install.js";import{isAgentActivityResponse as V}from"@messenger-agent/shared/agent-activity";import{isAgentWorkspacesReloadResponse as X}from"@messenger-agent/shared/agent-workspaces";const h=$(import.meta.url);function z(){return[{name:"codex",entry:h.resolve("@messenger-agent/codex-agent")},{name:"claude",entry:h.resolve("@messenger-agent/claude-agent")},{name:"workspace",entry:h.resolve("@messenger-agent/messenger-agent")}]}function J(a,t){let e=a;for(const r of t){if(!e||typeof e!="object"||Array.isArray(e))return;e=e[r]}return e}async function Q(a){const t=b(await E(a,"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 g(a)}class Z{options;stopping=!1;running;restartBaseMs;restartMaxMs;workspaceReloadTimeoutMs;constructor(t){this.options=t,this.running=(t.agents??z()).map(e=>({definition:e,restartAttempts:0})),this.restartBaseMs=t.restartBaseMs??1e3,this.restartMaxMs=t.restartMaxMs??3e4,this.workspaceReloadTimeoutMs=t.workspaceReloadTimeoutMs??5e3}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(s=>s.available);return{active:i.reduce((s,o)=>s+o.active,0),waiting:i.reduce((s,o)=>s+o.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 s=y(),o={type:"client.activity.request",requestId:s};let c=!1;const l=n=>{c||(c=!0,clearTimeout(w),r.off("message",u),i(n))},u=n=>{!V(n)||n.requestId!==s||n.agent!==t||l({available:!0,...n.snapshot})},w=setTimeout(()=>l({available:!1,error:`${t} agent did not respond`}),e);r.on("message",u),r.send(o,n=>{n&&l({available:!1,error:n.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 st(e)),this.stopping)throw new Error("Client service is stopping");t.restartAttempts=0,this.startAgent(t)}async reloadAgentWorkspaces(){await Promise.all([this.requestAgentWorkspacesReload("codex"),this.requestAgentWorkspacesReload("claude")])}requestAgentWorkspacesReload(t){const e=this.running.find(({definition:r})=>r.name===t)?.process;return!e||!e.connected||!e.send?Promise.reject(new Error(`${t} agent is not connected`)):new Promise((r,i)=>{const s=y(),o={type:"client.workspaces.reload.request",requestId:s};let c=!1;const l=n=>{c||(c=!0,clearTimeout(w),e.off("message",u),n?i(n):r())},u=n=>{if(!(!X(n)||n.requestId!==s||n.agent!==t)){if(!n.success){l(new Error(`${t} agent failed to reload workspaces: ${n.error??"unknown error"}`));return}console.log(`[client] reloaded ${t}-agent workspaces count=${n.workspaceCount??0}`),l()}},w=setTimeout(()=>l(new Error(`${t} agent did not reload workspaces`)),this.workspaceReloadTimeoutMs);e.on("message",u),e.send(o,n=>{n&&l(n)})})}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??x)(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"&&this.reloadAgentWorkspaces().catch(s=>{console.error("[client] failed to reload agents after workspace configuration changed",s)})}),console.log(`[client] started ${t.definition.name}-agent pid=${r.pid}`),r.once("exit",(i,s)=>{if(t.process=void 0,this.stopping||t.restartPromise)return;const o=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=${s??"null"}; restarting in ${o}ms`),t.restartTimer=setTimeout(()=>{t.restartTimer=void 0,this.startAgent(t)},o)})}}async function Tt(a){if(await tt(a))return;await j(N(),O());const t=await Q(a),e=new Z({configPath:a,workspacePath:t}),r=await R(a),i=new q({statePath:m(g(r),"maintenance.json"),getActivity:()=>e.activityStatus(),execute:(l,u)=>et(l,a,e,u)});await i.start();const s=new U({statePath:m(g(r),"auto-upgrade.json"),maintenance:i,getChannel:()=>C(a)});await s.start();const o=await L(r,{restartAgent:l=>e.restartAgent(l),getActivity:()=>e.activityStatus(),maintenance:i});e.start();const c=async()=>{s.stop(),i.stop(),await o.close(),await e.stop(),process.exit(0)};process.once("SIGINT",()=>{c()}),process.once("SIGTERM",()=>{c()})}async function tt(a,t={}){const e=M();if(v(a)!==v(e.configPath))return!1;const r=await(t.currentVersion??F)();if(!await(t.shouldAttempt??Y)(e.rootDir,r))return!1;let i;try{const s=await(t.migrate??H)();if(!s.migrated)return!1;const o=await(t.readPath??P)(s.paths.configPath),c=await(t.readServer??k)(s.paths.configPath);return i=await(t.install??T)({version:r,configPath:s.paths.configPath,runtimeDir:s.paths.runtimeDir,binDir:s.paths.binDir,path:o,server:c}),await(t.handoff??K)(s,i,s.paths.configPath,o),!0}catch(s){return i&&await(t.rollback??S)(i).catch(()=>{}),console.error(`[client] automatic legacy migration failed: ${s instanceof Error?s.message:String(s)}`),!1}}async function et(a,t,e,r){if(a.type==="upgrade"){a.path&&await I(t,a.path);const i=await T({version:a.version,configPath:t,server:await k(t),path:a.path??await P(t)});await r("restarting");try{await f(p(t).restart)}catch(s){throw await S(i),await f(p(t).restart).catch(()=>{}),s}return}if(a.type==="restart"&&a.agent){await e.restartAgent(a.agent);return}if(a.type==="restart"){await r("restarting"),await f(p(t).restart);return}if(a.type==="stop"){await r("stopping"),await f(p(t).stop);return}await r("stopping"),await rt(t)}function p(a){return G(a)}async function f(a){const t=await d("/bin/sh",["-lc",a],{allowFailure:!0});if(t.status!==0)throw new Error(t.stderr||t.stdout||`Service command failed: ${a}`)}async function rt(a){const t=process.env.HOME??"",e=a===M().configPath,r=e?B:W,i=e?_:D;if(process.platform==="linux"){await d("systemctl",["--user","disable",`${r}.service`],{allowFailure:!0}),await A(m(t,".config","systemd","user",`${r}.service`)).catch(s=>{if(s.code!=="ENOENT")throw s}),await d("systemctl",["--user","daemon-reload"],{allowFailure:!0}),await d("systemctl",["--user","stop",`${r}.service`],{allowFailure:!0});return}if(process.platform==="darwin"){const s=process.getuid?.();if(s===void 0)throw new Error("Unable to determine current uid for launchctl");const o=m(t,"Library","LaunchAgents",`${i}.plist`);await A(o).catch(c=>{if(c.code!=="ENOENT")throw c}),await d("launchctl",["bootout",`gui/${s}/${i}`],{allowFailure:!0});return}throw new Error("Only Linux and macOS are supported")}function st(a){return new Promise(t=>{const e=setTimeout(()=>{a.kill("SIGKILL")},8e3);a.once("exit",()=>{clearTimeout(e),t()})})}export{Z as AgentSupervisor,tt as migrateLegacyServiceAtStartup,Q as readWorkspacePathFromConfig,z as resolveAgentEntries,Tt as runSupervisor};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@messenger-agent/client",
3
- "version": "0.34.1",
3
+ "version": "0.35.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -21,10 +21,10 @@
21
21
  "dependencies": {
22
22
  "cac": "^7.0.0",
23
23
  "yaml": "^2.9.0",
24
- "@messenger-agent/claude-agent": "0.34.1",
25
- "@messenger-agent/codex-agent": "0.34.1",
26
- "@messenger-agent/shared": "0.34.1",
27
- "@messenger-agent/messenger-agent": "0.34.1"
24
+ "@messenger-agent/claude-agent": "0.35.0",
25
+ "@messenger-agent/codex-agent": "0.35.0",
26
+ "@messenger-agent/messenger-agent": "0.35.0",
27
+ "@messenger-agent/shared": "0.35.0"
28
28
  },
29
29
  "scripts": {
30
30
  "build": "rm -rf dist && tsc -p tsconfig.json && node ../../scripts/copy-client-assets.mjs && chmod +x dist/index.js",