@messenger-agent/client 0.33.0-alpha.5 → 0.33.0-alpha.8
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/exec.d.ts +1 -0
- package/dist/exec.js +1 -1
- package/dist/install.d.ts +7 -0
- package/dist/install.js +2 -2
- package/dist/service.d.ts +9 -2
- package/dist/service.js +4 -3
- package/dist/supervisor.d.ts +13 -0
- package/dist/supervisor.js +1 -1
- package/package.json +5 -5
package/dist/exec.d.ts
CHANGED
package/dist/exec.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{spawn as f}from"node:child_process";function a(e,o,
|
|
1
|
+
import{spawn as f}from"node:child_process";function a(e,o,t={}){return new Promise((c,s)=>{t.logCommand!==!1&&console.log(`$ ${e} ${o.join(" ")}`);const n=f(e,o,{stdio:t.interactive?"inherit":["ignore","pipe","pipe"],env:{...process.env,...t.env},cwd:t.cwd}),u=[],d=[];n.stdout?.on("data",r=>{u.push(r)}),n.stderr?.on("data",r=>{d.push(r)}),n.on("error",s),n.on("close",r=>{const i={status:r,stdout:Buffer.concat(u).toString("utf8"),stderr:Buffer.concat(d).toString("utf8")};if(!t.allowFailure&&r!==0){s(new Error(`${e} ${o.join(" ")} failed with status ${r}
|
|
2
2
|
${i.stderr||i.stdout}`));return}c(i)})})}export{a as runCommand};
|
package/dist/install.d.ts
CHANGED
|
@@ -16,6 +16,13 @@ export declare function scheduleDarwinMigrationHandoff(migration: ClientMigratio
|
|
|
16
16
|
wait?: (milliseconds: number) => Promise<unknown>;
|
|
17
17
|
attempts?: number;
|
|
18
18
|
}): Promise<boolean>;
|
|
19
|
+
export declare function scheduleMigrationHandoff(migration: ClientMigrationResult, runtime: RuntimeInstallResult, configPath: string, path?: string, dependencies?: {
|
|
20
|
+
run?: typeof runCommand;
|
|
21
|
+
wait?: (milliseconds: number) => Promise<unknown>;
|
|
22
|
+
attempts?: number;
|
|
23
|
+
platform?: NodeJS.Platform;
|
|
24
|
+
}): Promise<boolean>;
|
|
25
|
+
export declare function shouldAttemptAutomaticMigration(rootDir: string, version: string): Promise<boolean>;
|
|
19
26
|
export declare function completeMigrationHandoff(handoffPath: string): Promise<void>;
|
|
20
27
|
type ResolvedInstallArgs = Omit<InstallArgs, "tunnelId" | "token" | "workspace" | "workspaceId" | "workspaceName"> & {
|
|
21
28
|
tunnelId: string;
|
package/dist/install.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{randomUUID as
|
|
2
|
-
`,{mode:384}),await
|
|
1
|
+
import{randomUUID as N}from"node:crypto";import{mkdir as x,readFile as T,realpath as $,rename as O,stat as _,writeFile as L}from"node:fs/promises";import{join as y}from"node:path";import{setTimeout as C}from"node:timers/promises";import{normalizeAgentServer as H}from"@messenger-agent/shared/agent-config";import{readConfigYaml as v,updateClientConfig as j,writeUpgradeChannel as R}from"./config-file.js";import{installRuntime as W,rollbackRuntime as g}from"./runtime.js";import{installService as k,startLegacyService as P,stopLegacyService as A,uninstallLegacyService as S,uninstallService as D}from"./service.js";import{UsageError as w}from"./args.js";import{defaultWorkspacePath as E,legacyCodingAgentPaths as U,messengerAgentPaths as G}from"./paths.js";import{runCommand as q}from"./exec.js";import{completeLegacyMigration as I,migrateLegacyClient as z,rollbackLegacyMigration as F}from"./migration.js";const V="vip.elevo.messenger.agent.migration";async function lt(t,e,r,i){if(!t.migrated)return!1;if(process.platform==="darwin")return B(t,e,r,i);try{return await b(t,e,r,i),!0}catch(n){throw await g(e).catch(()=>{}),await M(t),n}}async function b(t,e,r,i){const n=await v(r),o=m(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=m(s,"path");if(!a)throw new Error("Migrated config does not contain a workspace path");await A(),await k({configPath:r,dataDir:o,workspacePath:a,serviceCommandPath:e.wrapperPath,path:i}),await S(),await I(t)}async function B(t,e,r,i,n={}){return J(t,e,r,i,{...n,platform:"darwin"})}async function J(t,e,r,i,n={}){const o=n.run??q,s=n.wait??C,a=n.attempts??1200,c=n.platform??process.platform;if(c!=="darwin"&&c!=="linux")throw new Error("Only Linux and macOS are supported");const d=y(t.paths.rootDir,"migration-handoff.json"),u=c==="darwin"?`${V}.${process.pid}`:`messenger-agent-migration-${process.pid}`;await p(d,{schemaVersion:1,status:"scheduled",helperLabel:u,migration:t,runtime:e,configPath:r,...i?{path:i}:{},updatedAt:new Date().toISOString()});try{c==="darwin"?await o("launchctl",["submit","-l",u,"--",e.cliWrapperPath,"complete-migration","--handoff",d]):await o("systemd-run",["--user","--quiet","--collect","--unit",u,"--",e.cliWrapperPath,"complete-migration","--handoff",d])}catch(f){throw await p(d,{...await l(d),status:"failed",message:f instanceof Error?f.message:String(f),updatedAt:new Date().toISOString()}),f}for(let f=0;f<a;f+=1){const h=await l(d);if(h.status==="completed")return c==="darwin"&&await o("launchctl",["remove",u],{allowFailure:!0}),!0;if(h.status==="failed")throw new Error(h.message??"Service migration failed");await s(100)}throw new Error(`Timed out waiting for service migration; inspect ${d}`)}async function mt(t,e){try{const r=await l(y(t,"migration-handoff.json"));return r.status!=="completed"&&r.runtime.version!==e}catch(r){if(r.code==="ENOENT")return!0;throw r}}async function ht(t){const e=await l(t);await p(t,{...e,status:"running",updatedAt:new Date().toISOString()});try{await b(e.migration,e.runtime,e.configPath,e.path),await p(t,{...e,status:"completed",updatedAt:new Date().toISOString()})}catch(r){const i=[];await g(e.runtime).catch(o=>{i.push(`runtime rollback failed: ${String(o)}`)}),await D().catch(o=>{i.push(`new service cleanup failed: ${String(o)}`)}),await P().catch(o=>{i.push(`legacy service restart failed: ${String(o)}`)});const n=[r instanceof Error?r.message:String(r),...i].join("; ");throw await p(t,{...e,status:"failed",message:n,updatedAt:new Date().toISOString()}),r}}async function l(t){const e=JSON.parse(await T(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 L(r,`${JSON.stringify(e,null,2)}
|
|
2
|
+
`,{mode:384}),await O(r,t)}function Z(t=process.platform){if(t!=="linux"&&t!=="darwin")throw new Error("Only Linux and macOS are supported")}function Y(t=process.versions.node){const e=/^(\d+)\.(\d+)\.(\d+)(?:[.-]|$)/.exec(t),r=Number(e?.[1]),i=Number(e?.[2]);if(!(r>22||r===22&&i>=19))throw new Error("Node.js 22.19.0 or newer is required")}function K(t){if(t.server)try{H(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 Q(t,e=!1){e&&await x(t,{recursive:!0});const r=await $(t);if(!(await _(r)).isDirectory())throw new Error("--workspace must point to an existing directory");return r}async function gt(t){Z(),Y();const e=G(),r=U(),n=t.configPath===e.configPath||t.configPath===r.configPath?await z():void 0;let o;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 X(s);K(a);const c=await Q(a.workspace,a.createWorkspace);await j({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 R(a.configPath,t.channel),o=await W({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 A();const d=await k({configPath:a.configPath,dataDir:a.dataDir,workspacePath:c,serviceCommandPath:o.wrapperPath,path:process.env.PATH});return n?.migrated&&(await S(),await I(n)),{configPath:a.configPath,dataDir:a.dataDir,workspacePath:c,runtime:o,service:d}}catch(s){throw o&&await g(o).catch(()=>{}),n?.migrated&&await M(n),s}}async function M(t){await D().catch(()=>{}),await F(t),await P().catch(()=>{})}async function X(t){const e=await v(t.configPath),r=et(e,"tunnel"),i=t.tunnelId??m(r,"tunnel_id"),n=t.token??m(r,"token"),o=tt(e,t.workspaceId,i),s=t.workspace??o?.path??E,a=t.workspaceId??o?.id??N(),c=t.workspaceName??o?.name??(i?`${i} Default`:void 0),d=s===E;if(!i)throw new w("Missing required option: --tunnel-id");if(!n)throw new w("Missing required option: --token");if(!a)throw new w("Missing required option: --workspace-id");if(!c)throw new w("Missing required option: --workspace-name");return{...t,tunnelId:i,token:n,workspace:s,workspaceId:a,workspaceName:c,preserveWorkspaces:!t.workspace&&!!o,createWorkspace:d}}function tt(t,e,r){const i=t.workspaces;if(!Array.isArray(i))return;const n=i.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 o=e??(r?`${r}--default`:void 0),s=(o?n.find(a=>a.id===o):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:o??"default",name:typeof s.name=="string"&&s.name?s.name:`${r??o??"default"} Default`,path:s.path}}function et(t,e){const r=t[e];if(!(!r||typeof r!="object"||Array.isArray(r)))return r}function m(t,e){const r=t?.[e];return typeof r=="string"&&r?r:void 0}export{lt as activateMigratedService,Y as assertNodeVersion,Z as assertSupportedPlatform,ht as completeMigrationHandoff,gt as installClient,X as resolveInstallArgs,Q as resolveWorkspacePath,B as scheduleDarwinMigrationHandoff,J as scheduleMigrationHandoff,mt as shouldAttemptAutomaticMigration,K as validateInstallArgs};
|
package/dist/service.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type CommandResult } from "./exec.js";
|
|
1
|
+
import { runCommand, type CommandResult } from "./exec.js";
|
|
2
2
|
export { launchdLabel, serviceName } from "./identity.js";
|
|
3
3
|
export type ServiceInstallOptions = {
|
|
4
4
|
configPath: string;
|
|
@@ -35,7 +35,14 @@ type LingerDependencies = {
|
|
|
35
35
|
};
|
|
36
36
|
export declare function ensureSystemdUserLinger(user: string, dependencies?: LingerDependencies): Promise<void>;
|
|
37
37
|
export declare function installService(options: ServiceInstallOptions): Promise<ServiceInstallResult>;
|
|
38
|
-
export declare function verifyServiceHealth(platform?: NodeJS.Platform, uid?: number | undefined
|
|
38
|
+
export declare function verifyServiceHealth(platform?: NodeJS.Platform, uid?: number | undefined, dependencies?: {
|
|
39
|
+
stderrPath?: string;
|
|
40
|
+
stderrOffset?: number;
|
|
41
|
+
run?: typeof runCommand;
|
|
42
|
+
wait?: (milliseconds: number) => Promise<unknown>;
|
|
43
|
+
attempts?: number;
|
|
44
|
+
intervalMs?: number;
|
|
45
|
+
}): Promise<void>;
|
|
39
46
|
export declare function linuxCommands(): ServiceCommands;
|
|
40
47
|
export declare function legacyLinuxCommands(): ServiceCommands;
|
|
41
48
|
export declare function darwinCommands(dataDir?: string): ServiceCommands;
|
package/dist/service.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
import{access as
|
|
2
|
-
`)}function
|
|
3
|
-
`)}async function
|
|
1
|
+
import{access as C,chmod as b,mkdir as L,open as D,stat as H,unlink as h,writeFile as x}from"node:fs/promises";import{userInfo as M}from"node:os";import{dirname as k,join as s}from"node:path";import{setTimeout as U}from"node:timers/promises";import{runCommand as n}from"./exec.js";import{legacyCodingAgentPaths as O}from"./paths.js";import{launchdLabel as u,legacyLaunchdLabel as f,legacyServiceName as c,productName as y,serviceName as i}from"./identity.js";import{launchdLabel as ut,serviceName as dt}from"./identity.js";function g(t){return`'${t.replaceAll("'","'\\''")}'`}function j(t){return["[Unit]",`Description=${y}`,"After=network-online.target","","[Service]","Type=simple",`ExecStart=${P(t.serviceCommandPath)}`,"Restart=always","RestartSec=5",`WorkingDirectory=${P(t.workspacePath)}`,`Environment=${A("AGENT_CONFIG_PATH",t.configPath)}`,...t.path?[`Environment=${A("PATH",t.path)}`]:[],"","[Install]","WantedBy=default.target",""].join(`
|
|
2
|
+
`)}function P(t){return t.replaceAll("\\","\\\\").replaceAll(" ","\\x20")}function A(t,e){return`${t}="${e.replaceAll("\\","\\\\").replaceAll('"','\\"')}"`}function d(t){return t.replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',""").replaceAll("'","'")}function I(t){const e=s(t.dataDir,"logs","client-service.out.log"),r=s(t.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>${d(u)}</string>`," <key>ProgramArguments</key>"," <array>",` <string>${d(t.serviceCommandPath)}</string>`," </array>"," <key>WorkingDirectory</key>",` <string>${d(t.workspacePath)}</string>`," <key>EnvironmentVariables</key>"," <dict>"," <key>AGENT_CONFIG_PATH</key>",` <string>${d(t.configPath)}</string>`,...t.path?[" <key>PATH</key>",` <string>${d(t.path)}</string>`]:[]," </dict>"," <key>RunAtLoad</key>"," <true/>"," <key>KeepAlive</key>"," <true/>"," <key>StandardOutPath</key>",` <string>${d(e)}</string>`," <key>StandardErrorPath</key>",` <string>${d(r)}</string>`,"</dict>","</plist>",""].join(`
|
|
3
|
+
`)}async function $(t){const e=(process.env.PATH??"").split(":").filter(Boolean);for(const r of e)try{return await C(s(r,t)),!0}catch{}return!1}const R={commandExists:$,runCommand:n};async function _(t,e=R){if(!await e.commandExists("loginctl"))throw new Error(`loginctl is required to keep the user service running after logout. Enable linger for ${t} and retry installation.`);if(await S(t,e.runCommand))return;if(console.log(`Enabling login persistence for ${t}`),(await e.runCommand("loginctl",["enable-linger",t],{allowFailure:!0,interactive:!0})).status!==0)throw new Error(`Unable to enable login persistence for ${t}. Run 'sudo loginctl enable-linger ${t}' and retry installation.`);if(!await S(t,e.runCommand))throw new Error(`Login persistence is still disabled for ${t}. Run 'sudo loginctl enable-linger ${t}' and retry installation.`)}async function S(t,e){const r=await e("loginctl",["show-user",t,"-p","Linger"],{allowFailure:!0});return r.status===0&&r.stdout.trim()==="Linger=yes"}async function rt(t){const e=t.platform??process.platform;if(e==="linux")return q(t);if(e==="darwin")return B(t);throw new Error("Only Linux and macOS are supported")}async function q(t){if(!await $("systemctl"))throw new Error("systemctl is required to install the user service");const e=t.user??process.env.USER??M().username;await _(e);const r=s(process.env.HOME??"",".config","systemd","user",`${i}.service`);return await L(k(r),{recursive:!0,mode:448}),await x(r,j(t),{mode:384}),await b(r,384),await n("systemctl",["--user","daemon-reload"]),await n("systemctl",["--user","enable","--now",`${i}.service`]),await n("systemctl",["--user","restart",`${i}.service`]),await F("linux"),{servicePath:r,commands:N(),warnings:[]}}async function B(t){if(!await $("launchctl"))throw new Error("launchctl is required to install the LaunchAgent");const e=s(process.env.HOME??"","Library","LaunchAgents",`${u}.plist`);await L(k(e),{recursive:!0,mode:448}),await x(e,I(t),{mode:384}),await b(e,384);const r=t.uid??process.getuid?.();if(r===void 0)throw new Error("Unable to determine current uid for launchctl");const a=`gui/${r}`,o=s(t.dataDir,"logs","client-service.err.log"),l=await G(o);return await n("launchctl",["bootout",a,e],{allowFailure:!0}),await n("launchctl",["bootstrap",a,e]),await F("darwin",t.uid,{stderrPath:o,stderrOffset:l}),{servicePath:e,commands:T(),warnings:[]}}async function F(t=process.platform,e=process.getuid?.(),r={}){if(t==="linux"){if((await n("systemctl",["--user","is-active","--quiet",`${i}.service`],{allowFailure:!0})).status!==0)throw new Error(`${y} systemd service did not become active`);return}if(t==="darwin"){if(e===void 0)throw new Error("Unable to determine current uid for launchctl");const a=r.run??n,o=r.wait??U,l=r.attempts??40,m=r.intervalMs??250;let w={status:1,stdout:"",stderr:""};for(let p=0;p<l;p+=1){if(w=await a("launchctl",["print",`gui/${e}/${u}`],{allowFailure:!0,logCommand:!1}),w.status===0&&w.stdout.includes("state = running"))return;p+1<l&&await o(m)}const v=z(w),E=r.stderrPath?await W(r.stderrPath,r.stderrOffset):void 0;throw new Error([`${y} LaunchAgent did not become active after ${l*m}ms`,v?`launchctl: ${v}`:void 0,E?`service stderr: ${E}`:void 0].filter(Boolean).join("; "))}throw new Error("Only Linux and macOS are supported")}function z(t){const e=t.stdout||t.stderr,r=e.split(`
|
|
4
|
+
`).map(o=>o.trim()).filter(o=>/^(state|last exit code|reason|runs|pid)\s*=/.test(o));return(r.length>0?r.join(", "):e.trim()).slice(-2e3)}async function G(t){try{return(await H(t)).size}catch(e){if(e.code==="ENOENT")return 0;throw e}}async function W(t,e=0){let r;try{r=await D(t,"r");const a=(await r.stat()).size,o=a>=e?e:0;if(a<=o)return;const l=Math.max(o,a-4e3),m=Buffer.alloc(a-l);return await r.read(m,0,m.length,l),m.toString("utf8").trim()||void 0}catch(a){return a.code==="ENOENT"?void 0:`unable to read ${t}: ${String(a)}`}finally{await r?.close()}}function N(){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(){return{status:`systemctl --user status ${c}.service`,logs:`journalctl --user -u ${c}.service -n 100 -f`,start:`systemctl --user start ${c}.service`,restart:`systemctl --user restart ${c}.service`,stop:`systemctl --user stop ${c}.service`,uninstall:`systemctl --user disable --now ${c}.service && rm -f ~/.config/systemd/user/${c}.service && systemctl --user daemon-reload`}}function T(t=s(process.env.HOME??"",".messenger-agent","data")){const e="gui/$(id -u)",r=`~/Library/LaunchAgents/${u}.plist`;return{status:`launchctl print ${e}/${u}`,logs:`tail -f ${g(s(t,"logs","client-service.out.log"))} ${g(s(t,"logs","client-service.err.log"))}`,start:`launchctl bootstrap ${e} ${r}`,restart:`launchctl kickstart -k ${e}/${u} 2>/dev/null || launchctl bootstrap ${e} ${r}`,stop:`launchctl bootout ${e} ${r}`,uninstall:`launchctl bootout ${e} ${r}; rm -f ${r}`}}function V(){const t="gui/$(id -u)",e=`~/Library/LaunchAgents/${f}.plist`,r=O().dataDir;return{status:`launchctl print ${t}/${f}`,logs:`tail -f ${g(s(r,"logs","client-service.out.log"))} ${g(s(r,"logs","client-service.err.log"))}`,start:`launchctl bootstrap ${t} ${e}`,restart:`launchctl kickstart -k ${t}/${f} 2>/dev/null || launchctl bootstrap ${t} ${e}`,stop:`launchctl bootout ${t} ${e}`,uninstall:`launchctl bootout ${t} ${e}; rm -f ${e}`}}function at(t){const e=t===O().configPath;return process.platform==="darwin"?e?V():T():e?K():N()}async function nt(t=process.platform){if(t==="linux"){await n("systemctl",["--user","disable","--now",`${c}.service`],{allowFailure:!0}),await h(s(process.env.HOME??"",".config","systemd","user",`${c}.service`)).catch(e=>{if(e.code!=="ENOENT")throw e}),await n("systemctl",["--user","daemon-reload"],{allowFailure:!0});return}if(t==="darwin"){const e=process.getuid?.();if(e===void 0)throw new Error("Unable to determine current uid for launchctl");const r=s(process.env.HOME??"","Library","LaunchAgents",`${f}.plist`);await n("launchctl",["bootout",`gui/${e}`,r],{allowFailure:!0}),await h(r).catch(a=>{if(a.code!=="ENOENT")throw a});return}throw new Error("Only Linux and macOS are supported")}async function st(t=process.platform){if(t==="linux"){await n("systemctl",["--user","stop",`${c}.service`],{allowFailure:!0});return}if(t==="darwin"){const e=process.getuid?.();if(e===void 0)throw new Error("Unable to determine current uid for launchctl");const r=s(process.env.HOME??"","Library","LaunchAgents",`${f}.plist`);await n("launchctl",["bootout",`gui/${e}`,r],{allowFailure:!0});return}throw new Error("Only Linux and macOS are supported")}async function it(t=process.platform){if(t==="linux"){await n("systemctl",["--user","start",`${c}.service`],{allowFailure:!0});return}if(t==="darwin"){const e=process.getuid?.();if(e===void 0)throw new Error("Unable to determine current uid for launchctl");const r=s(process.env.HOME??"","Library","LaunchAgents",`${f}.plist`);await n("launchctl",["bootstrap",`gui/${e}`,r],{allowFailure:!0});return}throw new Error("Only Linux and macOS are supported")}async function ot(t=process.platform){if(t==="linux"){await n("systemctl",["--user","disable","--now",`${i}.service`],{allowFailure:!0}),await h(s(process.env.HOME??"",".config","systemd","user",`${i}.service`)).catch(e=>{if(e.code!=="ENOENT")throw e}),await n("systemctl",["--user","daemon-reload"],{allowFailure:!0});return}if(t==="darwin"){const e=process.getuid?.();if(e===void 0)throw new Error("Unable to determine current uid for launchctl");const r=s(process.env.HOME??"","Library","LaunchAgents",`${u}.plist`);await n("launchctl",["bootout",`gui/${e}`,r],{allowFailure:!0}),await h(r).catch(a=>{if(a.code!=="ENOENT")throw a});return}throw new Error("Only Linux and macOS are supported")}export{I as createLaunchdPlist,j as createSystemdService,T as darwinCommands,_ as ensureSystemdUserLinger,rt as installService,ut as launchdLabel,V as legacyDarwinCommands,K as legacyLinuxCommands,N as linuxCommands,at as serviceCommandsForConfig,dt as serviceName,g as shellEscape,it as startLegacyService,st as stopLegacyService,nt as uninstallLegacyService,ot as uninstallService,F as verifyServiceHealth};
|
package/dist/supervisor.d.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
|
+
import { readClientPath } from "./config-file.js";
|
|
3
|
+
import { currentPackageVersion, installRuntime, rollbackRuntime } from "./runtime.js";
|
|
4
|
+
import { migrateLegacyClient } from "./migration.js";
|
|
5
|
+
import { scheduleMigrationHandoff, shouldAttemptAutomaticMigration } from "./install.js";
|
|
2
6
|
import { type AgentActivitySnapshot, type ManagedAgentName } from "@messenger-agent/shared/agent-activity";
|
|
3
7
|
export type { ManagedAgentName } from "@messenger-agent/shared/agent-activity";
|
|
4
8
|
export type AgentActivityStatus = ({
|
|
@@ -43,3 +47,12 @@ export declare class AgentSupervisor {
|
|
|
43
47
|
private startAgent;
|
|
44
48
|
}
|
|
45
49
|
export declare function runSupervisor(configPath: string): Promise<void>;
|
|
50
|
+
export declare function migrateLegacyServiceAtStartup(configPath: string, dependencies?: {
|
|
51
|
+
currentVersion?: typeof currentPackageVersion;
|
|
52
|
+
shouldAttempt?: typeof shouldAttemptAutomaticMigration;
|
|
53
|
+
migrate?: typeof migrateLegacyClient;
|
|
54
|
+
install?: typeof installRuntime;
|
|
55
|
+
handoff?: typeof scheduleMigrationHandoff;
|
|
56
|
+
rollback?: typeof rollbackRuntime;
|
|
57
|
+
readPath?: typeof readClientPath;
|
|
58
|
+
}): Promise<boolean>;
|
package/dist/supervisor.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{spawn as
|
|
1
|
+
import{spawn as x}from"node:child_process";import{randomUUID as b}from"node:crypto";import{createRequire as M}from"node:module";import{dirname as h,join as f,resolve as w}from"node:path";import{parse as k}from"yaml";import{readFile as C,unlink as y}from"node:fs/promises";import{readClientPath as v,readUpgradeChannel as E,writeClientPath as I}from"./config-file.js";import{readControlSocketPath as $,startControlServer as L}from"./control.js";import{MaintenanceScheduler as N}from"./maintenance.js";import{currentBundledSkillsDir as F,currentPackageVersion as O,defaultAgentHomes as R,installRuntime as A,rollbackRuntime as P,syncBundledSkills as D}from"./runtime.js";import{launchdLabel as G,serviceCommandsForConfig as _,serviceName as j}from"./service.js";import{legacyLaunchdLabel as B,legacyServiceName as q}from"./identity.js";import{legacyCodingAgentPaths as S}from"./paths.js";import{runCommand as u}from"./exec.js";import{AutoUpgradeScheduler as U}from"./auto-upgrade.js";import{migrateLegacyClient as H}from"./migration.js";import{scheduleMigrationHandoff as K,shouldAttemptAutomaticMigration as Y}from"./install.js";import{isAgentActivityResponse as V}from"@messenger-agent/shared/agent-activity";const g=M(import.meta.url);function W(){return[{name:"codex",entry:g.resolve("@messenger-agent/codex-agent")},{name:"claude",entry:g.resolve("@messenger-agent/claude-agent")},{name:"workspace",entry:g.resolve("@messenger-agent/messenger-agent")}]}function X(a,t){let e=a;for(const r of t){if(!e||typeof e!="object"||Array.isArray(e))return;e=e[r]}return e}async function z(a){const t=k(await C(a,"utf8")),e=X(t,["workspaces"]);if(Array.isArray(e)){const r=e.find(s=>!!s&&typeof s=="object"&&!Array.isArray(s)&&typeof s.path=="string");if(typeof r?.path=="string"&&r.path.trim())return r.path}return h(a)}class J{options;stopping=!1;running;restartBaseMs;restartMaxMs;constructor(t){this.options=t,this.running=(t.agents??W()).map(e=>({definition:e,restartAttempts:0})),this.restartBaseMs=t.restartBaseMs??1e3,this.restartMaxMs=t.restartMaxMs??3e4}start(){for(const t of this.running)this.startAgent(t)}async stop(){this.stopping=!0;for(const t of this.running)t.restartTimer&&clearTimeout(t.restartTimer),t.process?.kill("SIGTERM");await Promise.all(this.running.map(t=>t.process).filter(t=>!!t&&t.exitCode===null&&!t.killed).map(t=>new Promise(e=>{const r=setTimeout(()=>{t.kill("SIGKILL"),e()},8e3);t.once("exit",()=>{clearTimeout(r),e()})})))}restartAgent(t){const e=this.running.find(({definition:r})=>r.name===t);return e?(e.restartPromise||(e.restartPromise=this.restartAgentProcess(e).finally(()=>{e.restartPromise=void 0})),e.restartPromise):Promise.reject(new Error(`Agent is not managed by this client: ${t}`))}async activityStatus(t=1e3){const[e,r]=await Promise.all([this.agentActivityStatus("codex",t),this.agentActivityStatus("claude",t)]),s=[e,r].filter(i=>i.available);return{active:s.reduce((i,n)=>i+n.active,0),waiting:s.reduce((i,n)=>i+n.waiting,0),agents:{codex:e,claude:r}}}agentActivityStatus(t,e){const r=this.running.find(({definition:s})=>s.name===t)?.process;return!r||!r.connected||!r.send?Promise.resolve({available:!1,error:`${t} agent is not connected`}):new Promise(s=>{const i=b(),n={type:"client.activity.request",requestId:i};let c=!1;const l=o=>{c||(c=!0,clearTimeout(T),r.off("message",m),s(o))},m=o=>{!V(o)||o.requestId!==i||o.agent!==t||l({available:!0,...o.snapshot})},T=setTimeout(()=>l({available:!1,error:`${t} agent did not respond`}),e);r.on("message",m),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 et(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??x)(process.execPath,[t.definition.entry],{cwd:this.options.workspacePath,env:e,stdio:["inherit","inherit","inherit","ipc"]});t.process=r,t.definition.name==="workspace"&&r.on("message",s=>{s&&typeof s=="object"&&"type"in s&&s.type==="client.workspaces.changed"&&Promise.all([this.restartAgent("codex"),this.restartAgent("claude")]).catch(i=>{console.error("[client] failed to reload agents after workspace configuration changed",i)})}),console.log(`[client] started ${t.definition.name}-agent pid=${r.pid}`),r.once("exit",(s,i)=>{if(t.process=void 0,this.stopping||t.restartPromise)return;const n=Math.min(this.restartBaseMs*2**t.restartAttempts,this.restartMaxMs);t.restartAttempts+=1,console.error(`[client] ${t.definition.name}-agent exited with code=${s??"null"} signal=${i??"null"}; restarting in ${n}ms`),t.restartTimer=setTimeout(()=>{t.restartTimer=void 0,this.startAgent(t)},n)})}}async function At(a){if(await Q(a))return;await D(F(),R());const t=await z(a),e=new J({configPath:a,workspacePath:t}),r=await $(a),s=new N({statePath:f(h(r),"maintenance.json"),getActivity:()=>e.activityStatus(),execute:(l,m)=>Z(l,a,e,m)});await s.start();const i=new U({statePath:f(h(r),"auto-upgrade.json"),maintenance:s,getChannel:()=>E(a)});await i.start();const n=await L(r,{restartAgent:l=>e.restartAgent(l),getActivity:()=>e.activityStatus(),maintenance:s});e.start();const c=async()=>{i.stop(),s.stop(),await n.close(),await e.stop(),process.exit(0)};process.once("SIGINT",()=>{c()}),process.once("SIGTERM",()=>{c()})}async function Q(a,t={}){const e=S();if(w(a)!==w(e.configPath))return!1;const r=await(t.currentVersion??O)();if(!await(t.shouldAttempt??Y)(e.rootDir,r))return!1;let s;try{const i=await(t.migrate??H)();if(!i.migrated)return!1;const n=await(t.readPath??v)(i.paths.configPath);return s=await(t.install??A)({version:r,configPath:i.paths.configPath,runtimeDir:i.paths.runtimeDir,binDir:i.paths.binDir,path:n}),await(t.handoff??K)(i,s,i.paths.configPath,n),!0}catch(i){return s&&await(t.rollback??P)(s).catch(()=>{}),console.error(`[client] automatic legacy migration failed: ${i instanceof Error?i.message:String(i)}`),!1}}async function Z(a,t,e,r){if(a.type==="upgrade"){a.path&&await I(t,a.path);const s=await A({version:a.version,configPath:t,path:a.path??await v(t)});await r("restarting");try{await d(p(t).restart)}catch(i){throw await P(s),await d(p(t).restart).catch(()=>{}),i}return}if(a.type==="restart"&&a.agent){await e.restartAgent(a.agent);return}if(a.type==="restart"){await r("restarting"),await d(p(t).restart);return}if(a.type==="stop"){await r("stopping"),await d(p(t).stop);return}await r("stopping"),await tt(t)}function p(a){return _(a)}async function d(a){const t=await u("/bin/sh",["-lc",a],{allowFailure:!0});if(t.status!==0)throw new Error(t.stderr||t.stdout||`Service command failed: ${a}`)}async function tt(a){const t=process.env.HOME??"",e=a===S().configPath,r=e?q:j,s=e?B:G;if(process.platform==="linux"){await u("systemctl",["--user","disable",`${r}.service`],{allowFailure:!0}),await y(f(t,".config","systemd","user",`${r}.service`)).catch(i=>{if(i.code!=="ENOENT")throw i}),await u("systemctl",["--user","daemon-reload"],{allowFailure:!0}),await u("systemctl",["--user","stop",`${r}.service`],{allowFailure:!0});return}if(process.platform==="darwin"){const i=process.getuid?.();if(i===void 0)throw new Error("Unable to determine current uid for launchctl");const n=f(t,"Library","LaunchAgents",`${s}.plist`);await y(n).catch(c=>{if(c.code!=="ENOENT")throw c}),await u("launchctl",["bootout",`gui/${i}/${s}`],{allowFailure:!0});return}throw new Error("Only Linux and macOS are supported")}function et(a){return new Promise(t=>{const e=setTimeout(()=>{a.kill("SIGKILL")},8e3);a.once("exit",()=>{clearTimeout(e),t()})})}export{J as AgentSupervisor,Q as migrateLegacyServiceAtStartup,z as readWorkspacePathFromConfig,W as resolveAgentEntries,At as runSupervisor};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@messenger-agent/client",
|
|
3
|
-
"version": "0.33.0-alpha.
|
|
3
|
+
"version": "0.33.0-alpha.8",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -21,10 +21,10 @@
|
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"cac": "^7.0.0",
|
|
23
23
|
"yaml": "^2.9.0",
|
|
24
|
-
"@messenger-agent/
|
|
25
|
-
"@messenger-agent/
|
|
26
|
-
"@messenger-agent/
|
|
27
|
-
"@messenger-agent/
|
|
24
|
+
"@messenger-agent/claude-agent": "0.33.0-alpha.8",
|
|
25
|
+
"@messenger-agent/shared": "0.33.0-alpha.8",
|
|
26
|
+
"@messenger-agent/codex-agent": "0.33.0-alpha.8",
|
|
27
|
+
"@messenger-agent/messenger-agent": "0.33.0-alpha.8"
|
|
28
28
|
},
|
|
29
29
|
"scripts": {
|
|
30
30
|
"build": "rm -rf dist && tsc -p tsconfig.json && node ../../scripts/copy-client-assets.mjs && chmod +x dist/index.js",
|