@messenger-agent/client 0.32.2 → 0.33.0-alpha.5

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.
package/dist/args.d.ts CHANGED
@@ -23,6 +23,13 @@ export type UpgradeArgs = {
23
23
  export type VersionArgs = {
24
24
  command: "version";
25
25
  };
26
+ export type SelfCheckArgs = {
27
+ command: "self-check";
28
+ };
29
+ export type CompleteMigrationArgs = {
30
+ command: "complete-migration";
31
+ handoffPath: string;
32
+ };
26
33
  export type SessionsArgs = {
27
34
  command: "sessions";
28
35
  json: boolean;
@@ -60,7 +67,7 @@ export type UninstallArgs = {
60
67
  delaySeconds?: number;
61
68
  allowWaiting: boolean;
62
69
  };
63
- export type CliArgs = InstallArgs | UpgradeArgs | VersionArgs | SessionsArgs | MaintenanceArgs | RunServiceArgs | StatusArgs | StartArgs | RestartArgs | StopArgs | UninstallArgs;
70
+ export type CliArgs = InstallArgs | UpgradeArgs | VersionArgs | SelfCheckArgs | CompleteMigrationArgs | SessionsArgs | MaintenanceArgs | RunServiceArgs | StatusArgs | StartArgs | RestartArgs | StopArgs | UninstallArgs;
64
71
  export declare class UsageError extends Error {
65
72
  }
66
73
  export declare function parseCliArgs(argv: string[], env?: NodeJS.ProcessEnv): CliArgs;
package/dist/args.js CHANGED
@@ -1,5 +1,5 @@
1
- import v from"cac";import{defaultConfigPath as u,defaultDataDir as U,resolvePath as d}from"./paths.js";import{normalizeReleaseChannel as S}from"./runtime.js";const $="CODING_AGENT_TUNNEL_SERVER_URL",C=["install","upgrade","sessions","maintenance","status","start","restart","stop","uninstall"],W=[...C,"run-service"];class r extends Error{}function R(n,t=process.env){if(n[0]==="--version"||n[0]==="-v"){if(n.length>1)throw new r(`Unknown option for version: ${n[1]}`);return{command:"version"}}const o=n[0];if(!o||o==="--help"||o==="-h")throw new r(b());if(n.includes("--"))throw new r(`Unknown option for ${o}: --`);P(n);let i;const e=h({env:t,includeInternalCommands:!0,onParsed(a){i=a}});try{e.parse(["node","coding-agent",...n])}catch(a){const s=a instanceof Error?a.message:String(a);throw new r(I(s,n))}if(!i)throw new r(`Unknown command: ${o}`);return i}function b(){const n=h({includeInternalCommands:!1});return n.help(),`${x(()=>n.outputHelp())}
1
+ import U from"cac";import{defaultConfigPath as u,defaultDataDir as $,resolvePath as d}from"./paths.js";import{normalizeReleaseChannel as C}from"./runtime.js";import{cliName as m,productName as p}from"./identity.js";const N="MESSENGER_AGENT_TUNNEL_SERVER_URL",E="CODING_AGENT_TUNNEL_SERVER_URL",W=["install","upgrade","sessions","maintenance","status","start","restart","stop","uninstall"],b=[...W,"run-service","self-check","complete-migration"];class r extends Error{}function L(n,t=process.env){if(n[0]==="--version"||n[0]==="-v"){if(n.length>1)throw new r(`Unknown option for version: ${n[1]}`);return{command:"version"}}const o=n[0];if(!o||o==="--help"||o==="-h")throw new r(P());if(n.includes("--"))throw new r(`Unknown option for ${o}: --`);I(n);let a;const e=g({env:t,includeInternalCommands:!0,onParsed(i){a=i}});try{e.parse(["node",m,...n])}catch(i){const s=i instanceof Error?i.message:String(i);throw new r(A(s,n))}if(!a)throw new r(`Unknown command: ${o}`);return a}function P(){const n=g({includeInternalCommands:!1});return n.help(),`${x(()=>n.outputHelp())}
2
2
 
3
3
  Global Options:
4
- -v, --version Show the installed client version`}function h(n){const t=v("coding-agent"),o=n.env??process.env,i=n.onParsed??(()=>{});t.command("install","Install and configure the local coding-agent service").option("--tunnel-id <id>","Tunnel id").option("--token <token>","Tunnel token").option("--workspace <path>","Workspace path (default: ~/messenger-workspace)").option("--server <url>","Agent bridge server URL").option("--server-url <wss-url>","Tunnel server URL").option("--config <path>","Config file path",{default:u}).option("--data-dir <path>","Data directory",{default:U}).option("--workspace-id <id>","Workspace id").option("--workspace-name <name>","Workspace display name").option("--channel <channel>","Auto-upgrade release channel: latest, beta, or alpha").action(e=>{const a=c(e.tunnelId),s=c(e.token),l=c(e.workspace);i({command:"install",tunnelId:a,token:s,workspace:l?d(l):void 0,server:c(e.server),serverUrl:c(e.serverUrl)??o[$],configPath:d(f(e.config,"--config")),dataDir:d(f(e.dataDir,"--data-dir")),workspaceId:c(e.workspaceId),workspaceName:c(e.workspaceName),channel:g(e.channel)})}),t.command("upgrade","Upgrade the local coding-agent runtime").option("--version <version>","Runtime version or release channel",{default:"latest"}).option("--channel <channel>","Auto-upgrade release channel: latest, beta, or alpha").option("--config <path>","Config file path",{default:u}).option("--delay <seconds>","Schedule the upgrade after a delay").option("--allow-waiting","Allow upgrade while sessions wait for user input").action(e=>{const a=p(e.delay),s=e.allowWaiting===!0;w(s,a),i({command:"upgrade",version:f(e.version,"--version"),configPath:d(f(e.config,"--config")),delaySeconds:a,allowWaiting:s,channel:g(e.channel)})}),t.command("sessions","Show active and waiting client sessions").option("--json","Print machine-readable JSON").option("--config <path>","Config file path",{default:u}).action(e=>{i({command:"sessions",json:e.json===!0,configPath:d(f(e.config,"--config"))})}),t.command("maintenance <action>","Inspect or cancel scheduled maintenance").option("--json","Print machine-readable JSON").option("--config <path>","Config file path",{default:u}).action((e,a)=>{if(e!=="status"&&e!=="cancel")throw new r(`Unknown maintenance action: ${e}`);i({command:"maintenance",action:e,json:a.json===!0,configPath:d(f(a.config,"--config"))})});for(const e of["status","start"])t.command(e,`${y(e)} the local coding-agent service`).action(()=>{i({command:e})});t.command("restart [agent]","Restart the local service or a specific agent").option("--delay <seconds>","Schedule the restart after a delay").option("--allow-waiting","Allow restart while sessions wait for user input").action((e,a)=>{if(e!==void 0&&e!=="codex"&&e!=="claude")throw new r(`Unknown agent for restart: ${e}`);const s=p(a.delay),l=a.allowWaiting===!0;w(l,s),i({command:"restart",agent:e,delaySeconds:s,allowWaiting:l})});for(const e of["stop","uninstall"])t.command(e,`${y(e)} the local coding-agent service`).option("--delay <seconds>",`Schedule the ${e} after a delay`).option("--allow-waiting",`Allow ${e} while sessions wait for user input`).action(a=>{const s=p(a.delay),l=a.allowWaiting===!0;w(l,s),i({command:e,delaySeconds:s,allowWaiting:l})});return n.includeInternalCommands&&t.command("run-service","Run the managed service supervisor").option("--config <path>","Config file path").action(e=>{const a=c(e.config);i({command:"run-service",configPath:a?d(a):void 0})}),t}function f(n,t){const o=c(n);if(!o)throw new r(`Missing value for ${t}`);return o}function c(n){return typeof n=="string"?n:void 0}function g(n){const t=c(n);if(t!==void 0)try{return S(t)}catch{throw new r(`Unknown release channel for --channel: ${t} (expected latest, beta, or alpha)`)}}function p(n){if(n===void 0)return;const t=typeof n=="number"?String(n):f(n,"--delay");if(!/^\d+$/.test(t))throw new r("--delay must be an integer between 1 and 86400 seconds");const o=Number.parseInt(t,10);if(o<1||o>86400)throw new r("--delay must be an integer between 1 and 86400 seconds");return o}function w(n,t){if(n&&t===void 0)throw new r("--allow-waiting requires --delay")}function I(n,t){if(n.startsWith("Unknown option `")){const o=m(t);if(o)return`Unknown option for ${t[0]}: ${o}`}if(n.startsWith("option `")){const o=/^option `([^` ]+)/.exec(n);if(o)return`Missing value for ${o[1]}`}if(n.startsWith("Unused args:")){const o=t.slice(1).find(i=>!i.startsWith("-"));if(o)return`Unknown option for ${t[0]}: ${o}`}return n}function m(n){const t=n[0];if(!t||!k(t))return n.find(i=>i.startsWith("-"));const o=A(t);for(const i of n.slice(1)){if(!i.startsWith("-"))continue;const e=i.includes("=")?i.slice(0,i.indexOf("=")):i;if(!o.has(e))return e}}function P(n){const t=n[0];if(!t||!k(t))return;const o=m(n);if(o)throw new r(`Unknown option for ${t}: ${o}`)}function A(n){switch(n){case"install":return new Set(["--tunnel-id","--token","--workspace","--server","--server-url","--config","--data-dir","--workspace-id","--workspace-name","--channel"]);case"upgrade":return new Set(["--version","--channel","--config","--delay","--allow-waiting"]);case"sessions":case"maintenance":return new Set(["--json","--config"]);case"restart":case"stop":case"uninstall":return new Set(["--delay","--allow-waiting"]);case"run-service":return new Set(["--config"]);default:return new Set}}function k(n){return W.includes(n)}function x(n){const t=console.info,o=[];console.info=(...i)=>{o.push(i.map(String).join(" "))};try{n()}finally{console.info=t}return o.join(`
5
- `)}function y(n){return`${n.charAt(0).toUpperCase()}${n.slice(1)}`}export{r as UsageError,R as parseCliArgs,b as usage};
4
+ -v, --version Show the installed client version`}function g(n){const t=U(m),o=n.env??process.env,a=n.onParsed??(()=>{});t.command("install",`Install and configure the local ${p} service`).option("--tunnel-id <id>","Tunnel id").option("--token <token>","Tunnel token").option("--workspace <path>","Workspace path (default: ~/messenger-workspace)").option("--server <url>","Agent bridge server URL").option("--server-url <wss-url>","Tunnel server URL").option("--config <path>","Config file path",{default:u}).option("--data-dir <path>","Data directory",{default:$}).option("--workspace-id <id>","Workspace id").option("--workspace-name <name>","Workspace display name").option("--channel <channel>","Auto-upgrade release channel: latest, beta, or alpha").action(e=>{const i=c(e.tunnelId),s=c(e.token),l=c(e.workspace);a({command:"install",tunnelId:i,token:s,workspace:l?d(l):void 0,server:c(e.server),serverUrl:c(e.serverUrl)??o[N]??o[E],configPath:d(f(e.config,"--config")),dataDir:d(f(e.dataDir,"--data-dir")),workspaceId:c(e.workspaceId),workspaceName:c(e.workspaceName),channel:k(e.channel)})}),t.command("upgrade",`Upgrade the local ${p} runtime`).option("--version <version>","Runtime version or release channel",{default:"latest"}).option("--channel <channel>","Auto-upgrade release channel: latest, beta, or alpha").option("--config <path>","Config file path",{default:u}).option("--delay <seconds>","Schedule the upgrade after a delay").option("--allow-waiting","Allow upgrade while sessions wait for user input").action(e=>{const i=h(e.delay),s=e.allowWaiting===!0;w(s,i),a({command:"upgrade",version:f(e.version,"--version"),configPath:d(f(e.config,"--config")),delaySeconds:i,allowWaiting:s,channel:k(e.channel)})}),t.command("sessions","Show active and waiting client sessions").option("--json","Print machine-readable JSON").option("--config <path>","Config file path",{default:u}).action(e=>{a({command:"sessions",json:e.json===!0,configPath:d(f(e.config,"--config"))})}),t.command("maintenance <action>","Inspect or cancel scheduled maintenance").option("--json","Print machine-readable JSON").option("--config <path>","Config file path",{default:u}).action((e,i)=>{if(e!=="status"&&e!=="cancel")throw new r(`Unknown maintenance action: ${e}`);a({command:"maintenance",action:e,json:i.json===!0,configPath:d(f(i.config,"--config"))})});for(const e of["status","start"])t.command(e,`${S(e)} the local ${p} service`).action(()=>{a({command:e})});t.command("restart [agent]","Restart the local service or a specific agent").option("--delay <seconds>","Schedule the restart after a delay").option("--allow-waiting","Allow restart while sessions wait for user input").action((e,i)=>{if(e!==void 0&&e!=="codex"&&e!=="claude")throw new r(`Unknown agent for restart: ${e}`);const s=h(i.delay),l=i.allowWaiting===!0;w(l,s),a({command:"restart",agent:e,delaySeconds:s,allowWaiting:l})});for(const e of["stop","uninstall"])t.command(e,`${S(e)} the local ${p} service`).option("--delay <seconds>",`Schedule the ${e} after a delay`).option("--allow-waiting",`Allow ${e} while sessions wait for user input`).action(i=>{const s=h(i.delay),l=i.allowWaiting===!0;w(l,s),a({command:e,delaySeconds:s,allowWaiting:l})});return n.includeInternalCommands&&(t.command("run-service","Run the managed service supervisor").option("--config <path>","Config file path").action(e=>{const i=c(e.config);a({command:"run-service",configPath:i?d(i):void 0})}),t.command("self-check","Validate the bundled runtime").action(()=>{a({command:"self-check"})}),t.command("complete-migration","Complete a prepared service migration").option("--handoff <path>","Migration handoff state").action(e=>{a({command:"complete-migration",handoffPath:d(f(e.handoff,"--handoff"))})})),t}function f(n,t){const o=c(n);if(!o)throw new r(`Missing value for ${t}`);return o}function c(n){return typeof n=="string"?n:void 0}function k(n){const t=c(n);if(t!==void 0)try{return C(t)}catch{throw new r(`Unknown release channel for --channel: ${t} (expected latest, beta, or alpha)`)}}function h(n){if(n===void 0)return;const t=typeof n=="number"?String(n):f(n,"--delay");if(!/^\d+$/.test(t))throw new r("--delay must be an integer between 1 and 86400 seconds");const o=Number.parseInt(t,10);if(o<1||o>86400)throw new r("--delay must be an integer between 1 and 86400 seconds");return o}function w(n,t){if(n&&t===void 0)throw new r("--allow-waiting requires --delay")}function A(n,t){if(n.startsWith("Unknown option `")){const o=y(t);if(o)return`Unknown option for ${t[0]}: ${o}`}if(n.startsWith("option `")){const o=/^option `([^` ]+)/.exec(n);if(o)return`Missing value for ${o[1]}`}if(n.startsWith("Unused args:")){const o=t.slice(1).find(a=>!a.startsWith("-"));if(o)return`Unknown option for ${t[0]}: ${o}`}return n}function y(n){const t=n[0];if(!t||!v(t))return n.find(a=>a.startsWith("-"));const o=R(t);for(const a of n.slice(1)){if(!a.startsWith("-"))continue;const e=a.includes("=")?a.slice(0,a.indexOf("=")):a;if(!o.has(e))return e}}function I(n){const t=n[0];if(!t||!v(t))return;const o=y(n);if(o)throw new r(`Unknown option for ${t}: ${o}`)}function R(n){switch(n){case"install":return new Set(["--tunnel-id","--token","--workspace","--server","--server-url","--config","--data-dir","--workspace-id","--workspace-name","--channel"]);case"upgrade":return new Set(["--version","--channel","--config","--delay","--allow-waiting"]);case"sessions":case"maintenance":return new Set(["--json","--config"]);case"restart":case"stop":case"uninstall":return new Set(["--delay","--allow-waiting"]);case"run-service":return new Set(["--config"]);case"complete-migration":return new Set(["--handoff"]);default:return new Set}}function v(n){return b.includes(n)}function x(n){const t=console.info,o=[];console.info=(...a)=>{o.push(a.map(String).join(" "))};try{n()}finally{console.info=t}return o.join(`
5
+ `)}function S(n){return`${n.charAt(0).toUpperCase()}${n.slice(1)}`}export{r as UsageError,L as parseCliArgs,P as usage};
@@ -1,29 +1,31 @@
1
1
  ---
2
- name: manage-coding-agent-client
3
- description: 管理本机 coding-agent client,包括查询版本和会话状态、升级、启动、停止、重启 Codex/Claude、查看日志和卸载。用户要求检查、升级、重启、停止、排查或卸载 coding-agent client 时使用。
2
+ name: manage-messenger-agent
3
+ description: 管理本机 Messenger Agent,包括查询版本和会话状态、升级、启动、停止、重启 Codex/Claude、查看日志和卸载。用户要求检查、升级、重启、停止、排查或卸载 Messenger Agent 时使用。
4
4
  ---
5
5
 
6
- # 管理 Coding Agent Client
6
+ # 管理 Messenger Agent
7
7
 
8
8
  始终使用安装后生成的稳定命令:
9
9
 
10
10
  ```bash
11
- ~/.coding-agent/bin/coding-agent
11
+ ~/.messenger-agent/bin/messenger-agent
12
12
  ```
13
13
 
14
+ 若该路径尚不存在但 `~/.coding-agent/bin/messenger-agent` 存在,说明客户端正处于兼容桥接阶段;本次使用旧目录中的同名命令。即时执行一次 `upgrade` 或重新执行 `install` 后,目录和服务标识会完成迁移。
15
+
14
16
  ## 检查状态
15
17
 
16
18
  查询版本和服务状态:
17
19
 
18
20
  ```bash
19
- ~/.coding-agent/bin/coding-agent --version
20
- ~/.coding-agent/bin/coding-agent status
21
+ ~/.messenger-agent/bin/messenger-agent --version
22
+ ~/.messenger-agent/bin/messenger-agent status
21
23
  ```
22
24
 
23
25
  执行任何会中断 Agent 的操作前,先查询会话:
24
26
 
25
27
  ```bash
26
- ~/.coding-agent/bin/coding-agent sessions --json
28
+ ~/.messenger-agent/bin/messenger-agent sessions --json
27
29
  ```
28
30
 
29
31
  按以下规则处理结果:
@@ -40,13 +42,13 @@ description: 管理本机 coding-agent client,包括查询版本和会话状
40
42
  默认延后 10 秒:
41
43
 
42
44
  ```bash
43
- ~/.coding-agent/bin/coding-agent upgrade --delay 10
44
- ~/.coding-agent/bin/coding-agent upgrade --version <version> --delay 10
45
- ~/.coding-agent/bin/coding-agent restart --delay 10
46
- ~/.coding-agent/bin/coding-agent restart codex --delay 10
47
- ~/.coding-agent/bin/coding-agent restart claude --delay 10
48
- ~/.coding-agent/bin/coding-agent stop --delay 10
49
- ~/.coding-agent/bin/coding-agent uninstall --delay 10
45
+ ~/.messenger-agent/bin/messenger-agent upgrade --delay 10
46
+ ~/.messenger-agent/bin/messenger-agent upgrade --version <version> --delay 10
47
+ ~/.messenger-agent/bin/messenger-agent restart --delay 10
48
+ ~/.messenger-agent/bin/messenger-agent restart codex --delay 10
49
+ ~/.messenger-agent/bin/messenger-agent restart claude --delay 10
50
+ ~/.messenger-agent/bin/messenger-agent stop --delay 10
51
+ ~/.messenger-agent/bin/messenger-agent uninstall --delay 10
50
52
  ```
51
53
 
52
54
  存在 waiting 会话且用户已经确认时,追加:
@@ -66,8 +68,8 @@ description: 管理本机 coding-agent client,包括查询版本和会话状
66
68
  查询或取消任务:
67
69
 
68
70
  ```bash
69
- ~/.coding-agent/bin/coding-agent maintenance status
70
- ~/.coding-agent/bin/coding-agent maintenance cancel
71
+ ~/.messenger-agent/bin/messenger-agent maintenance status
72
+ ~/.messenger-agent/bin/messenger-agent maintenance cancel
71
73
  ```
72
74
 
73
75
  同一时间只能有一个延后任务。新会话中可使用 `maintenance status`、`--version` 和 `status` 验证升级结果。
@@ -84,9 +86,9 @@ description: 管理本机 coding-agent client,包括查询版本和会话状
84
86
  client 的自动升级默认跟踪稳定版本(`latest` 通道)。用户要求使用预发布版本或回到稳定版时,切换发布通道:
85
87
 
86
88
  ```bash
87
- ~/.coding-agent/bin/coding-agent upgrade --channel beta --delay 10
88
- ~/.coding-agent/bin/coding-agent upgrade --channel alpha --delay 10
89
- ~/.coding-agent/bin/coding-agent upgrade --channel latest --delay 10
89
+ ~/.messenger-agent/bin/messenger-agent upgrade --channel beta --delay 10
90
+ ~/.messenger-agent/bin/messenger-agent upgrade --channel alpha --delay 10
91
+ ~/.messenger-agent/bin/messenger-agent upgrade --channel latest --delay 10
90
92
  ```
91
93
 
92
94
  切换通道会更新自动升级跟踪的 npm dist-tag,并把 runtime 升级到该通道的最新版本。之后自动升级会持续跟踪同一通道,直到用户再次切换。
@@ -101,14 +103,14 @@ client 的自动升级默认跟踪稳定版本(`latest` 通道)。用户要
101
103
  Linux:
102
104
 
103
105
  ```bash
104
- journalctl --user -u coding-agent-client.service -n 100 --no-pager
106
+ journalctl --user -u messenger-agent.service -n 100 --no-pager
105
107
  ```
106
108
 
107
109
  macOS:
108
110
 
109
111
  ```bash
110
- tail -n 100 ~/.coding-agent/data/logs/client-service.out.log
111
- tail -n 100 ~/.coding-agent/data/logs/client-service.err.log
112
+ tail -n 100 ~/.messenger-agent/data/logs/client-service.out.log
113
+ tail -n 100 ~/.messenger-agent/data/logs/client-service.err.log
112
114
  ```
113
115
 
114
116
  提醒用户在分享日志前检查并隐藏 token、服务地址和敏感路径。
package/dist/control.js CHANGED
@@ -1,2 +1,2 @@
1
- import{chmod as d,mkdir as f,rm as c}from"node:fs/promises";import{createConnection as g,createServer as m}from"node:net";import{dirname as w,join as y}from"node:path";import{readConfigYaml as p}from"./config-file.js";import{defaultDataDir as v}from"./paths.js";async function s(e){const n=await p(e),t=typeof n.data_dir=="string"&&n.data_dir.trim()?n.data_dir:v;return y(t,"runtime","client.sock")}async function I(e,n){await f(w(e),{recursive:!0,mode:448}),await c(e,{force:!0});const t=m({allowHalfOpen:!0},r=>{let a="";r.setEncoding("utf8"),r.on("error",()=>{}),r.on("data",o=>{a+=o}),r.on("end",()=>{S(a,n).then(o=>r.end(`${JSON.stringify(o)}
2
- `))})});return await q(t,e),await d(e,384),{async close(){await k(t),await c(e,{force:!0})}}}async function N(e,n){const t=await s(e),r=await u(t,{action:"restart",agent:n}).catch(a=>{const o=a instanceof Error?a.message:String(a);throw new Error(`Unable to contact the coding-agent client service: ${o}`)});if(!r.ok)throw new Error(r.error??`Failed to restart ${n} agent`)}async function A(e){return i(e,{action:"activity"})}async function D(e,n){return i(e,{action:"schedule-maintenance",...n})}async function J(e){return i(e,{action:"maintenance-status"})}async function M(e){return i(e,{action:"maintenance-cancel"})}async function S(e,n){try{const t=JSON.parse(e);switch(t.action){case"restart":return t.agent!=="codex"&&t.agent!=="claude"?{ok:!1,error:"Invalid control request"}:(await n.restartAgent(t.agent),{ok:!0});case"activity":return{ok:!0,data:await n.getActivity()};case"schedule-maintenance":return!h(t.operation)||!Number.isInteger(t.delaySeconds)||typeof t.allowWaiting!="boolean"?{ok:!1,error:"Invalid maintenance request"}:{ok:!0,data:await n.maintenance.schedule({operation:t.operation,delaySeconds:t.delaySeconds,allowWaiting:t.allowWaiting})};case"maintenance-status":return{ok:!0,data:n.maintenance.getTask()};case"maintenance-cancel":return{ok:!0,data:await n.maintenance.cancel()};default:return{ok:!1,error:"Invalid control request"}}}catch(t){return{ok:!1,error:t instanceof Error?t.message:String(t)}}}function h(e){return!e||typeof e!="object"||!("type"in e)?!1:e.type==="upgrade"?"version"in e&&typeof e.version=="string"&&e.version.length>0:e.type==="restart"?!("agent"in e&&e.agent!==void 0&&e.agent!=="codex"&&e.agent!=="claude"):e.type==="stop"||e.type==="uninstall"}async function i(e,n){const t=await s(e),r=await u(t,n).catch(a=>{const o=a instanceof Error?a.message:String(a);throw new Error(`Unable to contact the coding-agent client service: ${o}`)});if(!r.ok)throw new Error(r.error??"Client control request failed");return r.data}function q(e,n){return new Promise((t,r)=>{e.once("error",r),e.listen(n,()=>{e.off("error",r),t()})})}function k(e){return new Promise((n,t)=>{e.close(r=>r?t(r):n())})}function u(e,n){return new Promise((t,r)=>{const a=g(e);let o="";a.setEncoding("utf8"),a.once("error",r),a.on("data",l=>{o+=l}),a.once("connect",()=>a.end(JSON.stringify(n))),a.once("end",()=>{try{t(JSON.parse(o))}catch{r(new Error("Invalid response from the coding-agent client service"))}})})}export{s as readControlSocketPath,N as requestAgentRestart,A as requestClientActivity,M as requestMaintenanceCancel,J as requestMaintenanceStatus,D as requestScheduleMaintenance,I as startControlServer};
1
+ import{chmod as d,mkdir as m,rm as s}from"node:fs/promises";import{createConnection as w,createServer as g}from"node:net";import{dirname as y,join as p}from"node:path";import{readConfigYaml as v}from"./config-file.js";import{productName as c}from"./identity.js";import{defaultDataDir as S}from"./paths.js";async function u(e){const n=await v(e),t=typeof n.data_dir=="string"&&n.data_dir.trim()?n.data_dir:S;return p(t,"runtime","client.sock")}async function I(e,n){await m(y(e),{recursive:!0,mode:448}),await s(e,{force:!0});const t=g({allowHalfOpen:!0},r=>{let a="";r.setEncoding("utf8"),r.on("error",()=>{}),r.on("data",o=>{a+=o}),r.on("end",()=>{h(a,n).then(o=>r.end(`${JSON.stringify(o)}
2
+ `))})});return await k(t,e),await d(e,384),{async close(){await E(t),await s(e,{force:!0})}}}async function A(e,n){const t=await u(e),r=await l(t,{action:"restart",agent:n}).catch(a=>{const o=a instanceof Error?a.message:String(a);throw new Error(`Unable to contact the ${c} service: ${o}`)});if(!r.ok)throw new Error(r.error??`Failed to restart ${n} agent`)}async function D(e){return i(e,{action:"activity"})}async function J(e,n){return i(e,{action:"schedule-maintenance",...n})}async function M(e){return i(e,{action:"maintenance-status"})}async function R(e){return i(e,{action:"maintenance-cancel"})}async function h(e,n){try{const t=JSON.parse(e);switch(t.action){case"restart":return t.agent!=="codex"&&t.agent!=="claude"?{ok:!1,error:"Invalid control request"}:(await n.restartAgent(t.agent),{ok:!0});case"activity":return{ok:!0,data:await n.getActivity()};case"schedule-maintenance":return!q(t.operation)||!Number.isInteger(t.delaySeconds)||typeof t.allowWaiting!="boolean"?{ok:!1,error:"Invalid maintenance request"}:{ok:!0,data:await n.maintenance.schedule({operation:t.operation,delaySeconds:t.delaySeconds,allowWaiting:t.allowWaiting})};case"maintenance-status":return{ok:!0,data:n.maintenance.getTask()};case"maintenance-cancel":return{ok:!0,data:await n.maintenance.cancel()};default:return{ok:!1,error:"Invalid control request"}}}catch(t){return{ok:!1,error:t instanceof Error?t.message:String(t)}}}function q(e){return!e||typeof e!="object"||!("type"in e)?!1:e.type==="upgrade"?"version"in e&&typeof e.version=="string"&&e.version.length>0:e.type==="restart"?!("agent"in e&&e.agent!==void 0&&e.agent!=="codex"&&e.agent!=="claude"):e.type==="stop"||e.type==="uninstall"}async function i(e,n){const t=await u(e),r=await l(t,n).catch(a=>{const o=a instanceof Error?a.message:String(a);throw new Error(`Unable to contact the ${c} service: ${o}`)});if(!r.ok)throw new Error(r.error??"Client control request failed");return r.data}function k(e,n){return new Promise((t,r)=>{e.once("error",r),e.listen(n,()=>{e.off("error",r),t()})})}function E(e){return new Promise((n,t)=>{e.close(r=>r?t(r):n())})}function l(e,n){return new Promise((t,r)=>{const a=w(e);let o="";a.setEncoding("utf8"),a.once("error",r),a.on("data",f=>{o+=f}),a.once("connect",()=>a.end(JSON.stringify(n))),a.once("end",()=>{try{t(JSON.parse(o))}catch{r(new Error(`Invalid response from the ${c} service`))}})})}export{u as readControlSocketPath,A as requestAgentRestart,D as requestClientActivity,R as requestMaintenanceCancel,M as requestMaintenanceStatus,J as requestScheduleMaintenance,I as startControlServer};
package/dist/exec.d.ts CHANGED
@@ -7,4 +7,5 @@ export declare function runCommand(command: string, args: string[], options?: {
7
7
  allowFailure?: boolean;
8
8
  interactive?: boolean;
9
9
  env?: NodeJS.ProcessEnv;
10
+ cwd?: string;
10
11
  }): Promise<CommandResult>;
package/dist/exec.js CHANGED
@@ -1,2 +1,2 @@
1
- import{spawn as f}from"node:child_process";function p(n,o,e={}){return new Promise((c,s)=>{console.log(`$ ${n} ${o.join(" ")}`);const r=f(n,o,{stdio:e.interactive?"inherit":["ignore","pipe","pipe"],env:{...process.env,...e.env}}),u=[],d=[];r.stdout?.on("data",t=>{u.push(t)}),r.stderr?.on("data",t=>{d.push(t)}),r.on("error",s),r.on("close",t=>{const i={status:t,stdout:Buffer.concat(u).toString("utf8"),stderr:Buffer.concat(d).toString("utf8")};if(!e.allowFailure&&t!==0){s(new Error(`${n} ${o.join(" ")} failed with status ${t}
2
- ${i.stderr||i.stdout}`));return}c(i)})})}export{p as runCommand};
1
+ import{spawn as f}from"node:child_process";function a(e,o,r={}){return new Promise((c,s)=>{console.log(`$ ${e} ${o.join(" ")}`);const n=f(e,o,{stdio:r.interactive?"inherit":["ignore","pipe","pipe"],env:{...process.env,...r.env},cwd:r.cwd}),u=[],d=[];n.stdout?.on("data",t=>{u.push(t)}),n.stderr?.on("data",t=>{d.push(t)}),n.on("error",s),n.on("close",t=>{const i={status:t,stdout:Buffer.concat(u).toString("utf8"),stderr:Buffer.concat(d).toString("utf8")};if(!r.allowFailure&&t!==0){s(new Error(`${e} ${o.join(" ")} failed with status ${t}
2
+ ${i.stderr||i.stdout}`));return}c(i)})})}export{a as runCommand};
@@ -0,0 +1,10 @@
1
+ export declare const productName = "Messenger Agent";
2
+ export declare const cliName = "messenger-agent";
3
+ export declare const legacyCliName = "coding-agent";
4
+ export declare const serviceName = "messenger-agent";
5
+ export declare const legacyServiceName = "coding-agent-client";
6
+ export declare const launchdLabel = "vip.elevo.messenger.agent";
7
+ export declare const legacyLaunchdLabel = "vip.elevo.coding-agent.client";
8
+ export declare const appBundleIdentifier = "vip.elevo.messenger.agent";
9
+ export declare const helperBundleIdentifier = "vip.elevo.messenger.agent.helper";
10
+ export declare const runtimePackageScope = "@messenger-agent";
@@ -0,0 +1 @@
1
+ const e="Messenger Agent",n="messenger-agent",t="coding-agent",o="messenger-agent",c="coding-agent-client",r="vip.elevo.messenger.agent",s="vip.elevo.coding-agent.client",a="vip.elevo.messenger.agent",g="vip.elevo.messenger.agent.helper",p="@messenger-agent";export{a as appBundleIdentifier,n as cliName,g as helperBundleIdentifier,r as launchdLabel,t as legacyCliName,s as legacyLaunchdLabel,c as legacyServiceName,e as productName,p as runtimePackageScope,o as serviceName};
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import{parseCliArgs as p,UsageError as $,usage as i}from"./args.js";import{installClient as w}from"./install.js";import{currentPackageVersion as h,installRuntime as v,normalizeUpgradeVersion as P}from"./runtime.js";import{darwinCommands as c,linuxCommands as l,serviceName as m,uninstallService as y}from"./service.js";import{runSupervisor as C}from"./supervisor.js";import{runCommand as S}from"./exec.js";import{defaultConfigPath as s}from"./paths.js";import{readClientPath as A,writeClientPath as N,writeUpgradeChannel as T}from"./config-file.js";import{requestAgentRestart as W,requestClientActivity as G,requestMaintenanceCancel as O,requestMaintenanceStatus as _,requestScheduleMaintenance as b}from"./control.js";async function E(){const e=p(process.argv.slice(2));if(e.command==="version"){console.log(`coding-agent ${await h()}`);return}if(e.command==="run-service"){await C(e.configPath??process.env.AGENT_CONFIG_PATH??s);return}if(e.command==="install"){const n=await w(e);console.log(`Installed ${m}`),console.log(`Config: ${n.configPath}`),console.log(`Data: ${n.dataDir}`),console.log(`Runtime: ${n.runtime.currentLink}`),console.log(`Command: ${n.runtime.cliWrapperPath}`),console.log(`Service file: ${n.service.servicePath}`),console.log(`Upgrade: ${n.runtime.cliWrapperPath} upgrade`),console.log(`Restart: ${n.runtime.cliWrapperPath} restart`),console.log(`Status: ${n.runtime.cliWrapperPath} status`),console.log(`Logs: ${n.service.commands.logs}`);for(const t of n.service.warnings)console.warn(`Warning: ${t}`);return}if(e.command==="upgrade"){const{version:n,channel:t}=P(e.version,e.channel),a=process.env.PATH;if(a&&await N(e.configPath,a),t&&(await T(e.configPath,t),console.log(`Auto-upgrade channel: ${t}`)),e.delaySeconds!==void 0){await r(e.configPath,{type:"upgrade",version:n,path:a},e.delaySeconds,e.allowWaiting);return}const o=await v({version:n,configPath:e.configPath,path:a??await A(e.configPath)}),f=process.platform==="darwin"?c():l();await g(f.restart),console.log(`Upgraded runtime to ${o.version}`),console.log(`Runtime: ${o.currentLink}`),console.log(`Command: ${o.cliWrapperPath}`),console.log(`Status: ${o.cliWrapperPath} status`);return}if(e.command==="sessions"){const n=await G(e.configPath);console.log(e.json?JSON.stringify(n):U(n));return}if(e.command==="maintenance"){const n=e.action==="cancel"?await O(e.configPath):await _(e.configPath);console.log(e.json?JSON.stringify(n??null):F(n));return}if(e.command==="restart"&&e.agent){if(e.delaySeconds!==void 0){await r(process.env.AGENT_CONFIG_PATH??s,{type:"restart",agent:e.agent},e.delaySeconds,e.allowWaiting);return}await W(process.env.AGENT_CONFIG_PATH??s,e.agent),console.log(`Restarted ${e.agent} agent`);return}if(e.command==="status"||e.command==="start"||e.command==="restart"||e.command==="stop"){if((e.command==="restart"||e.command==="stop")&&e.delaySeconds!==void 0){await r(process.env.AGENT_CONFIG_PATH??s,{type:e.command},e.delaySeconds,e.allowWaiting);return}const n=process.platform==="darwin"?c():l(),t=e.command==="status"?n.status:e.command==="start"?n.start:e.command==="restart"?n.restart:n.stop;await g(t);return}if(e.command==="uninstall"){if(e.delaySeconds!==void 0){await r(process.env.AGENT_CONFIG_PATH??s,{type:"uninstall"},e.delaySeconds,e.allowWaiting);return}await y(),console.log(`Uninstalled ${m}`);return}}async function r(e,n,t,a){const o=await b(e,{operation:n,delaySeconds:t,allowWaiting:a});console.log(`Scheduled maintenance task ${o.id}`),console.log(`Operation: ${u(o.operation)}`),console.log(`Not before: ${o.notBefore}`)}function U(e){const n=[`Active sessions: ${e.active}`,`Waiting sessions: ${e.waiting}`];for(const t of["codex","claude"]){const a=e.agents[t];n.push(a.available?`${d(t)}: active=${a.active}, waiting=${a.waiting}`:`${d(t)}: unavailable (${a.error})`)}return n.join(`
3
- `)}function F(e){return e?[`Task: ${e.id}`,`Operation: ${u(e.operation)}`,`Status: ${e.status}`,`Not before: ${e.notBefore}`,...e.message?[`Message: ${e.message}`]:[]].join(`
4
- `):"No maintenance task has been scheduled"}function u(e){return e.type==="upgrade"?`upgrade to ${e.version}`:e.type==="restart"?e.agent?`restart ${e.agent}`:"restart client":e.type}function d(e){return`${e.charAt(0).toUpperCase()}${e.slice(1)}`}async function g(e){const n=await S("/bin/sh",["-lc",e],{allowFailure:!0});n.stdout&&process.stdout.write(n.stdout),n.stderr&&process.stderr.write(n.stderr),n.status!==0&&process.exit(n.status??1)}E().catch(e=>{if(e instanceof $){const t=e.message===i()?e.message:`${e.message}
2
+ import{parseCliArgs as S,UsageError as N,usage as d}from"./args.js";import{activateMigratedService as A,completeMigrationHandoff as T,installClient as W}from"./install.js";import{currentPackageVersion as f,installRuntime as b,normalizeUpgradeVersion as G,rollbackRuntime as O}from"./runtime.js";import{serviceCommandsForConfig as p,serviceName as h,uninstallLegacyService as _,uninstallService as E,verifyServiceHealth as H}from"./service.js";import{runSupervisor as M}from"./supervisor.js";import{runCommand as D}from"./exec.js";import{defaultConfigPath as r,preferredClientPaths as l}from"./paths.js";import{readClientPath as F,writeClientPath as w,writeUpgradeChannel as $}from"./config-file.js";import{requestAgentRestart as I,requestClientActivity as R,requestMaintenanceCancel as U,requestMaintenanceStatus as k,requestScheduleMaintenance as q}from"./control.js";import{cliName as L,productName as j}from"./identity.js";import{migrateLegacyClient as x}from"./migration.js";import{assertPlatformNativeDependencies as z}from"./native-dependencies.js";async function B(){const e=S(process.argv.slice(2));if(e.command==="version"){console.log(`${L} ${await f()}`);return}if(e.command==="self-check"){await f(),z(),console.log(`${j} runtime is healthy`);return}if(e.command==="complete-migration"){await T(e.handoffPath);return}if(e.command==="run-service"){await M(e.configPath??process.env.AGENT_CONFIG_PATH??r);return}if(e.command==="install"){const t=await W(e);console.log(`Installed ${h}`),console.log(`Config: ${t.configPath}`),console.log(`Data: ${t.dataDir}`),console.log(`Runtime: ${t.runtime.currentLink}`),console.log(`Command: ${t.runtime.cliWrapperPath}`),console.log(`Service file: ${t.service.servicePath}`),console.log(`Upgrade: ${t.runtime.cliWrapperPath} upgrade`),console.log(`Restart: ${t.runtime.cliWrapperPath} restart`),console.log(`Status: ${t.runtime.cliWrapperPath} status`),console.log(`Logs: ${t.service.commands.logs}`);for(const n of t.service.warnings)console.warn(`Warning: ${n}`);return}if(e.command==="upgrade"){const{version:t,channel:n}=G(e.version,e.channel),a=process.env.PATH,o=e.configPath===r?l().configPath:e.configPath;if(a&&await w(o,a),n&&(await $(o,n),console.log(`Auto-upgrade channel: ${n}`)),e.delaySeconds!==void 0){await m(o,{type:"upgrade",version:t,path:a},e.delaySeconds,e.allowWaiting);return}const c=await x(),i=o===c.legacyPaths.configPath?c.paths.configPath:o;a&&i!==o&&await w(i,a),n&&i!==o&&await $(i,n);const s=await b({version:t,configPath:i,runtimeDir:c.paths.runtimeDir,binDir:c.paths.binDir,path:a??await F(i)});if(!await A(c,s,i,a)){const u=p(i);try{await g(u.restart),await H()}catch(y){throw await O(s),await g(u.restart).catch(()=>{}),y}}console.log(`Upgraded runtime to ${s.version}`),console.log(`Runtime: ${s.currentLink}`),console.log(`Command: ${s.cliWrapperPath}`),console.log(`Status: ${s.cliWrapperPath} status`);return}if(e.command==="sessions"){const t=await R(P(e.configPath));console.log(e.json?JSON.stringify(t):J(t));return}if(e.command==="maintenance"){const t=P(e.configPath),n=e.action==="cancel"?await U(t):await k(t);console.log(e.json?JSON.stringify(n??null):V(n));return}if(e.command==="restart"&&e.agent){if(e.delaySeconds!==void 0){await m(process.env.AGENT_CONFIG_PATH??r,{type:"restart",agent:e.agent},e.delaySeconds,e.allowWaiting);return}await I(process.env.AGENT_CONFIG_PATH??r,e.agent),console.log(`Restarted ${e.agent} agent`);return}if(e.command==="status"||e.command==="start"||e.command==="restart"||e.command==="stop"){if((e.command==="restart"||e.command==="stop")&&e.delaySeconds!==void 0){await m(process.env.AGENT_CONFIG_PATH??r,{type:e.command},e.delaySeconds,e.allowWaiting);return}const t=l().configPath,n=p(t),a=e.command==="status"?n.status:e.command==="start"?n.start:e.command==="restart"?n.restart:n.stop;await g(a);return}if(e.command==="uninstall"){if(e.delaySeconds!==void 0){await m(process.env.AGENT_CONFIG_PATH??r,{type:"uninstall"},e.delaySeconds,e.allowWaiting);return}l().configPath===r?await E():await _(),console.log(`Uninstalled ${h}`);return}}function P(e){return e===r?l().configPath:e}async function m(e,t,n,a){const o=await q(e,{operation:t,delaySeconds:n,allowWaiting:a});console.log(`Scheduled maintenance task ${o.id}`),console.log(`Operation: ${v(o.operation)}`),console.log(`Not before: ${o.notBefore}`)}function J(e){const t=[`Active sessions: ${e.active}`,`Waiting sessions: ${e.waiting}`];for(const n of["codex","claude"]){const a=e.agents[n];t.push(a.available?`${C(n)}: active=${a.active}, waiting=${a.waiting}`:`${C(n)}: unavailable (${a.error})`)}return t.join(`
3
+ `)}function V(e){return e?[`Task: ${e.id}`,`Operation: ${v(e.operation)}`,`Status: ${e.status}`,`Not before: ${e.notBefore}`,...e.message?[`Message: ${e.message}`]:[]].join(`
4
+ `):"No maintenance task has been scheduled"}function v(e){return e.type==="upgrade"?`upgrade to ${e.version}`:e.type==="restart"?e.agent?`restart ${e.agent}`:"restart client":e.type}function C(e){return`${e.charAt(0).toUpperCase()}${e.slice(1)}`}async function g(e){const t=await D("/bin/sh",["-lc",e],{allowFailure:!0});if(t.stdout&&process.stdout.write(t.stdout),t.stderr&&process.stderr.write(t.stderr),t.status!==0)throw new Error(t.stderr||t.stdout||`Command failed: ${e}`)}B().catch(e=>{if(e instanceof N){const n=e.message===d()?e.message:`${e.message}
5
5
 
6
- ${i()}`;console.error(t),process.exit(2)}const n=e instanceof Error?e.message:String(e);console.error(n),process.exit(1)});
6
+ ${d()}`;console.error(n),process.exit(2)}const t=e instanceof Error?e.message:String(e);console.error(t),process.exit(1)});
package/dist/install.d.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  import { type RuntimeInstallResult } from "./runtime.js";
2
2
  import { type ServiceInstallResult } from "./service.js";
3
3
  import { type InstallArgs } from "./args.js";
4
+ import { runCommand } from "./exec.js";
5
+ import { type ClientMigrationResult } from "./migration.js";
4
6
  export type InstallResult = {
5
7
  configPath: string;
6
8
  dataDir: string;
@@ -8,6 +10,13 @@ export type InstallResult = {
8
10
  runtime: RuntimeInstallResult;
9
11
  service: ServiceInstallResult;
10
12
  };
13
+ export declare function activateMigratedService(migration: ClientMigrationResult, runtime: RuntimeInstallResult, configPath: string, path?: string): Promise<boolean>;
14
+ export declare function scheduleDarwinMigrationHandoff(migration: ClientMigrationResult, runtime: RuntimeInstallResult, configPath: string, path?: string, dependencies?: {
15
+ run?: typeof runCommand;
16
+ wait?: (milliseconds: number) => Promise<unknown>;
17
+ attempts?: number;
18
+ }): Promise<boolean>;
19
+ export declare function completeMigrationHandoff(handoffPath: string): Promise<void>;
11
20
  type ResolvedInstallArgs = Omit<InstallArgs, "tunnelId" | "token" | "workspace" | "workspaceId" | "workspaceName"> & {
12
21
  tunnelId: string;
13
22
  token: string;
package/dist/install.js CHANGED
@@ -1 +1,2 @@
1
- import{randomUUID as w}from"node:crypto";import{mkdir as m,realpath as l,stat as h}from"node:fs/promises";import{normalizeAgentServer as k}from"@messenger-agent/shared/agent-config";import{readConfigYaml as v,updateClientConfig as y,writeUpgradeChannel as P}from"./config-file.js";import{installRuntime as g}from"./runtime.js";import{installService as A}from"./service.js";import{UsageError as p}from"./args.js";import{defaultWorkspacePath as d}from"./paths.js";function x(e=process.platform){if(e!=="linux"&&e!=="darwin")throw new Error("Only Linux and macOS are supported")}function D(e=process.versions.node){const r=/^(\d+)\.(\d+)\.(\d+)(?:[.-]|$)/.exec(e),t=Number(r?.[1]),o=Number(r?.[2]);if(!(t>22||t===22&&o>=19))throw new Error("Node.js 22.19.0 or newer is required")}function N(e){if(e.server)try{k(e.server)}catch{throw new Error("--server must be a valid HTTP or HTTPS server address")}if(e.tunnelId&&!/^[A-Za-z0-9._:-]+$/.test(e.tunnelId))throw new Error("--tunnel-id may only contain letters, numbers, '.', '_', ':', and '-'");if(e.workspaceId&&!/^[A-Za-z0-9._:-]+$/.test(e.workspaceId))throw new Error("--workspace-id may only contain letters, numbers, '.', '_', ':', and '-'");if(e.workspaceName&&(e.workspaceName.trim().length===0||e.workspaceName.trim().length>128))throw new Error("--workspace-name must be between 1 and 128 characters")}async function b(e,r=!1){r&&await m(e,{recursive:!0});const t=await l(e);if(!(await h(t)).isDirectory())throw new Error("--workspace must point to an existing directory");return t}async function M(e){x(),D();const r=await I(e);N(r);const t=await b(r.workspace,r.createWorkspace);await y({configPath:r.configPath,dataDir:r.dataDir,workspacePath:t,workspaceId:r.workspaceId,workspaceName:r.workspaceName,tunnelId:r.tunnelId,token:r.token,server:r.server,serverUrl:r.serverUrl,preserveWorkspaces:r.preserveWorkspaces,path:process.env.PATH}),e.channel&&await P(r.configPath,e.channel);const o=await g({version:"current",configPath:r.configPath,path:process.env.PATH}),i=await A({configPath:r.configPath,dataDir:r.dataDir,workspacePath:t,serviceCommandPath:o.wrapperPath,path:process.env.PATH});return{configPath:r.configPath,dataDir:r.dataDir,workspacePath:t,runtime:o,service:i}}async function I(e){const r=await v(e.configPath),t=T(r,"tunnel"),o=e.tunnelId??f(t,"tunnel_id"),i=e.token??f(t,"token"),s=E(r,e.workspaceId,o),a=e.workspace??s?.path??d,n=e.workspaceId??s?.id??w(),c=e.workspaceName??s?.name??(o?`${o} Default`:void 0),u=a===d;if(!o)throw new p("Missing required option: --tunnel-id");if(!i)throw new p("Missing required option: --token");if(!n)throw new p("Missing required option: --workspace-id");if(!c)throw new p("Missing required option: --workspace-name");return{...e,tunnelId:o,token:i,workspace:a,workspaceId:n,workspaceName:c,preserveWorkspaces:!e.workspace&&!!s,createWorkspace:u}}function E(e,r,t){const o=e.workspaces;if(!Array.isArray(o))return;const i=o.filter(n=>!!n&&typeof n=="object"&&!Array.isArray(n)&&typeof n.path=="string");if(r){const n=i.find(c=>c.id===r);return!n||typeof n.path!="string"?void 0:{id:r,name:typeof n.name=="string"&&n.name?n.name:`${t??r} Default`,path:n.path}}const s=r??(t?`${t}--default`:void 0),a=(s?i.find(n=>n.id===s):void 0)??i.find(n=>n.id==="default")??i[0];if(!(!a||typeof a.path!="string"))return{id:typeof a.id=="string"&&a.id?a.id:s??"default",name:typeof a.name=="string"&&a.name?a.name:`${t??s??"default"} Default`,path:a.path}}function T(e,r){const t=e[r];if(!(!t||typeof t!="object"||Array.isArray(t)))return t}function f(e,r){const t=e?.[r];return typeof t=="string"&&t?t:void 0}export{D as assertNodeVersion,x as assertSupportedPlatform,M as installClient,I as resolveInstallArgs,b as resolveWorkspacePath,N as validateInstallArgs};
1
+ import{randomUUID as I}from"node:crypto";import{mkdir as M,readFile as N,realpath as T,rename as $,stat as x,writeFile as O}from"node:fs/promises";import{join as _}from"node:path";import{setTimeout as C}from"node:timers/promises";import{normalizeAgentServer as L}from"@messenger-agent/shared/agent-config";import{readConfigYaml as h,updateClientConfig as H,writeUpgradeChannel as j}from"./config-file.js";import{installRuntime as R,rollbackRuntime as l}from"./runtime.js";import{installService as g,startLegacyService as y,stopLegacyService as v,uninstallLegacyService as k,uninstallService as P}from"./service.js";import{UsageError as f}from"./args.js";import{defaultWorkspacePath as A,legacyCodingAgentPaths as W,messengerAgentPaths as U}from"./paths.js";import{runCommand as G}from"./exec.js";import{completeLegacyMigration as S,migrateLegacyClient as z,rollbackLegacyMigration as F}from"./migration.js";const V="vip.elevo.messenger.agent.migration";async function ut(t,e,r,o){if(!t.migrated)return!1;if(process.platform==="darwin")return q(t,e,r,o);try{return await D(t,e,r,o),!0}catch(n){throw await l(e).catch(()=>{}),await b(t),n}}async function D(t,e,r,o){const n=await h(r),i=u(n,"data_dir")??t.paths.dataDir,s=Array.isArray(n.workspaces)?n.workspaces.find(c=>!!c&&typeof c=="object"&&!Array.isArray(c)&&typeof c.path=="string"):void 0,a=u(s,"path");if(!a)throw new Error("Migrated config does not contain a workspace path");await v(),await g({configPath:r,dataDir:i,workspacePath:a,serviceCommandPath:e.wrapperPath,path:o}),await k(),await S(t)}async function q(t,e,r,o,n={}){const i=n.run??G,s=n.wait??C,a=n.attempts??1200,c=_(t.paths.rootDir,"migration-handoff.json"),d=`${V}.${process.pid}`;await p(c,{schemaVersion:1,status:"scheduled",helperLabel:d,migration:t,runtime:e,configPath:r,...o?{path:o}:{},updatedAt:new Date().toISOString()}),await i("launchctl",["submit","-l",d,"--",e.cliWrapperPath,"complete-migration","--handoff",c]);for(let m=0;m<a;m+=1){const w=await E(c);if(w.status==="completed")return await i("launchctl",["remove",d],{allowFailure:!0}),!0;if(w.status==="failed")throw new Error(w.message??"macOS service migration failed");await s(100)}throw new Error(`Timed out waiting for macOS service migration; inspect ${c}`)}async function wt(t){const e=await E(t);await p(t,{...e,status:"running",updatedAt:new Date().toISOString()});try{await D(e.migration,e.runtime,e.configPath,e.path),await p(t,{...e,status:"completed",updatedAt:new Date().toISOString()})}catch(r){const o=[];await l(e.runtime).catch(i=>{o.push(`runtime rollback failed: ${String(i)}`)}),await P().catch(i=>{o.push(`new service cleanup failed: ${String(i)}`)}),await y().catch(i=>{o.push(`legacy service restart failed: ${String(i)}`)});const n=[r instanceof Error?r.message:String(r),...o].join("; ");throw await p(t,{...e,status:"failed",message:n,updatedAt:new Date().toISOString()}),r}}async function E(t){const e=JSON.parse(await N(t,"utf8"));if(e.schemaVersion!==1||!e.migration||!e.runtime||typeof e.configPath!="string"||typeof e.helperLabel!="string"||e.status!=="scheduled"&&e.status!=="running"&&e.status!=="completed"&&e.status!=="failed")throw new Error(`Invalid Messenger Agent migration handoff: ${t}`);return e}async function p(t,e){const r=`${t}.${process.pid}.tmp`;await O(r,`${JSON.stringify(e,null,2)}
2
+ `,{mode:384}),await $(r,t)}function B(t=process.platform){if(t!=="linux"&&t!=="darwin")throw new Error("Only Linux and macOS are supported")}function J(t=process.versions.node){const e=/^(\d+)\.(\d+)\.(\d+)(?:[.-]|$)/.exec(t),r=Number(e?.[1]),o=Number(e?.[2]);if(!(r>22||r===22&&o>=19))throw new Error("Node.js 22.19.0 or newer is required")}function Z(t){if(t.server)try{L(t.server)}catch{throw new Error("--server must be a valid HTTP or HTTPS server address")}if(t.tunnelId&&!/^[A-Za-z0-9._:-]+$/.test(t.tunnelId))throw new Error("--tunnel-id may only contain letters, numbers, '.', '_', ':', and '-'");if(t.workspaceId&&!/^[A-Za-z0-9._:-]+$/.test(t.workspaceId))throw new Error("--workspace-id may only contain letters, numbers, '.', '_', ':', and '-'");if(t.workspaceName&&(t.workspaceName.trim().length===0||t.workspaceName.trim().length>128))throw new Error("--workspace-name must be between 1 and 128 characters")}async function Y(t,e=!1){e&&await M(t,{recursive:!0});const r=await T(t);if(!(await x(r)).isDirectory())throw new Error("--workspace must point to an existing directory");return r}async function lt(t){B(),J();const e=U(),r=W(),n=t.configPath===e.configPath||t.configPath===r.configPath?await z():void 0;let i;try{const s=n?{...t,configPath:t.configPath===n.legacyPaths.configPath?n.paths.configPath:t.configPath,dataDir:t.dataDir===n.legacyPaths.dataDir?n.paths.dataDir:t.dataDir}:t,a=await K(s);Z(a);const c=await Y(a.workspace,a.createWorkspace);await H({configPath:a.configPath,dataDir:a.dataDir,workspacePath:c,workspaceId:a.workspaceId,workspaceName:a.workspaceName,tunnelId:a.tunnelId,token:a.token,server:a.server,serverUrl:a.serverUrl,preserveWorkspaces:a.preserveWorkspaces,path:process.env.PATH}),t.channel&&await j(a.configPath,t.channel),i=await R({version:"current",configPath:a.configPath,runtimeDir:n?.paths.runtimeDir,binDir:n?.paths.binDir,path:process.env.PATH,hostArtifactDirectory:process.env.MESSENGER_AGENT_BOOTSTRAP_HOST_DIR,clientDirectory:process.env.MESSENGER_AGENT_BOOTSTRAP_CLIENT_DIR}),n?.migrated&&await v();const d=await g({configPath:a.configPath,dataDir:a.dataDir,workspacePath:c,serviceCommandPath:i.wrapperPath,path:process.env.PATH});return n?.migrated&&(await k(),await S(n)),{configPath:a.configPath,dataDir:a.dataDir,workspacePath:c,runtime:i,service:d}}catch(s){throw i&&await l(i).catch(()=>{}),n?.migrated&&await b(n),s}}async function b(t){await P().catch(()=>{}),await F(t),await y().catch(()=>{})}async function K(t){const e=await h(t.configPath),r=X(e,"tunnel"),o=t.tunnelId??u(r,"tunnel_id"),n=t.token??u(r,"token"),i=Q(e,t.workspaceId,o),s=t.workspace??i?.path??A,a=t.workspaceId??i?.id??I(),c=t.workspaceName??i?.name??(o?`${o} Default`:void 0),d=s===A;if(!o)throw new f("Missing required option: --tunnel-id");if(!n)throw new f("Missing required option: --token");if(!a)throw new f("Missing required option: --workspace-id");if(!c)throw new f("Missing required option: --workspace-name");return{...t,tunnelId:o,token:n,workspace:s,workspaceId:a,workspaceName:c,preserveWorkspaces:!t.workspace&&!!i,createWorkspace:d}}function Q(t,e,r){const o=t.workspaces;if(!Array.isArray(o))return;const n=o.filter(a=>!!a&&typeof a=="object"&&!Array.isArray(a)&&typeof a.path=="string");if(e){const a=n.find(c=>c.id===e);return!a||typeof a.path!="string"?void 0:{id:e,name:typeof a.name=="string"&&a.name?a.name:`${r??e} Default`,path:a.path}}const i=e??(r?`${r}--default`:void 0),s=(i?n.find(a=>a.id===i):void 0)??n.find(a=>a.id==="default")??n[0];if(!(!s||typeof s.path!="string"))return{id:typeof s.id=="string"&&s.id?s.id:i??"default",name:typeof s.name=="string"&&s.name?s.name:`${r??i??"default"} Default`,path:s.path}}function X(t,e){const r=t[e];if(!(!r||typeof r!="object"||Array.isArray(r)))return r}function u(t,e){const r=t?.[e];return typeof r=="string"&&r?r:void 0}export{ut as activateMigratedService,J as assertNodeVersion,B as assertSupportedPlatform,wt as completeMigrationHandoff,lt as installClient,K as resolveInstallArgs,Y as resolveWorkspacePath,q as scheduleDarwinMigrationHandoff,Z as validateInstallArgs};
@@ -0,0 +1,10 @@
1
+ import { type ClientPaths } from "./paths.js";
2
+ export type ClientMigrationResult = {
3
+ paths: ClientPaths;
4
+ migrated: boolean;
5
+ legacyPaths: ClientPaths;
6
+ statePath?: string;
7
+ };
8
+ export declare function migrateLegacyClient(home?: string): Promise<ClientMigrationResult>;
9
+ export declare function completeLegacyMigration(result: ClientMigrationResult): Promise<void>;
10
+ export declare function rollbackLegacyMigration(result: ClientMigrationResult): Promise<void>;
@@ -0,0 +1,2 @@
1
+ import{constants as O}from"node:fs";import{access as $,chmod as u,lstat as m,mkdir as M,readFile as y,readlink as D,rename as s,symlink as N,unlink as _,writeFile as l}from"node:fs/promises";import{dirname as f,isAbsolute as P,join as w,relative as k,resolve as e}from"node:path";import{parse as x,stringify as C}from"yaml";import{legacyCodingAgentPaths as v,messengerAgentPaths as j}from"./paths.js";async function K(t){const r=j(t),a=v(t);if(await d(r.configPath)){const i=w(r.rootDir,"migration.json"),n=await F(i);return n?.status==="prepared"&&e(n.sourceRoot)===e(a.rootDir)&&e(n.targetRoot)===e(r.rootDir)?(await A(a.rootDir,r.rootDir),{paths:r,migrated:!0,legacyPaths:a,statePath:i}):{paths:r,migrated:!1,legacyPaths:a}}if(!await d(a.configPath))return{paths:r,migrated:!1,legacyPaths:a};if(await d(r.rootDir))throw new Error(`Messenger Agent migration target already exists without a config file: ${r.rootDir}`);await s(a.rootDir,r.rootDir);const o=w(r.rootDir,"migration.json");try{return await A(a.rootDir,r.rootDir),await E(r.configPath,a,r),await h(o,{schemaVersion:1,status:"prepared",sourceRoot:a.rootDir,targetRoot:r.rootDir,updatedAt:new Date().toISOString()}),{paths:r,migrated:!0,legacyPaths:a,statePath:o}}catch(i){throw await b(a.rootDir,r.rootDir).catch(()=>{}),await s(r.rootDir,a.rootDir).catch(()=>{}),i}}async function Y(t){!t.migrated||!t.statePath||await h(t.statePath,{schemaVersion:1,status:"completed",sourceRoot:t.legacyPaths.rootDir,targetRoot:t.paths.rootDir,updatedAt:new Date().toISOString()})}async function q(t){if(t.migrated){if(await b(t.legacyPaths.rootDir,t.paths.rootDir),await d(t.legacyPaths.rootDir))throw new Error(`Cannot roll back Messenger Agent migration because the legacy directory exists: ${t.legacyPaths.rootDir}`);t.statePath&&await h(t.statePath,{schemaVersion:1,status:"rolled-back",sourceRoot:t.legacyPaths.rootDir,targetRoot:t.paths.rootDir,updatedAt:new Date().toISOString()}),await E(t.paths.configPath,t.paths,t.legacyPaths),await s(t.paths.rootDir,t.legacyPaths.rootDir)}}async function F(t){try{const r=JSON.parse(await y(t,"utf8"));if(r.schemaVersion!==1||r.status!=="prepared"&&r.status!=="completed"&&r.status!=="rolled-back"||typeof r.sourceRoot!="string"||typeof r.targetRoot!="string"||typeof r.updatedAt!="string")throw new Error(`Invalid Messenger Agent migration state: ${t}`);return r}catch(r){if(r.code==="ENOENT")return;throw r}}async function A(t,r){try{if((await m(t)).isSymbolicLink()&&e(f(t),await D(t))===e(r))return;throw new Error(`Legacy migration path is occupied: ${t}`)}catch(a){if(a.code!=="ENOENT")throw a}await N(r,t,"dir")}async function b(t,r){try{if(!(await m(t)).isSymbolicLink()||e(f(t),await D(t))!==e(r))return;await _(t)}catch(a){if(a.code!=="ENOENT")throw a}}async function E(t,r,a){const o=x(await y(t,"utf8"));if(!S(o))throw new Error(`Config file must contain a YAML object: ${t}`);c(o,["data_dir"],r.rootDir,a.rootDir),c(o,["file_uploads","temp_dir"],r.rootDir,a.rootDir);for(const n of["codex","claude","workspace"])c(o,[n,"data_dir"],r.rootDir,a.rootDir),c(o,[n,"log_file"],r.rootDir,a.rootDir),c(o,[n,"file_uploads","temp_dir"],r.rootDir,a.rootDir);const i=`${t}.${process.pid}.tmp`;await l(i,C(o),{mode:384}),await u(i,384),await s(i,t),await u(t,384)}function c(t,r,a,o){let i=t;for(const L of r.slice(0,-1)){const p=i[L];if(!S(p))return;i=p}const n=r.at(-1);if(!n)return;const g=i[n];typeof g!="string"||!I(g,a)||(i[n]=w(o,k(e(a),e(g))))}function I(t,r){if(!P(t))return!1;const a=k(e(r),e(t));return a===""||!a.startsWith("..")&&!P(a)}async function h(t,r){await M(f(t),{recursive:!0,mode:448});const a=`${t}.${process.pid}.tmp`;await l(a,`${JSON.stringify(r,null,2)}
2
+ `,{mode:384}),await s(a,t)}async function d(t){try{return await $(t,O.F_OK),!0}catch{return!1}}function S(t){return typeof t=="object"&&t!==null&&!Array.isArray(t)}export{Y as completeLegacyMigration,K as migrateLegacyClient,q as rollbackLegacyMigration};
@@ -0,0 +1 @@
1
+ export declare function assertPlatformNativeDependencies(platform?: NodeJS.Platform, arch?: string, resolvePackage?: (request: string) => string, isFile?: (path: string) => boolean): void;
@@ -0,0 +1 @@
1
+ import{statSync as l}from"node:fs";import{createRequire as p}from"node:module";import{dirname as s,join as x}from"node:path";const m=p(import.meta.url),g={"linux-x64":[{packageName:"@openai/codex-linux-x64",executable:"vendor/x86_64-unknown-linux-musl/bin/codex"},{packageName:"@anthropic-ai/claude-agent-sdk-linux-x64",executable:"claude"}],"linux-arm64":[{packageName:"@openai/codex-linux-arm64",executable:"vendor/aarch64-unknown-linux-musl/bin/codex"},{packageName:"@anthropic-ai/claude-agent-sdk-linux-arm64",executable:"claude"}],"darwin-x64":[{packageName:"@openai/codex-darwin-x64",executable:"vendor/x86_64-apple-darwin/bin/codex"},{packageName:"@anthropic-ai/claude-agent-sdk-darwin-x64",executable:"claude"}],"darwin-arm64":[{packageName:"@openai/codex-darwin-arm64",executable:"vendor/aarch64-apple-darwin/bin/codex"},{packageName:"@anthropic-ai/claude-agent-sdk-darwin-arm64",executable:"claude"}]};function h(e=process.platform,t=process.arch,i=m.resolve,o=k){const a=`${e}-${t}`,r=g[a];if(!r)throw new Error(`Unsupported native dependency target: ${a}`);const c=r.filter(({packageName:n,executable:d})=>{try{const u=i(`${n}/package.json`);return!o(x(s(u),d))}catch{return!0}});if(c.length>0)throw new Error(`Missing native Agent dependencies for ${a}: ${c.map(({packageName:n})=>n).join(", ")}`)}function k(e){try{return l(e).isFile()}catch{return!1}}export{h as assertPlatformNativeDependencies};
package/dist/paths.d.ts CHANGED
@@ -1,3 +1,14 @@
1
+ export type ClientPaths = {
2
+ rootDir: string;
3
+ configPath: string;
4
+ dataDir: string;
5
+ runtimeDir: string;
6
+ binDir: string;
7
+ };
8
+ export declare function messengerAgentPaths(home?: string): ClientPaths;
9
+ export declare function legacyCodingAgentPaths(home?: string): ClientPaths;
10
+ export declare function preferredClientPaths(home?: string): ClientPaths;
11
+ export declare const defaultRootDir: string;
1
12
  export declare const defaultConfigPath: string;
2
13
  export declare const defaultDataDir: string;
3
14
  export declare const defaultRuntimeDir: string;
package/dist/paths.js CHANGED
@@ -1 +1 @@
1
- import{homedir as t}from"node:os";import{dirname as r,join as n,resolve as o}from"node:path";const f=n(t(),".coding-agent","config.yaml"),s=n(t(),".coding-agent","data"),u=n(t(),".coding-agent","runtime"),d=n(t(),".coding-agent","bin"),g=n(t(),"messenger-workspace");function i(e){return e==="~"?t():e.startsWith("~/")?n(t(),e.slice(2)):e}function m(e){return o(i(e))}function p(e){return r(e)}export{p as configBaseDir,d as defaultBinDir,f as defaultConfigPath,s as defaultDataDir,u as defaultRuntimeDir,g as defaultWorkspacePath,i as expandHome,m as resolvePath};
1
+ import{existsSync as a}from"node:fs";import{homedir as n}from"node:os";import{dirname as u,join as e,resolve as f}from"node:path";function c(t=n()){return s(e(t,".messenger-agent"))}function g(t=n()){return s(e(t,".coding-agent"))}function x(t=n()){const i=c(t),o=g(t);return a(i.configPath)||!a(o.configPath)?i:o}const r=c(),P=r.rootDir,h=r.configPath,D=r.dataDir,y=r.runtimeDir,b=r.binDir,C=e(n(),"messenger-workspace");function s(t){return{rootDir:t,configPath:e(t,"config.yaml"),dataDir:e(t,"data"),runtimeDir:e(t,"runtime"),binDir:e(t,"bin")}}function d(t){return t==="~"?n():t.startsWith("~/")?e(n(),t.slice(2)):t}function k(t){return f(d(t))}function v(t){return u(t)}export{v as configBaseDir,b as defaultBinDir,h as defaultConfigPath,D as defaultDataDir,P as defaultRootDir,y as defaultRuntimeDir,C as defaultWorkspacePath,d as expandHome,g as legacyCodingAgentPaths,c as messengerAgentPaths,x as preferredClientPaths,k as resolvePath};
package/dist/runtime.d.ts CHANGED
@@ -7,13 +7,39 @@ export type RuntimeInstallOptions = {
7
7
  codexHome?: string;
8
8
  claudeHome?: string;
9
9
  path?: string;
10
+ platform?: NodeJS.Platform;
11
+ arch?: NodeJS.Architecture;
12
+ registryUrl?: string;
13
+ hostArtifactDirectory?: string;
14
+ clientDirectory?: string;
10
15
  };
11
16
  export type RuntimeInstallResult = {
12
17
  releaseDir: string;
13
18
  currentLink: string;
14
19
  wrapperPath: string;
20
+ legacyServiceWrapperPath: string;
15
21
  cliWrapperPath: string;
22
+ legacyCliWrapperPath: string;
16
23
  version: string;
24
+ previousReleaseDir?: string;
25
+ hostCurrentLink: string;
26
+ previousHostReleaseDir?: string;
27
+ previousWrappers: PreviousWrapper[];
28
+ };
29
+ type PreviousWrapper = {
30
+ path: string;
31
+ content?: string;
32
+ };
33
+ export type NodeHostManifest = {
34
+ schemaVersion: 1;
35
+ product: "messenger-agent-node-host";
36
+ version: string;
37
+ nodeVersion: string;
38
+ platform: "linux" | "darwin";
39
+ arch: "x64" | "arm64";
40
+ node: string;
41
+ npm: string;
42
+ payload?: string;
17
43
  };
18
44
  export type ReleaseChannel = "latest" | "beta" | "alpha";
19
45
  export declare const releaseChannels: ReleaseChannel[];
@@ -26,10 +52,11 @@ export declare function normalizeUpgradeVersion(version: string, explicitChannel
26
52
  export declare function isPrereleaseVersion(version: string): boolean;
27
53
  export declare function compareSemverVersions(left: string, right: string): number;
28
54
  export declare function currentPackageVersion(): Promise<string>;
29
- export declare function npmExecutablePath(nodeExecutable?: string): string;
30
- export declare function npmProcessPath(nodeExecutable?: string, currentPath?: string | undefined): string;
31
55
  export declare function currentBundledSkillsDir(moduleUrl?: string): string;
32
56
  export declare function defaultAgentHomes(): [string, string];
33
57
  export declare function installRuntime(options: RuntimeInstallOptions): Promise<RuntimeInstallResult>;
34
- export declare function resolveChannelPackageVersion(channel: ReleaseChannel): Promise<string>;
58
+ export declare function rollbackRuntime(result: RuntimeInstallResult): Promise<void>;
59
+ export declare function nodeHostPackageName(platform: "linux" | "darwin", arch: "x64" | "arm64"): string;
60
+ export declare function resolveChannelPackageVersion(channel: ReleaseChannel, registryUrl?: string): Promise<string>;
35
61
  export declare function syncBundledSkills(skillsSourceDir: string, agentHomes: string[]): Promise<void>;
62
+ export {};
package/dist/runtime.js CHANGED
@@ -1,3 +1,3 @@
1
- import{access as O,cp as R,lstat as P,mkdir as g,readFile as j,rename as p,rm as h,symlink as V,writeFile as k}from"node:fs/promises";import{homedir as v}from"node:os";import{basename as F,delimiter as M,dirname as x,join as n}from"node:path";import{fileURLToPath as b}from"node:url";import{runCommand as D}from"./exec.js";import{defaultBinDir as G,defaultRuntimeDir as I}from"./paths.js";const N="@messenger-agent/client",ne=["latest","beta","alpha"];function ie(e){if(!(e==null||e==="")){if(e==="stable")return"latest";if(e==="latest"||e==="beta"||e==="alpha")return e;throw new Error(`Unknown release channel: ${String(e)} (expected latest, beta, or alpha)`)}}function E(e){return e==="stable"?"latest":e==="latest"||e==="beta"||e==="alpha"?e:void 0}function ae(e,t){const r=E(e),o=t??r;return{version:r?o??r:e,channel:o}}function se(e){return/^\d+\.\d+\.\d+-/.test(e)}function oe(e,t){const[r]=e.split("+",1),[o]=t.split("+",1),s=/^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?$/.exec(r??""),c=/^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?$/.exec(o??"");if(!s||!c)throw new Error(`Automatic upgrades require semantic versions, received: ${e} and ${t}`);for(let i=1;i<=3;i+=1){const a=Number(s[i])-Number(c[i]);if(a!==0)return Math.sign(a)}return L(s[4],c[4])}function L(e,t){if(!e&&!t)return 0;if(!e)return 1;if(!t)return-1;const r=e.split("."),o=t.split(".");for(let s=0;s<Math.max(r.length,o.length);s+=1){const c=r[s],i=o[s];if(c===void 0)return-1;if(i===void 0)return 1;const a=/^\d+$/.test(c),u=/^\d+$/.test(i);if(a&&u){const l=Number(c)-Number(i);if(l!==0)return Math.sign(l);continue}if(a)return-1;if(u)return 1;if(c!==i)return c<i?-1:1}return 0}async function U(){const e=n(x(b(import.meta.url)),"..","package.json");return JSON.parse(await j(e,"utf8")).version??"latest"}function A(e=process.execPath){return n(x(e),"npm")}function C(e=process.execPath,t=process.env.PATH){const r=x(e);return t?`${r}${M}${t}`:r}function ce(e=import.meta.url){const t=x(b(e));return F(t)==="src"?n(t,"..","assets","skills"):n(t,"assets","skills")}function z(){return[process.env.CODEX_HOME??n(v(),".codex"),process.env.CLAUDE_CONFIG_DIR??n(v(),".claude")]}async function w(e){await h(e,{recursive:!0,force:!0})}async function J(e,t){const r=`${t}.tmp-${process.pid}`;await w(r),await V(e,r,"dir"),await p(r,t)}async function B(e){try{return(await P(e)).isSymbolicLink()}catch{return!1}}async function ue(e){const t=e.runtimeDir??I,r=e.binDir??G,o=E(e.version),s=e.version==="current"?await U():o?await Z(o):e.version,c=s.replaceAll("/","_").replaceAll(":","_"),i=n(t,"releases"),a=n(i,c),u=n(t,"current"),l=n(a,"node_modules","@messenger-agent","client","dist","index.js"),f=n(u,"node_modules","@messenger-agent","client","dist","index.js"),S=e.skillsSourceDir??n(a,"node_modules","@messenger-agent","client","dist","assets","skills"),$=n(r,"coding-agent-client-service"),y=n(r,"coding-agent");if(await g(i,{recursive:!0,mode:448}),await g(r,{recursive:!0,mode:448}),!await q(l)){const m=`${a}.tmp-${process.pid}`;await w(m),await g(m,{recursive:!0,mode:448});try{await D(A(),["install","--prefix",m,"--omit=dev","--verbose",`${N}@${s}`],{env:{PATH:C()}}),await w(a),await p(m,a)}catch(T){throw await w(m),T}}const[H,_]=z();return await Q(S,[e.codexHome??H,e.claudeHome??_]),await B(u)||await w(u),await J(a,u),await k($,["#!/bin/sh","set -eu",...e.path?[`export PATH=${d(e.path)}`]:[],`export AGENT_CONFIG_PATH=${d(e.configPath)}`,`exec ${d(process.execPath)} ${d(l)} run-service --config ${d(e.configPath)}`,""].join(`
2
- `),{mode:448}),await k(y,["#!/bin/sh","set -eu",`export AGENT_CONFIG_PATH=${d(e.configPath)}`,`exec ${d(process.execPath)} ${d(f)} "$@"`,""].join(`
3
- `),{mode:448}),{releaseDir:a,currentLink:u,wrapperPath:$,cliWrapperPath:y,version:s}}async function Z(e){const t=await D(A(),["view",`${N}@${e}`,"version","--json"],{allowFailure:!0,env:{PATH:C()}});if(t.status!==0)throw new Error(`Unable to resolve the ${e} coding-agent version: ${t.stderr||t.stdout}`);const r=JSON.parse(t.stdout);if(typeof r!="string"||!r)throw new Error("Registry returned an invalid coding-agent version");return r}async function q(e){try{return await O(e),!0}catch{return!1}}async function Q(e,t){const r="manage-coding-agent-client",o=n(e,r),s=await W(o);for(const c of new Set(t)){const i=n(c,"skills"),a=n(i,r),u=n(i,`.${r}.tmp-${process.pid}`),l=n(i,`.${r}.old-${process.pid}`);if(await h(u,{recursive:!0,force:!0}),await h(l,{recursive:!0,force:!0}),!s){await h(a,{recursive:!0,force:!0});continue}await g(i,{recursive:!0,mode:448}),await R(o,u,{recursive:!0}),await p(a,l).catch(f=>{if(f.code!=="ENOENT")throw f});try{await p(u,a),await h(l,{recursive:!0,force:!0})}catch(f){throw await p(l,a).catch(()=>{}),f}}}async function W(e){try{return(await P(e)).isDirectory()}catch(t){if(t.code==="ENOENT")return!1;throw t}}function d(e){return`'${e.replaceAll("'","'\\''")}'`}export{oe as compareSemverVersions,ce as currentBundledSkillsDir,U as currentPackageVersion,z as defaultAgentHomes,ue as installRuntime,se as isPrereleaseVersion,ie as normalizeReleaseChannel,ae as normalizeUpgradeVersion,A as npmExecutablePath,C as npmProcessPath,E as parseReleaseChannel,ne as releaseChannels,Z as resolveChannelPackageVersion,Q as syncBundledSkills};
1
+ import{createHash as oe}from"node:crypto";import{createReadStream as se,createWriteStream as ce}from"node:fs";import{access as le,cp as j,lstat as Z,mkdir as k,readFile as S,readlink as ue,rename as E,rm as $,symlink as fe,writeFile as N}from"node:fs/promises";import{homedir as q}from"node:os";import{basename as de,delimiter as me,dirname as C,isAbsolute as G,join as a,relative as we,resolve as v}from"node:path";import{Readable as pe}from"node:stream";import{pipeline as he}from"node:stream/promises";import{fileURLToPath as Q}from"node:url";import{runCommand as P}from"./exec.js";import{cliName as ge,legacyCliName as ye,runtimePackageScope as $e}from"./identity.js";import{defaultBinDir as ve,defaultRuntimeDir as ke,legacyCodingAgentPaths as Ee,messengerAgentPaths as Ne}from"./paths.js";const O="https://registry.npmjs.org",I="@messenger-agent/client",Ke=["latest","beta","alpha"];function et(e){if(!(e==null||e==="")){if(e==="stable")return"latest";if(e==="latest"||e==="beta"||e==="alpha")return e;throw new Error(`Unknown release channel: ${String(e)} (expected latest, beta, or alpha)`)}}function W(e){return e==="stable"?"latest":e==="latest"||e==="beta"||e==="alpha"?e:void 0}function tt(e,t){const r=W(e),n=t??r;return{version:r?n??r:e,channel:n}}function rt(e){return/^\d+\.\d+\.\d+-/.test(e)}function at(e,t){const[r]=e.split("+",1),[n]=t.split("+",1),o=/^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?$/.exec(r??""),s=/^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?$/.exec(n??"");if(!o||!s)throw new Error(`Automatic upgrades require semantic versions, received: ${e} and ${t}`);for(let c=1;c<=3;c+=1){const l=Number(o[c])-Number(s[c]);if(l!==0)return Math.sign(l)}return Pe(o[4],s[4])}function Pe(e,t){if(!e&&!t)return 0;if(!e)return 1;if(!t)return-1;const r=e.split("."),n=t.split(".");for(let o=0;o<Math.max(r.length,n.length);o+=1){const s=r[o],c=n[o];if(s===void 0)return-1;if(c===void 0)return 1;const l=/^\d+$/.test(s),i=/^\d+$/.test(c);if(l&&i){const w=Number(s)-Number(c);if(w!==0)return Math.sign(w);continue}if(l)return-1;if(i)return 1;if(s!==c)return s<c?-1:1}return 0}async function xe(){const e=a(C(Q(import.meta.url)),"..","package.json");return JSON.parse(await S(e,"utf8")).version??"latest"}function nt(e=import.meta.url){const t=C(Q(e));return de(t)==="src"?a(t,"..","assets","skills"):a(t,"assets","skills")}function be(){return[process.env.CODEX_HOME??a(q(),".codex"),process.env.CLAUDE_CONFIG_DIR??a(q(),".claude")]}async function m(e){await $(e,{recursive:!0,force:!0})}async function V(e,t){const r=`${t}.tmp-${process.pid}`;await m(r),await fe(e,r,"dir"),await E(r,t)}async function F(e){try{return(await Z(e)).isSymbolicLink()}catch{return!1}}async function it(e){const t=De(e.configPath),r=e.runtimeDir??t?.runtimeDir??ke,n=e.binDir??t?.binDir??ve,o=Ue(e.platform??process.platform),s=je(e.arch??process.arch),c=W(e.version),l=e.version==="current"?await xe():c?await Ce(c,e.registryUrl):e.version,i=await He({runtimeDir:r,platform:o,arch:s,registryUrl:e.registryUrl,artifactDirectory:e.hostArtifactDirectory}),w=l.replaceAll("/","_").replaceAll(":","_"),p=a(r,"releases"),u=a(p,w),h=a(r,"current"),D=a(u,"node_modules","@messenger-agent","client","dist","index.js"),d=a(u,"node_modules","@messenger-agent","client","package.json"),_=e.skillsSourceDir??a(u,"node_modules","@messenger-agent","client","dist","assets","skills"),M=a(n,"messenger-agent-service"),T=a(n,"coding-agent-client-service"),L=a(n,ge),U=a(n,ye),ae=[M,T,L,U];if(await k(p,{recursive:!0,mode:448}),await k(n,{recursive:!0,mode:448}),!(await z(d,l)&&(await X(i.nodeExecutable,D)).status===0)){const f=`${u}.tmp-${process.pid}`,A=a(f,"node_modules","@messenger-agent","client","dist","index.js");await m(f),await k(f,{recursive:!0,mode:448});try{if(e.clientDirectory&&await z(a(e.clientDirectory,"node_modules","@messenger-agent","client","package.json"),l))await j(e.clientDirectory,f,{recursive:!0});else{const x=a(f,".npmrc");await N(x,""),await P(i.nodeExecutable,[i.npmCli,"install","--prefix",f,"--omit=dev","--include=optional","--ignore-scripts","--no-audit","--no-fund","--registry",e.registryUrl??process.env.MESSENGER_AGENT_NPM_REGISTRY??O,"--userconfig",x,`${I}@${l}`],{env:{PATH:_e(i.nodeExecutable,e.path)},cwd:f})}if(!await z(a(f,"node_modules","@messenger-agent","client","package.json"),l))throw new Error(`Installed Messenger Agent Client does not match ${l}`);const g=await X(i.nodeExecutable,A);if(g.status!==0)throw new Error(`Messenger Agent Client self-check failed: ${g.stderr||g.stdout}`);await m(u),await E(f,u)}catch(g){throw await m(f),g}}const[ne,ie]=be();await Ge(_,[e.codexHome??ne,e.claudeHome??ie]);const J=await re(h),B=await re(i.currentLink),Y=await Promise.all(ae.map(async f=>({path:f,content:await Ae(f)})));await F(h)||await m(h),await F(i.currentLink)||await m(i.currentLink);try{await V(i.releaseDir,i.currentLink),await V(u,h);const f=b(i.currentLink,i.manifest.node),A=a(h,"node_modules","@messenger-agent","client","dist","index.js"),g=["#!/bin/sh","set -eu",...e.path?[`export PATH=${y(e.path)}`]:[],`export AGENT_CONFIG_PATH=${y(e.configPath)}`,`exec ${y(f)} ${y(A)} run-service --config ${y(e.configPath)}`,""].join(`
2
+ `),x=["#!/bin/sh","set -eu",`export AGENT_CONFIG_PATH=${y(e.configPath)}`,`exec ${y(f)} ${y(A)} "$@"`,""].join(`
3
+ `);await N(M,g,{mode:448}),await N(T,g,{mode:448}),await N(L,x,{mode:448}),await N(U,x,{mode:448})}catch(f){throw await H(J,h),await H(B,i.currentLink),await K(Y),f}return{releaseDir:u,currentLink:h,wrapperPath:M,legacyServiceWrapperPath:T,cliWrapperPath:L,legacyCliWrapperPath:U,version:l,previousReleaseDir:J,hostCurrentLink:i.currentLink,previousHostReleaseDir:B,previousWrappers:Y}}async function X(e,t){return P(e,[t,"self-check"],{allowFailure:!0})}function De(e){const t=Ne();if(v(e)===v(t.configPath))return t;const r=Ee();return v(e)===v(r.configPath)?r:void 0}async function ot(e){await H(e.previousReleaseDir,e.currentLink),await H(e.previousHostReleaseDir,e.hostCurrentLink),await K(e.previousWrappers)}async function H(e,t){e?await V(e,t):await m(t)}async function Ae(e){try{return await S(e,"utf8")}catch(t){if(t.code==="ENOENT")return;throw t}}async function K(e){await Promise.all(e.map(({path:t,content:r})=>r===void 0?m(t):N(t,r,{mode:448})))}function Se(e,t){return`${$e}/node-host-${e}-${t}`}async function Ce(e,t){return(await ee(I,e,t)).version}async function He(e){const t=Se(e.platform,e.arch),r=e.artifactDirectory?void 0:await Me(t,"latest",e.registryUrl),o=(e.artifactDirectory?await te(a(e.artifactDirectory,"manifest.json"),{platform:e.platform,arch:e.arch}):void 0)?.version??r?.version;if(!o)throw new Error(`Unable to resolve ${t}`);const s=a(e.runtimeDir,"host"),c=a(s,"releases"),l=`${o}-${e.platform}-${e.arch}`.replaceAll("/","_").replaceAll(":","_"),i=a(c,l),w=a(s,"current"),p=a(i,"manifest.json");if(await k(c,{recursive:!0,mode:448}),!await R(p)){const d=`${i}.tmp-${process.pid}`;await m(d),await k(d,{recursive:!0,mode:448});try{e.artifactDirectory?await j(e.artifactDirectory,d,{recursive:!0}):r&&await Re(r,d),await m(i),await E(d,i)}catch(_){throw await m(d),_}}const u=await te(p,{version:o,platform:e.platform,arch:e.arch});await Le(i,u);const h=b(i,u.node),D=b(i,u.npm);if(!await R(h)||!await R(D))throw new Error(`Node Host ${t}@${o} is incomplete`);if(u.platform==="darwin"){const d=a(i,"Messenger Agent.app");await P("codesign",["--verify","--deep","--strict",d]),await P("spctl",["--assess","--type","execute",d])}return{releaseDir:i,currentLink:w,nodeExecutable:h,npmCli:D,manifest:u}}async function Re(e,t){const r=`${t}.tgz`;try{const n=await fetch(e.tarball);if(!n.ok||!n.body)throw new Error(`Unable to download ${e.packageName}@${e.version}: HTTP ${n.status}`);await he(pe.fromWeb(n.body),ce(r,{mode:384})),await Te(r,e.integrity),await P("tar",["-xzf",r,"-C",t,"--strip-components=2","package/host"])}finally{await $(r,{force:!0})}}async function z(e,t){try{const r=JSON.parse(await S(e,"utf8"));return r.name===I&&r.version===t}catch{return!1}}function _e(e,t=process.env.PATH){const r=C(e);return t?`${r}${me}${t}`:r}async function Me(e,t,r=process.env.MESSENGER_AGENT_NPM_REGISTRY??O){const{metadata:n,version:o}=await ee(e,t,r),s=n.versions?.[o]?.dist;if(!s?.tarball||!s.integrity)throw new Error(`Registry returned incomplete artifact metadata for ${e}@${o}`);return{packageName:e,version:o,tarball:s.tarball,integrity:s.integrity}}async function ee(e,t,r=process.env.MESSENGER_AGENT_NPM_REGISTRY??O){const n=`${r.replace(/\/$/,"")}/${encodeURIComponent(e)}`,o=await fetch(n,{headers:{accept:"application/json"}});if(!o.ok)throw new Error(`Unable to resolve ${e}: HTTP ${o.status}`);const s=await o.json(),c=W(t)?s["dist-tags"]?.[t]:t;if(!c)throw new Error(`Registry has no ${t} release for ${e}`);return{metadata:s,version:c}}async function Te(e,t){const r=t.indexOf("-");if(r<=0)throw new Error("Node Host has an invalid integrity value");const n=t.slice(0,r),o=t.slice(r+1),s=oe(n);for await(const c of se(e))s.update(c);if(s.digest("base64")!==o)throw new Error("Node Host integrity verification failed")}async function te(e,t){const r=JSON.parse(await S(e,"utf8"));if(r.schemaVersion!==1||r.product!=="messenger-agent-node-host"||t.version!==void 0&&r.version!==t.version||typeof r.version!="string"||typeof r.nodeVersion!="string"||r.platform!==t.platform||r.arch!==t.arch||typeof r.node!="string"||typeof r.npm!="string"||r.payload!==void 0&&typeof r.payload!="string")throw new Error(`Node Host manifest does not match ${t.platform}-${t.arch}${t.version?`@${t.version}`:""}`);return r}async function Le(e,t){if(!t.payload)return;const r=b(e,t.node);if(await R(r))return;if(t.platform!=="darwin")throw new Error("Only macOS Node Hosts may contain a zip payload");const n=b(e,t.payload);await P("ditto",["-x","-k",n,e])}function b(e,t){if(!t||G(t))throw new Error(`Node Host contains an unsafe path: ${t}`);const r=v(e),n=v(e,t),o=we(r,n);if(o.startsWith("..")||G(o))throw new Error(`Node Host contains an unsafe path: ${t}`);return n}function Ue(e){if(e==="linux"||e==="darwin")return e;throw new Error(`No Messenger Agent runtime is available for platform: ${e}`)}function je(e){if(e==="x64"||e==="arm64")return e;throw new Error(`No Messenger Agent runtime is available for architecture: ${e}`)}async function re(e){if(!await F(e))return;const t=await ue(e);return G(t)?t:v(C(e),t)}async function R(e){try{return await le(e),!0}catch{return!1}}async function Ge(e,t){const r="manage-messenger-agent",n="manage-coding-agent-client",o=a(e,r),s=await Oe(o);for(const c of new Set(t)){const l=a(c,"skills"),i=a(l,r),w=a(l,`.${r}.tmp-${process.pid}`),p=a(l,`.${r}.old-${process.pid}`);if(await $(w,{recursive:!0,force:!0}),await $(p,{recursive:!0,force:!0}),await $(a(l,n),{recursive:!0,force:!0}),!s){await $(i,{recursive:!0,force:!0});continue}await k(l,{recursive:!0,mode:448}),await j(o,w,{recursive:!0}),await E(i,p).catch(u=>{if(u.code!=="ENOENT")throw u});try{await E(w,i),await $(p,{recursive:!0,force:!0})}catch(u){throw await E(p,i).catch(()=>{}),u}}}async function Oe(e){try{return(await Z(e)).isDirectory()}catch(t){if(t.code==="ENOENT")return!1;throw t}}function y(e){return`'${e.replaceAll("'","'\\''")}'`}export{at as compareSemverVersions,nt as currentBundledSkillsDir,xe as currentPackageVersion,be as defaultAgentHomes,it as installRuntime,rt as isPrereleaseVersion,Se as nodeHostPackageName,et as normalizeReleaseChannel,tt as normalizeUpgradeVersion,W as parseReleaseChannel,Ke as releaseChannels,Ce as resolveChannelPackageVersion,ot as rollbackRuntime,Ge as syncBundledSkills};
package/dist/service.d.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  import { type CommandResult } from "./exec.js";
2
- export declare const serviceName = "coding-agent-client";
3
- export declare const launchdLabel = "vip.elevo.coding-agent.client";
2
+ export { launchdLabel, serviceName } from "./identity.js";
4
3
  export type ServiceInstallOptions = {
5
4
  configPath: string;
6
5
  dataDir: string;
@@ -36,7 +35,13 @@ type LingerDependencies = {
36
35
  };
37
36
  export declare function ensureSystemdUserLinger(user: string, dependencies?: LingerDependencies): Promise<void>;
38
37
  export declare function installService(options: ServiceInstallOptions): Promise<ServiceInstallResult>;
38
+ export declare function verifyServiceHealth(platform?: NodeJS.Platform, uid?: number | undefined): Promise<void>;
39
39
  export declare function linuxCommands(): ServiceCommands;
40
- export declare function darwinCommands(): ServiceCommands;
40
+ export declare function legacyLinuxCommands(): ServiceCommands;
41
+ export declare function darwinCommands(dataDir?: string): ServiceCommands;
42
+ export declare function legacyDarwinCommands(): ServiceCommands;
43
+ export declare function serviceCommandsForConfig(configPath: string): ServiceCommands;
44
+ export declare function uninstallLegacyService(platform?: NodeJS.Platform): Promise<void>;
45
+ export declare function stopLegacyService(platform?: NodeJS.Platform): Promise<void>;
46
+ export declare function startLegacyService(platform?: NodeJS.Platform): Promise<void>;
41
47
  export declare function uninstallService(platform?: NodeJS.Platform): Promise<void>;
42
- export {};
package/dist/service.js CHANGED
@@ -1,3 +1,3 @@
1
- import{access as h,chmod as u,mkdir as d,unlink as m,writeFile as g}from"node:fs/promises";import{userInfo as $}from"node:os";import{dirname as w,join as i}from"node:path";import{runCommand as a}from"./exec.js";const n="coding-agent-client",s="vip.elevo.coding-agent.client";function D(e){return`'${e.replaceAll("'","'\\''")}'`}function v(e){return["[Unit]","Description=Coding Agent Client","After=network-online.target","","[Service]","Type=simple",`ExecStart=${p(e.serviceCommandPath)}`,"Restart=always","RestartSec=5",`WorkingDirectory=${p(e.workspacePath)}`,`Environment=${y("AGENT_CONFIG_PATH",e.configPath)}`,...e.path?[`Environment=${y("PATH",e.path)}`]:[],"","[Install]","WantedBy=default.target",""].join(`
2
- `)}function p(e){return e.replaceAll("\\","\\\\").replaceAll(" ","\\x20")}function y(e,t){return`${e}="${t.replaceAll("\\","\\\\").replaceAll('"','\\"')}"`}function l(e){return e.replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;").replaceAll("'","&apos;")}function E(e){const t=i(e.dataDir,"logs","client-service.out.log"),r=i(e.dataDir,"logs","client-service.err.log");return['<?xml version="1.0" encoding="UTF-8"?>','<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">','<plist version="1.0">',"<dict>"," <key>Label</key>",` <string>${l(s)}</string>`," <key>ProgramArguments</key>"," <array>",` <string>${l(e.serviceCommandPath)}</string>`," </array>"," <key>WorkingDirectory</key>",` <string>${l(e.workspacePath)}</string>`," <key>EnvironmentVariables</key>"," <dict>"," <key>AGENT_CONFIG_PATH</key>",` <string>${l(e.configPath)}</string>`,...e.path?[" <key>PATH</key>",` <string>${l(e.path)}</string>`]:[]," </dict>"," <key>RunAtLoad</key>"," <true/>"," <key>KeepAlive</key>"," <true/>"," <key>StandardOutPath</key>",` <string>${l(t)}</string>`," <key>StandardErrorPath</key>",` <string>${l(r)}</string>`,"</dict>","</plist>",""].join(`
3
- `)}async function c(e){const t=(process.env.PATH??"").split(":").filter(Boolean);for(const r of t)try{return await h(i(r,e)),!0}catch{}return!1}const k={commandExists:c,runCommand:a};async function b(e,t=k){if(!await t.commandExists("loginctl"))throw new Error(`loginctl is required to keep the user service running after logout. Enable linger for ${e} and retry installation.`);if(await f(e,t.runCommand))return;if(console.log(`Enabling login persistence for ${e}`),(await t.runCommand("loginctl",["enable-linger",e],{allowFailure:!0,interactive:!0})).status!==0)throw new Error(`Unable to enable login persistence for ${e}. Run 'sudo loginctl enable-linger ${e}' and retry installation.`);if(!await f(e,t.runCommand))throw new Error(`Login persistence is still disabled for ${e}. Run 'sudo loginctl enable-linger ${e}' and retry installation.`)}async function f(e,t){const r=await t("loginctl",["show-user",e,"-p","Linger"],{allowFailure:!0});return r.status===0&&r.stdout.trim()==="Linger=yes"}async function F(e){const t=e.platform??process.platform;if(t==="linux")return A(e);if(t==="darwin")return P(e);throw new Error("Only Linux and macOS are supported")}async function A(e){if(!await c("systemctl"))throw new Error("systemctl is required to install the user service");const t=e.user??process.env.USER??$().username;await b(t);const r=i(process.env.HOME??"",".config","systemd","user",`${n}.service`);return await d(w(r),{recursive:!0,mode:448}),await g(r,v(e),{mode:384}),await u(r,384),await a("systemctl",["--user","daemon-reload"]),await a("systemctl",["--user","enable","--now",`${n}.service`]),await a("systemctl",["--user","restart",`${n}.service`]),{servicePath:r,commands:L(),warnings:[]}}async function P(e){if(!await c("launchctl"))throw new Error("launchctl is required to install the LaunchAgent");const t=i(process.env.HOME??"","Library","LaunchAgents",`${s}.plist`);await d(w(t),{recursive:!0,mode:448}),await g(t,E(e),{mode:384}),await u(t,384);const r=e.uid??process.getuid?.();if(r===void 0)throw new Error("Unable to determine current uid for launchctl");const o=`gui/${r}`;return await a("launchctl",["bootout",o,t],{allowFailure:!0}),await a("launchctl",["bootstrap",o,t]),await a("launchctl",["kickstart","-k",`${o}/${s}`]),{servicePath:t,commands:x(),warnings:[]}}function L(){return{status:`systemctl --user status ${n}.service`,logs:`journalctl --user -u ${n}.service -n 100 -f`,start:`systemctl --user start ${n}.service`,restart:`systemctl --user restart ${n}.service`,stop:`systemctl --user stop ${n}.service`,uninstall:`systemctl --user disable --now ${n}.service && rm -f ~/.config/systemd/user/${n}.service && systemctl --user daemon-reload`}}function x(){const e="gui/$(id -u)",t=`~/Library/LaunchAgents/${s}.plist`;return{status:`launchctl print ${e}/${s}`,logs:"tail -f ~/.coding-agent/data/logs/client-service.out.log ~/.coding-agent/data/logs/client-service.err.log",start:`launchctl bootstrap ${e} ${t}`,restart:`launchctl kickstart -k ${e}/${s} 2>/dev/null || launchctl bootstrap ${e} ${t}`,stop:`launchctl bootout ${e} ${t}`,uninstall:`launchctl bootout ${e} ${t}; rm -f ${t}`}}async function N(e=process.platform){if(e==="linux"){await a("systemctl",["--user","disable","--now",`${n}.service`],{allowFailure:!0}),await m(i(process.env.HOME??"",".config","systemd","user",`${n}.service`)).catch(t=>{if(t.code!=="ENOENT")throw t}),await a("systemctl",["--user","daemon-reload"],{allowFailure:!0});return}if(e==="darwin"){const t=process.getuid?.();if(t===void 0)throw new Error("Unable to determine current uid for launchctl");const r=i(process.env.HOME??"","Library","LaunchAgents",`${s}.plist`);await a("launchctl",["bootout",`gui/${t}`,r],{allowFailure:!0}),await m(r).catch(o=>{if(o.code!=="ENOENT")throw o});return}throw new Error("Only Linux and macOS are supported")}export{E as createLaunchdPlist,v as createSystemdService,x as darwinCommands,b as ensureSystemdUserLinger,F as installService,s as launchdLabel,L as linuxCommands,n as serviceName,D as shellEscape,N as uninstallService};
1
+ import{access as A,chmod as h,mkdir as g,unlink as d,writeFile as f}from"node:fs/promises";import{userInfo as P}from"node:os";import{dirname as y,join as a}from"node:path";import{runCommand as n}from"./exec.js";import{legacyCodingAgentPaths as $}from"./paths.js";import{launchdLabel as c,legacyLaunchdLabel as u,legacyServiceName as i,productName as w,serviceName as s}from"./identity.js";import{launchdLabel as J,serviceName as Q}from"./identity.js";function m(e){return`'${e.replaceAll("'","'\\''")}'`}function O(e){return["[Unit]",`Description=${w}`,"After=network-online.target","","[Service]","Type=simple",`ExecStart=${v(e.serviceCommandPath)}`,"Restart=always","RestartSec=5",`WorkingDirectory=${v(e.workspacePath)}`,`Environment=${E("AGENT_CONFIG_PATH",e.configPath)}`,...e.path?[`Environment=${E("PATH",e.path)}`]:[],"","[Install]","WantedBy=default.target",""].join(`
2
+ `)}function v(e){return e.replaceAll("\\","\\\\").replaceAll(" ","\\x20")}function E(e,t){return`${e}="${t.replaceAll("\\","\\\\").replaceAll('"','\\"')}"`}function l(e){return e.replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;").replaceAll("'","&apos;")}function S(e){const t=a(e.dataDir,"logs","client-service.out.log"),r=a(e.dataDir,"logs","client-service.err.log");return['<?xml version="1.0" encoding="UTF-8"?>','<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">','<plist version="1.0">',"<dict>"," <key>Label</key>",` <string>${l(c)}</string>`," <key>ProgramArguments</key>"," <array>",` <string>${l(e.serviceCommandPath)}</string>`," </array>"," <key>WorkingDirectory</key>",` <string>${l(e.workspacePath)}</string>`," <key>EnvironmentVariables</key>"," <dict>"," <key>AGENT_CONFIG_PATH</key>",` <string>${l(e.configPath)}</string>`,...e.path?[" <key>PATH</key>",` <string>${l(e.path)}</string>`]:[]," </dict>"," <key>RunAtLoad</key>"," <true/>"," <key>KeepAlive</key>"," <true/>"," <key>StandardOutPath</key>",` <string>${l(t)}</string>`," <key>StandardErrorPath</key>",` <string>${l(r)}</string>`,"</dict>","</plist>",""].join(`
3
+ `)}async function p(e){const t=(process.env.PATH??"").split(":").filter(Boolean);for(const r of t)try{return await A(a(r,e)),!0}catch{}return!1}const F={commandExists:p,runCommand:n};async function C(e,t=F){if(!await t.commandExists("loginctl"))throw new Error(`loginctl is required to keep the user service running after logout. Enable linger for ${e} and retry installation.`);if(await b(e,t.runCommand))return;if(console.log(`Enabling login persistence for ${e}`),(await t.runCommand("loginctl",["enable-linger",e],{allowFailure:!0,interactive:!0})).status!==0)throw new Error(`Unable to enable login persistence for ${e}. Run 'sudo loginctl enable-linger ${e}' and retry installation.`);if(!await b(e,t.runCommand))throw new Error(`Login persistence is still disabled for ${e}. Run 'sudo loginctl enable-linger ${e}' and retry installation.`)}async function b(e,t){const r=await t("loginctl",["show-user",e,"-p","Linger"],{allowFailure:!0});return r.status===0&&r.stdout.trim()==="Linger=yes"}async function G(e){const t=e.platform??process.platform;if(t==="linux")return N(e);if(t==="darwin")return T(e);throw new Error("Only Linux and macOS are supported")}async function N(e){if(!await p("systemctl"))throw new Error("systemctl is required to install the user service");const t=e.user??process.env.USER??P().username;await C(t);const r=a(process.env.HOME??"",".config","systemd","user",`${s}.service`);return await g(y(r),{recursive:!0,mode:448}),await f(r,O(e),{mode:384}),await h(r,384),await n("systemctl",["--user","daemon-reload"]),await n("systemctl",["--user","enable","--now",`${s}.service`]),await n("systemctl",["--user","restart",`${s}.service`]),await L("linux"),{servicePath:r,commands:x(),warnings:[]}}async function T(e){if(!await p("launchctl"))throw new Error("launchctl is required to install the LaunchAgent");const t=a(process.env.HOME??"","Library","LaunchAgents",`${c}.plist`);await g(y(t),{recursive:!0,mode:448}),await f(t,S(e),{mode:384}),await h(t,384);const r=e.uid??process.getuid?.();if(r===void 0)throw new Error("Unable to determine current uid for launchctl");const o=`gui/${r}`;return await n("launchctl",["bootout",o,t],{allowFailure:!0}),await n("launchctl",["bootstrap",o,t]),await L("darwin",e.uid),{servicePath:t,commands:k(),warnings:[]}}async function L(e=process.platform,t=process.getuid?.()){if(e==="linux"){if((await n("systemctl",["--user","is-active","--quiet",`${s}.service`],{allowFailure:!0})).status!==0)throw new Error(`${w} systemd service did not become active`);return}if(e==="darwin"){if(t===void 0)throw new Error("Unable to determine current uid for launchctl");const r=await n("launchctl",["print",`gui/${t}/${c}`],{allowFailure:!0});if(r.status!==0||!r.stdout.includes("state = running"))throw new Error(`${w} LaunchAgent did not become active`);return}throw new Error("Only Linux and macOS are supported")}function x(){return{status:`systemctl --user status ${s}.service`,logs:`journalctl --user -u ${s}.service -n 100 -f`,start:`systemctl --user start ${s}.service`,restart:`systemctl --user restart ${s}.service`,stop:`systemctl --user stop ${s}.service`,uninstall:`systemctl --user disable --now ${s}.service && rm -f ~/.config/systemd/user/${s}.service && systemctl --user daemon-reload`}}function D(){return{status:`systemctl --user status ${i}.service`,logs:`journalctl --user -u ${i}.service -n 100 -f`,start:`systemctl --user start ${i}.service`,restart:`systemctl --user restart ${i}.service`,stop:`systemctl --user stop ${i}.service`,uninstall:`systemctl --user disable --now ${i}.service && rm -f ~/.config/systemd/user/${i}.service && systemctl --user daemon-reload`}}function k(e=a(process.env.HOME??"",".messenger-agent","data")){const t="gui/$(id -u)",r=`~/Library/LaunchAgents/${c}.plist`;return{status:`launchctl print ${t}/${c}`,logs:`tail -f ${m(a(e,"logs","client-service.out.log"))} ${m(a(e,"logs","client-service.err.log"))}`,start:`launchctl bootstrap ${t} ${r}`,restart:`launchctl kickstart -k ${t}/${c} 2>/dev/null || launchctl bootstrap ${t} ${r}`,stop:`launchctl bootout ${t} ${r}`,uninstall:`launchctl bootout ${t} ${r}; rm -f ${r}`}}function H(){const e="gui/$(id -u)",t=`~/Library/LaunchAgents/${u}.plist`,r=$().dataDir;return{status:`launchctl print ${e}/${u}`,logs:`tail -f ${m(a(r,"logs","client-service.out.log"))} ${m(a(r,"logs","client-service.err.log"))}`,start:`launchctl bootstrap ${e} ${t}`,restart:`launchctl kickstart -k ${e}/${u} 2>/dev/null || launchctl bootstrap ${e} ${t}`,stop:`launchctl bootout ${e} ${t}`,uninstall:`launchctl bootout ${e} ${t}; rm -f ${t}`}}function _(e){const t=e===$().configPath;return process.platform==="darwin"?t?H():k():t?D():x()}async function B(e=process.platform){if(e==="linux"){await n("systemctl",["--user","disable","--now",`${i}.service`],{allowFailure:!0}),await d(a(process.env.HOME??"",".config","systemd","user",`${i}.service`)).catch(t=>{if(t.code!=="ENOENT")throw t}),await n("systemctl",["--user","daemon-reload"],{allowFailure:!0});return}if(e==="darwin"){const t=process.getuid?.();if(t===void 0)throw new Error("Unable to determine current uid for launchctl");const r=a(process.env.HOME??"","Library","LaunchAgents",`${u}.plist`);await n("launchctl",["bootout",`gui/${t}`,r],{allowFailure:!0}),await d(r).catch(o=>{if(o.code!=="ENOENT")throw o});return}throw new Error("Only Linux and macOS are supported")}async function W(e=process.platform){if(e==="linux"){await n("systemctl",["--user","stop",`${i}.service`],{allowFailure:!0});return}if(e==="darwin"){const t=process.getuid?.();if(t===void 0)throw new Error("Unable to determine current uid for launchctl");const r=a(process.env.HOME??"","Library","LaunchAgents",`${u}.plist`);await n("launchctl",["bootout",`gui/${t}`,r],{allowFailure:!0});return}throw new Error("Only Linux and macOS are supported")}async function K(e=process.platform){if(e==="linux"){await n("systemctl",["--user","start",`${i}.service`],{allowFailure:!0});return}if(e==="darwin"){const t=process.getuid?.();if(t===void 0)throw new Error("Unable to determine current uid for launchctl");const r=a(process.env.HOME??"","Library","LaunchAgents",`${u}.plist`);await n("launchctl",["bootstrap",`gui/${t}`,r],{allowFailure:!0});return}throw new Error("Only Linux and macOS are supported")}async function V(e=process.platform){if(e==="linux"){await n("systemctl",["--user","disable","--now",`${s}.service`],{allowFailure:!0}),await d(a(process.env.HOME??"",".config","systemd","user",`${s}.service`)).catch(t=>{if(t.code!=="ENOENT")throw t}),await n("systemctl",["--user","daemon-reload"],{allowFailure:!0});return}if(e==="darwin"){const t=process.getuid?.();if(t===void 0)throw new Error("Unable to determine current uid for launchctl");const r=a(process.env.HOME??"","Library","LaunchAgents",`${c}.plist`);await n("launchctl",["bootout",`gui/${t}`,r],{allowFailure:!0}),await d(r).catch(o=>{if(o.code!=="ENOENT")throw o});return}throw new Error("Only Linux and macOS are supported")}export{S as createLaunchdPlist,O as createSystemdService,k as darwinCommands,C as ensureSystemdUserLinger,G as installService,J as launchdLabel,H as legacyDarwinCommands,D as legacyLinuxCommands,x as linuxCommands,_ as serviceCommandsForConfig,Q as serviceName,m as shellEscape,K as startLegacyService,W as stopLegacyService,B as uninstallLegacyService,V as uninstallService,L as verifyServiceHealth};
@@ -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 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{readClientPath as C,readUpgradeChannel as E,writeClientPath as I}from"./config-file.js";import{readControlSocketPath as k,startControlServer as b}from"./control.js";import{MaintenanceScheduler as $}from"./maintenance.js";import{currentBundledSkillsDir as N,defaultAgentHomes as O,installRuntime as F,syncBundledSkills as G}from"./runtime.js";import{darwinCommands as L,launchdLabel as v,linuxCommands as R,serviceName as f}from"./service.js";import{runCommand as u}from"./exec.js";import{AutoUpgradeScheduler as _}from"./auto-upgrade.js";import{isAgentActivityResponse as j}from"@messenger-agent/shared/agent-activity";const w=x(import.meta.url);function B(){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 q(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 U(s){const t=S(await M(s,"utf8")),e=q(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 K{options;stopping=!1;running;restartBaseMs;restartMaxMs;constructor(t){this.options=t,this.running=(t.agents??B()).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=>{!j(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 Y(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 ot(s){await G(N(),O());const t=await U(s),e=new K({configPath:s,workspacePath:t}),r=await k(s),i=new $({statePath:d(m(r),"maintenance.json"),getActivity:()=>e.activityStatus(),execute:(c,p)=>H(c,s,e,p)});await i.start();const n=new _({statePath:d(m(r),"auto-upgrade.json"),maintenance:i,getChannel:()=>E(s)});await n.start();const a=await b(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 H(s,t,e,r){if(s.type==="upgrade"){s.path&&await I(t,s.path),await F({version:s.version,configPath:t,path:s.path??await C(t)}),await r("restarting"),await g(h().restart);return}if(s.type==="restart"&&s.agent){await e.restartAgent(s.agent);return}if(s.type==="restart"){await r("restarting"),await g(h().restart);return}if(s.type==="stop"){await r("stopping"),await g(h().stop);return}await r("stopping"),await D()}function h(){return process.platform==="darwin"?L():R()}async function g(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 D(){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 Y(s){return new Promise(t=>{const e=setTimeout(()=>{s.kill("SIGKILL")},8e3);s.once("exit",()=>{clearTimeout(e),t()})})}export{K as AgentSupervisor,U as readWorkspacePathFromConfig,B as resolveAgentEntries,ot as runSupervisor};
1
+ import{spawn as v}from"node:child_process";import{randomUUID as A}from"node:crypto";import{createRequire as P}from"node:module";import{dirname as w,join as m}from"node:path";import{parse as T}from"yaml";import{readFile as S,unlink as g}from"node:fs/promises";import{readClientPath as x,readUpgradeChannel as b,writeClientPath as C}from"./config-file.js";import{readControlSocketPath as M,startControlServer as k}from"./control.js";import{MaintenanceScheduler as E}from"./maintenance.js";import{currentBundledSkillsDir as I,defaultAgentHomes as $,installRuntime as L,rollbackRuntime as N,syncBundledSkills as F}from"./runtime.js";import{launchdLabel as O,serviceCommandsForConfig as R,serviceName as G}from"./service.js";import{legacyLaunchdLabel as _,legacyServiceName as j}from"./identity.js";import{legacyCodingAgentPaths as B}from"./paths.js";import{runCommand as u}from"./exec.js";import{AutoUpgradeScheduler as q}from"./auto-upgrade.js";import{isAgentActivityResponse as U}from"@messenger-agent/shared/agent-activity";const h=P(import.meta.url);function K(){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 H(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 D(s){const t=T(await S(s,"utf8")),e=H(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 w(s)}class Y{options;stopping=!1;running;restartBaseMs;restartMaxMs;constructor(t){this.options=t,this.running=(t.agents??K()).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(a=>a.available);return{active:i.reduce((a,n)=>a+n.active,0),waiting:i.reduce((a,n)=>a+n.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 a=A(),n={type:"client.activity.request",requestId:a};let c=!1;const l=o=>{c||(c=!0,clearTimeout(y),r.off("message",d),i(o))},d=o=>{!U(o)||o.requestId!==a||o.agent!==t||l({available:!0,...o.snapshot})},y=setTimeout(()=>l({available:!1,error:`${t} agent did not respond`}),e);r.on("message",d),r.send(n,o=>{o&&l({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 z(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??v)(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(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",(i,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=${i??"null"} signal=${a??"null"}; restarting in ${n}ms`),t.restartTimer=setTimeout(()=>{t.restartTimer=void 0,this.startAgent(t)},n)})}}async function mt(s){await F(I(),$());const t=await D(s),e=new Y({configPath:s,workspacePath:t}),r=await M(s),i=new E({statePath:m(w(r),"maintenance.json"),getActivity:()=>e.activityStatus(),execute:(l,d)=>W(l,s,e,d)});await i.start();const a=new q({statePath:m(w(r),"auto-upgrade.json"),maintenance:i,getChannel:()=>b(s)});await a.start();const n=await k(r,{restartAgent:l=>e.restartAgent(l),getActivity:()=>e.activityStatus(),maintenance:i});e.start();const c=async()=>{a.stop(),i.stop(),await n.close(),await e.stop(),process.exit(0)};process.once("SIGINT",()=>{c()}),process.once("SIGTERM",()=>{c()})}async function W(s,t,e,r){if(s.type==="upgrade"){s.path&&await C(t,s.path);const i=await L({version:s.version,configPath:t,path:s.path??await x(t)});await r("restarting");try{await f(p(t).restart)}catch(a){throw await N(i),await f(p(t).restart).catch(()=>{}),a}return}if(s.type==="restart"&&s.agent){await e.restartAgent(s.agent);return}if(s.type==="restart"){await r("restarting"),await f(p(t).restart);return}if(s.type==="stop"){await r("stopping"),await f(p(t).stop);return}await r("stopping"),await X(t)}function p(s){return R(s)}async function f(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 X(s){const t=process.env.HOME??"",e=s===B().configPath,r=e?j:G,i=e?_:O;if(process.platform==="linux"){await u("systemctl",["--user","disable",`${r}.service`],{allowFailure:!0}),await g(m(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=m(t,"Library","LaunchAgents",`${i}.plist`);await g(n).catch(c=>{if(c.code!=="ENOENT")throw c}),await u("launchctl",["bootout",`gui/${a}/${i}`],{allowFailure:!0});return}throw new Error("Only Linux and macOS are supported")}function z(s){return new Promise(t=>{const e=setTimeout(()=>{s.kill("SIGKILL")},8e3);s.once("exit",()=>{clearTimeout(e),t()})})}export{Y as AgentSupervisor,D as readWorkspacePathFromConfig,K as resolveAgentEntries,mt as runSupervisor};
package/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "@messenger-agent/client",
3
- "version": "0.32.2",
3
+ "version": "0.33.0-alpha.5",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
7
7
  "bin": {
8
+ "messenger-agent": "./dist/index.js",
8
9
  "coding-agent": "./dist/index.js"
9
10
  },
10
11
  "files": [
@@ -20,10 +21,10 @@
20
21
  "dependencies": {
21
22
  "cac": "^7.0.0",
22
23
  "yaml": "^2.9.0",
23
- "@messenger-agent/codex-agent": "0.32.2",
24
- "@messenger-agent/claude-agent": "0.32.2",
25
- "@messenger-agent/messenger-agent": "0.32.2",
26
- "@messenger-agent/shared": "0.32.2"
24
+ "@messenger-agent/codex-agent": "0.33.0-alpha.5",
25
+ "@messenger-agent/messenger-agent": "0.33.0-alpha.5",
26
+ "@messenger-agent/claude-agent": "0.33.0-alpha.5",
27
+ "@messenger-agent/shared": "0.33.0-alpha.5"
27
28
  },
28
29
  "scripts": {
29
30
  "build": "rm -rf dist && tsc -p tsconfig.json && node ../../scripts/copy-client-assets.mjs && chmod +x dist/index.js",