@messenger-agent/client 0.32.1 → 0.32.2

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.
@@ -10,11 +10,14 @@ export type ClientConfigOptions = {
10
10
  server?: string;
11
11
  serverUrl?: string;
12
12
  preserveWorkspaces?: boolean;
13
+ path?: string;
13
14
  };
14
15
  type YamlObject = Record<string, unknown>;
15
16
  export declare function ensureClientDirectories(dataDir: string, configPath: string): Promise<void>;
16
17
  export declare function readConfigYaml(configPath: string): Promise<YamlObject>;
17
18
  export declare function mergeClientConfig(existing: YamlObject, options: ClientConfigOptions): YamlObject;
19
+ export declare function readClientPath(configPath: string): Promise<string | undefined>;
20
+ export declare function writeClientPath(configPath: string, path: string): Promise<void>;
18
21
  export declare function readUpgradeChannel(configPath: string): Promise<ReleaseChannel>;
19
22
  export declare function writeUpgradeChannel(configPath: string, channel: ReleaseChannel): Promise<void>;
20
23
  export declare function writeConfigYaml(configPath: string, config: YamlObject): Promise<void>;
@@ -1 +1 @@
1
- import{constants as m}from"node:fs";import{access as w,chmod as u,mkdir as n,readFile as p,rename as g,stat as v,writeFile as x}from"node:fs/promises";import{dirname as s,join as o}from"node:path";import{normalizeAgentServer as C}from"@messenger-agent/shared/agent-config";import{parse as y,stringify as _}from"yaml";import{normalizeReleaseChannel as h}from"./runtime.js";function c(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function d(e,r){const t=e[r];if(c(t))return t;const a={};return e[r]=a,a}async function k(e,r){await n(s(r),{recursive:!0,mode:448}),await u(s(r),448),await n(e,{recursive:!0,mode:448}),await n(o(e,"logs"),{recursive:!0,mode:448}),await n(o(e,"uploads"),{recursive:!0,mode:448}),await n(o(e,"runtime"),{recursive:!0,mode:448})}async function U(e){try{return await w(e,m.F_OK),!0}catch{return!1}}async function f(e){if(!await U(e))return{};const r=y(await p(e,"utf8"));if(r==null)return{};if(!c(r))throw new Error(`Config file must contain a YAML object: ${e}`);return r}function $(e,r){const t={...e};t.log_level??="info",r.preserveWorkspaces||(t.workspaces=[{id:r.workspaceId,name:r.workspaceName??`${r.tunnelId} Default`,path:r.workspacePath}]),t.data_dir=r.dataDir,r.server&&(t.server=C(r.server));const a=d(t,"file_uploads");a.temp_dir=o(r.dataDir,"uploads");const i=d(t,"tunnel");return i.enabled=!0,r.serverUrl&&(i.server_url=r.serverUrl),i.tunnel_id=r.tunnelId,i.token=r.token,t}async function I(e){const t=(await f(e)).auto_upgrade,a=c(t)?t.channel:void 0;return h(a)??"latest"}async function Y(e,r){const t=await f(e),a=d(t,"auto_upgrade");a.channel=r,await l(e,t)}async function l(e,r){await n(s(e),{recursive:!0,mode:448});const t=`${e}.${process.pid}.${Date.now()}.tmp`;await x(t,_(r),{mode:384}),await u(t,384),await g(t,e),await u(e,384)}async function z(e){await k(e.dataDir,e.configPath);const r=await f(e.configPath),t=$(r,e);return await l(e.configPath,t),t}async function R(e){if(((await v(e)).mode&511&63)!==0)throw new Error(`Config file permissions are too broad: ${e}`)}export{R as assertPrivateConfig,k as ensureClientDirectories,$ as mergeClientConfig,f as readConfigYaml,I as readUpgradeChannel,z as updateClientConfig,l as writeConfigYaml,Y as writeUpgradeChannel};
1
+ import{constants as w}from"node:fs";import{access as p,chmod as f,mkdir as a,readFile as g,rename as h,stat as v,writeFile as x}from"node:fs/promises";import{dirname as l,join as s}from"node:path";import{normalizeAgentServer as y}from"@messenger-agent/shared/agent-config";import{parse as C,stringify as _}from"yaml";import{normalizeReleaseChannel as k}from"./runtime.js";function u(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function i(e,t){const r=e[t];if(u(r))return r;const n={};return e[t]=n,n}async function U(e,t){await a(l(t),{recursive:!0,mode:448}),await f(l(t),448),await a(e,{recursive:!0,mode:448}),await a(s(e,"logs"),{recursive:!0,mode:448}),await a(s(e,"uploads"),{recursive:!0,mode:448}),await a(s(e,"runtime"),{recursive:!0,mode:448})}async function $(e){try{return await p(e,w.F_OK),!0}catch{return!1}}async function o(e){if(!await $(e))return{};const t=C(await g(e,"utf8"));if(t==null)return{};if(!u(t))throw new Error(`Config file must contain a YAML object: ${e}`);return t}function b(e,t){const r={...e};if(r.log_level??="info",t.preserveWorkspaces||(r.workspaces=[{id:t.workspaceId,name:t.workspaceName??`${t.tunnelId} Default`,path:t.workspacePath}]),r.data_dir=t.dataDir,t.path){const m=i(r,"client");m.path=t.path}t.server&&(r.server=y(t.server));const n=i(r,"file_uploads");n.temp_dir=s(t.dataDir,"uploads");const c=i(r,"tunnel");return c.enabled=!0,t.serverUrl&&(c.server_url=t.serverUrl),c.tunnel_id=t.tunnelId,c.token=t.token,r}async function Y(e){const r=(await o(e)).client;if(!(!u(r)||typeof r.path!="string"||!r.path.trim()))return r.path}async function z(e,t){const r=await o(e),n=i(r,"client");n.path=t,await d(e,r)}async function R(e){const r=(await o(e)).auto_upgrade,n=u(r)?r.channel:void 0;return k(n)??"latest"}async function K(e,t){const r=await o(e),n=i(r,"auto_upgrade");n.channel=t,await d(e,r)}async function d(e,t){await a(l(e),{recursive:!0,mode:448});const r=`${e}.${process.pid}.${Date.now()}.tmp`;await x(r,_(t),{mode:384}),await f(r,384),await h(r,e),await f(e,384)}async function L(e){await U(e.dataDir,e.configPath);const t=await o(e.configPath),r=b(t,e);return await d(e.configPath,r),r}async function M(e){if(((await v(e)).mode&511&63)!==0)throw new Error(`Config file permissions are too broad: ${e}`)}export{M as assertPrivateConfig,U as ensureClientDirectories,b as mergeClientConfig,Y as readClientPath,o as readConfigYaml,R as readUpgradeChannel,L as updateClientConfig,z as writeClientPath,d as writeConfigYaml,K as writeUpgradeChannel};
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import{parseCliArgs as f,UsageError as p,usage as i}from"./args.js";import{installClient as $}from"./install.js";import{currentPackageVersion as w,installRuntime as h,normalizeUpgradeVersion as v}from"./runtime.js";import{darwinCommands as c,linuxCommands as l,serviceName as m,uninstallService as P}from"./service.js";import{runSupervisor as y}from"./supervisor.js";import{runCommand as S}from"./exec.js";import{defaultConfigPath as s}from"./paths.js";import{writeUpgradeChannel as C}from"./config-file.js";import{requestAgentRestart as A,requestClientActivity as N,requestMaintenanceCancel as T,requestMaintenanceStatus as W,requestScheduleMaintenance as G}from"./control.js";async function O(){const e=f(process.argv.slice(2));if(e.command==="version"){console.log(`coding-agent ${await w()}`);return}if(e.command==="run-service"){await y(e.configPath??process.env.AGENT_CONFIG_PATH??s);return}if(e.command==="install"){const n=await $(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}=v(e.version,e.channel);if(t&&(await C(e.configPath,t),console.log(`Auto-upgrade channel: ${t}`)),e.delaySeconds!==void 0){await r(e.configPath,{type:"upgrade",version:n},e.delaySeconds,e.allowWaiting);return}const a=await h({version:n,configPath:e.configPath}),o=process.platform==="darwin"?c():l();await g(o.restart),console.log(`Upgraded runtime to ${a.version}`),console.log(`Runtime: ${a.currentLink}`),console.log(`Command: ${a.cliWrapperPath}`),console.log(`Status: ${a.cliWrapperPath} status`);return}if(e.command==="sessions"){const n=await N(e.configPath);console.log(e.json?JSON.stringify(n):_(n));return}if(e.command==="maintenance"){const n=e.action==="cancel"?await T(e.configPath):await W(e.configPath);console.log(e.json?JSON.stringify(n??null):b(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 A(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 P(),console.log(`Uninstalled ${m}`);return}}async function r(e,n,t,a){const o=await G(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 _(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 b(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)}O().catch(e=>{if(e instanceof p){const t=e.message===i()?e.message:`${e.message}
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}
5
5
 
6
6
  ${i()}`;console.error(t),process.exit(2)}const n=e instanceof Error?e.message:String(e);console.error(n),process.exit(1)});
package/dist/install.js CHANGED
@@ -1 +1 @@
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 g}from"./config-file.js";import{installRuntime as P}from"./runtime.js";import{installService as A}from"./service.js";import{UsageError as d}from"./args.js";import{defaultWorkspacePath as f}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 z(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}),e.channel&&await g(r.configPath,e.channel);const o=await P({version:"current",configPath:r.configPath}),a=await A({configPath:r.configPath,dataDir:r.dataDir,workspacePath:t,serviceCommandPath:o.wrapperPath});return{configPath:r.configPath,dataDir:r.dataDir,workspacePath:t,runtime:o,service:a}}async function I(e){const r=await v(e.configPath),t=C(r,"tunnel"),o=e.tunnelId??p(t,"tunnel_id"),a=e.token??p(t,"token"),s=E(r,e.workspaceId,o),i=e.workspace??s?.path??f,n=e.workspaceId??s?.id??w(),c=e.workspaceName??s?.name??(o?`${o} Default`:void 0),u=i===f;if(!o)throw new d("Missing required option: --tunnel-id");if(!a)throw new d("Missing required option: --token");if(!n)throw new d("Missing required option: --workspace-id");if(!c)throw new d("Missing required option: --workspace-name");return{...e,tunnelId:o,token:a,workspace:i,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 a=o.filter(n=>!!n&&typeof n=="object"&&!Array.isArray(n)&&typeof n.path=="string");if(r){const n=a.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),i=(s?a.find(n=>n.id===s):void 0)??a.find(n=>n.id==="default")??a[0];if(!(!i||typeof i.path!="string"))return{id:typeof i.id=="string"&&i.id?i.id:s??"default",name:typeof i.name=="string"&&i.name?i.name:`${t??s??"default"} Default`,path:i.path}}function C(e,r){const t=e[r];if(!(!t||typeof t!="object"||Array.isArray(t)))return t}function p(e,r){const t=e?.[r];return typeof t=="string"&&t?t:void 0}export{D as assertNodeVersion,x as assertSupportedPlatform,z as installClient,I as resolveInstallArgs,b as resolveWorkspacePath,N as validateInstallArgs};
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};
@@ -2,6 +2,7 @@ import type { ClientActivityStatus, ManagedAgentName } from "./supervisor.js";
2
2
  export type MaintenanceOperation = {
3
3
  type: "upgrade";
4
4
  version: string;
5
+ path?: string;
5
6
  } | {
6
7
  type: "restart";
7
8
  agent?: ManagedAgentName;
package/dist/runtime.d.ts CHANGED
@@ -6,6 +6,7 @@ export type RuntimeInstallOptions = {
6
6
  skillsSourceDir?: string;
7
7
  codexHome?: string;
8
8
  claudeHome?: string;
9
+ path?: string;
9
10
  };
10
11
  export type RuntimeInstallResult = {
11
12
  releaseDir: string;
package/dist/runtime.js CHANGED
@@ -1,3 +1,3 @@
1
- import{access as O,cp as R,lstat as k,mkdir as g,readFile as j,rename as p,rm as h,symlink as V,writeFile as P}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 k(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??_,e.claudeHome??H]),await B(u)||await w(u),await J(a,u),await P($,["#!/bin/sh","set -eu",`export AGENT_CONFIG_PATH=${d(e.configPath)}`,`exec ${d(process.execPath)} ${d(l)} run-service --config ${d(e.configPath)}`,""].join(`
2
- `),{mode:448}),await P(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 k(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{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};
package/dist/service.d.ts CHANGED
@@ -6,6 +6,7 @@ export type ServiceInstallOptions = {
6
6
  dataDir: string;
7
7
  workspacePath: string;
8
8
  serviceCommandPath: string;
9
+ path?: string;
9
10
  platform?: NodeJS.Platform;
10
11
  uid?: number;
11
12
  user?: string;
package/dist/service.js CHANGED
@@ -1,3 +1,3 @@
1
- import{access as f,chmod as u,mkdir as d,unlink as m,writeFile as g}from"node:fs/promises";import{userInfo as h}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 T(e){return`'${e.replaceAll("'","'\\''")}'`}function $(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=${v("AGENT_CONFIG_PATH",e.configPath)}`,"","[Install]","WantedBy=default.target",""].join(`
2
- `)}function p(e){return e.replaceAll("\\","\\\\").replaceAll(" ","\\x20")}function v(e,t){return`${e}=${t.replaceAll("\\","\\\\").replaceAll('"','\\"')}`}function o(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>${o(s)}</string>`," <key>ProgramArguments</key>"," <array>",` <string>${o(e.serviceCommandPath)}</string>`," </array>"," <key>WorkingDirectory</key>",` <string>${o(e.workspacePath)}</string>`," <key>EnvironmentVariables</key>"," <dict>"," <key>AGENT_CONFIG_PATH</key>",` <string>${o(e.configPath)}</string>`," </dict>"," <key>RunAtLoad</key>"," <true/>"," <key>KeepAlive</key>"," <true/>"," <key>StandardOutPath</key>",` <string>${o(t)}</string>`," <key>StandardErrorPath</key>",` <string>${o(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 f(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 y(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 y(e,t.runCommand))throw new Error(`Login persistence is still disabled for ${e}. Run 'sudo loginctl enable-linger ${e}' and retry installation.`)}async function y(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 L(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??h().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,$(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:P(),warnings:[]}}async function L(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 l=`gui/${r}`;return await a("launchctl",["bootout",l,t],{allowFailure:!0}),await a("launchctl",["bootstrap",l,t]),await a("launchctl",["kickstart","-k",`${l}/${s}`]),{servicePath:t,commands:x(),warnings:[]}}function P(){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(l=>{if(l.code!=="ENOENT")throw l});return}throw new Error("Only Linux and macOS are supported")}export{E as createLaunchdPlist,$ as createSystemdService,x as darwinCommands,b as ensureSystemdUserLinger,F as installService,s as launchdLabel,P as linuxCommands,n as serviceName,T as shellEscape,N as uninstallService};
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 +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{readControlSocketPath as E,startControlServer as I}from"./control.js";import{MaintenanceScheduler as k}from"./maintenance.js";import{readUpgradeChannel as b}from"./config-file.js";import{currentBundledSkillsDir as C,defaultAgentHomes as $,installRuntime as N,syncBundledSkills as O}from"./runtime.js";import{darwinCommands as F,launchdLabel as v,linuxCommands as G,serviceName as f}from"./service.js";import{runCommand as u}from"./exec.js";import{AutoUpgradeScheduler as L}from"./auto-upgrade.js";import{isAgentActivityResponse as R}from"@messenger-agent/shared/agent-activity";const w=x(import.meta.url);function _(){return[{name:"codex",entry:w.resolve("@messenger-agent/codex-agent")},{name:"claude",entry:w.resolve("@messenger-agent/claude-agent")},{name:"workspace",entry:w.resolve("@messenger-agent/messenger-agent")}]}function j(s,t){let e=s;for(const r of t){if(!e||typeof e!="object"||Array.isArray(e))return;e=e[r]}return e}async function B(s){const t=S(await M(s,"utf8")),e=j(t,["workspaces"]);if(Array.isArray(e)){const r=e.find(i=>!!i&&typeof i=="object"&&!Array.isArray(i)&&typeof i.path=="string");if(typeof r?.path=="string"&&r.path.trim())return r.path}return m(s)}class q{options;stopping=!1;running;restartBaseMs;restartMaxMs;constructor(t){this.options=t,this.running=(t.agents??_()).map(e=>({definition:e,restartAttempts:0})),this.restartBaseMs=t.restartBaseMs??1e3,this.restartMaxMs=t.restartMaxMs??3e4}start(){for(const t of this.running)this.startAgent(t)}async stop(){this.stopping=!0;for(const t of this.running)t.restartTimer&&clearTimeout(t.restartTimer),t.process?.kill("SIGTERM");await Promise.all(this.running.map(t=>t.process).filter(t=>!!t&&t.exitCode===null&&!t.killed).map(t=>new Promise(e=>{const r=setTimeout(()=>{t.kill("SIGKILL"),e()},8e3);t.once("exit",()=>{clearTimeout(r),e()})})))}restartAgent(t){const e=this.running.find(({definition:r})=>r.name===t);return e?(e.restartPromise||(e.restartPromise=this.restartAgentProcess(e).finally(()=>{e.restartPromise=void 0})),e.restartPromise):Promise.reject(new Error(`Agent is not managed by this client: ${t}`))}async activityStatus(t=1e3){const[e,r]=await Promise.all([this.agentActivityStatus("codex",t),this.agentActivityStatus("claude",t)]),i=[e,r].filter(n=>n.available);return{active:i.reduce((n,a)=>n+a.active,0),waiting:i.reduce((n,a)=>n+a.waiting,0),agents:{codex:e,claude:r}}}agentActivityStatus(t,e){const r=this.running.find(({definition:i})=>i.name===t)?.process;return!r||!r.connected||!r.send?Promise.resolve({available:!1,error:`${t} agent is not connected`}):new Promise(i=>{const n=T(),a={type:"client.activity.request",requestId:n};let l=!1;const c=o=>{l||(l=!0,clearTimeout(A),r.off("message",p),i(o))},p=o=>{!R(o)||o.requestId!==n||o.agent!==t||c({available:!0,...o.snapshot})},A=setTimeout(()=>c({available:!1,error:`${t} agent did not respond`}),e);r.on("message",p),r.send(a,o=>{o&&c({available:!1,error:o.message})})})}async restartAgentProcess(t){t.restartTimer&&(clearTimeout(t.restartTimer),t.restartTimer=void 0);const e=t.process;if(e&&e.exitCode===null&&!e.killed&&(e.kill("SIGTERM"),await H(e)),this.stopping)throw new Error("Client service is stopping");t.restartAttempts=0,this.startAgent(t)}startAgent(t){const e={...process.env,AGENT_CONFIG_PATH:this.options.configPath};t.definition.name==="workspace"&&(delete e.OPENAI_API_KEY,delete e.CODEX_API_KEY,delete e.ANTHROPIC_API_KEY);const r=(this.options.spawnProcess??P)(process.execPath,[t.definition.entry],{cwd:this.options.workspacePath,env:e,stdio:["inherit","inherit","inherit","ipc"]});t.process=r,t.definition.name==="workspace"&&r.on("message",i=>{i&&typeof i=="object"&&"type"in i&&i.type==="client.workspaces.changed"&&Promise.all([this.restartAgent("codex"),this.restartAgent("claude")]).catch(n=>{console.error("[client] failed to reload agents after workspace configuration changed",n)})}),console.log(`[client] started ${t.definition.name}-agent pid=${r.pid}`),r.once("exit",(i,n)=>{if(t.process=void 0,this.stopping||t.restartPromise)return;const a=Math.min(this.restartBaseMs*2**t.restartAttempts,this.restartMaxMs);t.restartAttempts+=1,console.error(`[client] ${t.definition.name}-agent exited with code=${i??"null"} signal=${n??"null"}; restarting in ${a}ms`),t.restartTimer=setTimeout(()=>{t.restartTimer=void 0,this.startAgent(t)},a)})}}async function nt(s){await O(C(),$());const t=await B(s),e=new q({configPath:s,workspacePath:t}),r=await E(s),i=new k({statePath:d(m(r),"maintenance.json"),getActivity:()=>e.activityStatus(),execute:(c,p)=>U(c,s,e,p)});await i.start();const n=new L({statePath:d(m(r),"auto-upgrade.json"),maintenance:i,getChannel:()=>b(s)});await n.start();const a=await I(r,{restartAgent:c=>e.restartAgent(c),getActivity:()=>e.activityStatus(),maintenance:i});e.start();const l=async()=>{n.stop(),i.stop(),await a.close(),await e.stop(),process.exit(0)};process.once("SIGINT",()=>{l()}),process.once("SIGTERM",()=>{l()})}async function U(s,t,e,r){if(s.type==="upgrade"){await N({version:s.version,configPath:t}),await r("restarting"),await h(g().restart);return}if(s.type==="restart"&&s.agent){await e.restartAgent(s.agent);return}if(s.type==="restart"){await r("restarting"),await h(g().restart);return}if(s.type==="stop"){await r("stopping"),await h(g().stop);return}await r("stopping"),await K()}function g(){return process.platform==="darwin"?F():G()}async function h(s){const t=await u("/bin/sh",["-lc",s],{allowFailure:!0});if(t.status!==0)throw new Error(t.stderr||t.stdout||`Service command failed: ${s}`)}async function K(){const s=process.env.HOME??"";if(process.platform==="linux"){await u("systemctl",["--user","disable",`${f}.service`],{allowFailure:!0}),await y(d(s,".config","systemd","user",`${f}.service`)).catch(t=>{if(t.code!=="ENOENT")throw t}),await u("systemctl",["--user","daemon-reload"],{allowFailure:!0}),await u("systemctl",["--user","stop",`${f}.service`],{allowFailure:!0});return}if(process.platform==="darwin"){const t=process.getuid?.();if(t===void 0)throw new Error("Unable to determine current uid for launchctl");const e=d(s,"Library","LaunchAgents",`${v}.plist`);await y(e).catch(r=>{if(r.code!=="ENOENT")throw r}),await u("launchctl",["bootout",`gui/${t}/${v}`],{allowFailure:!0});return}throw new Error("Only Linux and macOS are supported")}function H(s){return new Promise(t=>{const e=setTimeout(()=>{s.kill("SIGKILL")},8e3);s.once("exit",()=>{clearTimeout(e),t()})})}export{q as AgentSupervisor,B as readWorkspacePathFromConfig,_ as resolveAgentEntries,nt as runSupervisor};
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};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@messenger-agent/client",
3
- "version": "0.32.1",
3
+ "version": "0.32.2",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -20,10 +20,10 @@
20
20
  "dependencies": {
21
21
  "cac": "^7.0.0",
22
22
  "yaml": "^2.9.0",
23
- "@messenger-agent/claude-agent": "0.32.1",
24
- "@messenger-agent/codex-agent": "0.32.1",
25
- "@messenger-agent/messenger-agent": "0.32.1",
26
- "@messenger-agent/shared": "0.32.1"
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"
27
27
  },
28
28
  "scripts": {
29
29
  "build": "rm -rf dist && tsc -p tsconfig.json && node ../../scripts/copy-client-assets.mjs && chmod +x dist/index.js",