grix-connector 2.2.8 → 2.2.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- import{existsSync as m,mkdirSync as P,readFileSync as U,renameSync as I,writeFileSync as $}from"node:fs";import{join as _,dirname as E}from"node:path";import{spawn as M}from"node:child_process";import{log as o}from"../log/index.js";import{GRIX_PATHS as y}from"../log/index.js";import{resolveClientVersion as g}from"../util/client-version.js";import{UpgradeError as D,collectEnvInfo as L,getUpgradeLogTail as O,npmInstall as N,pendingExists as v,preflightCheck as R,readPending as F,removePending as f,upgradeLog as n,verifyInstalledVersion as x,writePending as B}from"./npm-upgrader.js";const G=360*60*1e3,V=300*1e3,w=1800*1e3,j=2,H=3,K="grix-connector",S=1e4;function k(c){return c.replace(/^wss:/,"https:").replace(/^ws:/,"http:")}function T(){return _(y.data,"upgrade-state.json")}function A(){const c=T();if(!m(c))return{daily_attempts:{},version_attempts:{}};try{return JSON.parse(U(c,"utf-8"))}catch{return{daily_attempts:{},version_attempts:{}}}}function q(c){const t=T();P(E(t),{recursive:!0});const e=t+".tmp";$(e,JSON.stringify(c),"utf-8"),I(e,t)}function C(){return new Date().toISOString().slice(0,10)}class tt{agentConfigs;isBusy;timer=null;initialTimer=null;running=!1;stopped=!1;constructor(t,e){this.agentConfigs=t,this.isBusy=e}async start(){await this.handlePendingOnStartup(),this.initialTimer=setTimeout(()=>{this.stopped||(this.runCheck(),!this.stopped&&(this.timer=setInterval(()=>this.runCheck(),G)))},V)}stop(){this.stopped=!0,this.initialTimer&&(clearTimeout(this.initialTimer),this.initialTimer=null),this.timer&&(clearInterval(this.timer),this.timer=null)}triggerCheck(){this.stopped||this.runCheck()}async checkForUpdate(){return this.agentConfigs.length===0?{available:!1}:(await Promise.all(this.agentConfigs.map(a=>this.queryUpgrade(a)))).find(a=>a.available&&a.release)??{available:!1}}async handlePendingOnStartup(){if(!v())return;const t=F();if(!t){f();return}const e=g();e===t.target_version?(n(`daemon startup: version matches target ${t.target_version}, upgrade succeeded`),await this.reportUpgrade({from_version:t.from_version,to_version:t.target_version,status:"success"})):(n(`daemon startup: version ${e} != target ${t.target_version}, rolled back`),await this.reportUpgrade({from_version:t.from_version,to_version:t.target_version,status:"rolled_back",error_code:"STARTUP_CRASH",crash_count:t.crash_count})),f()}async runCheck(){if(!this.running){this.running=!0;try{await this.check()}catch(t){o.error("upgrade",`Check failed: ${t instanceof Error?t.message:t}`)}finally{this.running=!1}}}async check(){if(v()||this.agentConfigs.length===0)return;const a=(await Promise.all(this.agentConfigs.map(s=>this.queryUpgrade(s)))).find(s=>s.available&&s.release)?.release;if(!a)return;const r=a.version,i=g();if(!a.force&&!this.checkRateLimit(r))return;const d=R();if(!d.ok){await this.reportUpgrade({from_version:i,to_version:r,status:"failed",error_code:d.errorCode,error_msg:d.errorMsg});return}n(`upgrade start: ${i} -> ${r}`);const h=Date.now();try{if(B(i,r),await N(K,r),x(r),this.startGuardian(),n("npm install verified, reporting installed"),await this.reportUpgrade({from_version:i,to_version:r,status:"installed",duration_ms:Date.now()-h}),n("shutting down for restart"),this.isBusy?.()){n("active tasks detected, waiting for completion before restart");const s=3600*1e3,l=5e3,p=Date.now()+s;for(;this.isBusy()&&Date.now()<p&&!this.stopped;)await new Promise(b=>setTimeout(b,l));if(this.stopped){n("upgrade aborted: checker stopped during wait");return}this.isBusy()?n("active tasks still running after 1h max wait, forcing restart"):n("all tasks completed, proceeding with restart")}process.kill(process.pid,"SIGTERM")}catch(s){const l=s instanceof D?s.code:"NPM_INSTALL_FAILED",p=s instanceof Error?s.message:String(s);n(`upgrade failed: ${l} ${p}`),f(),this.recordFailure(r),await this.reportUpgrade({from_version:i,to_version:r,status:"failed",error_code:l,error_msg:p,duration_ms:Date.now()-h,upgrade_log:O()})}}checkRateLimit(t){const e=A();if(e.last_failure_at){const u=Date.now()-new Date(e.last_failure_at).getTime();if(u<w)return o.info("upgrade",`In cooldown, ${(w-u)/6e4}m remaining`),!1}const a=e.version_attempts[t]??0;if(a>=H)return o.info("upgrade",`Version ${t} already tried ${a} times, skipping`),!1;const r=C(),i=e.daily_attempts[r]??0;return i>=j?(o.info("upgrade",`Already ${i} attempts today, skipping`),!1):!0}recordFailure(t){const e=A(),a=C();e.last_failure_at=new Date().toISOString(),e.last_failure_version=t,e.daily_attempts[a]=(e.daily_attempts[a]??0)+1,e.version_attempts[t]=(e.version_attempts[t]??0)+1,q(e)}async queryUpgrade(t){const a=`${k(t.wsUrl)}/v1/agent-api/upgrade/check?`+new URLSearchParams({client_type:"grix-connector",client_version:g(),channel:t.channel??"stable",platform:process.platform,arch:process.arch}).toString();try{const r=await fetch(a,{headers:{Authorization:`Bearer ${t.apiKey}`},signal:AbortSignal.timeout(S)});if(!r.ok)return o.warn("upgrade",`Check API returned ${r.status}`),{available:!1};const i=await r.json();return i.code!==0?{available:!1}:i.data}catch(r){return o.warn("upgrade",`Check API error: ${r instanceof Error?r.message:r}`),{available:!1}}}async reportUpgrade(t){if(this.agentConfigs.length===0)return;const e=t.npm_version?t:{...t,...L()};await Promise.all(this.agentConfigs.map(async a=>{const i=`${k(a.wsUrl)}/v1/agent-api/upgrade/report`;try{await fetch(i,{method:"POST",headers:{Authorization:`Bearer ${a.apiKey}`,"Content-Type":"application/json"},body:JSON.stringify(e),signal:AbortSignal.timeout(S)})}catch{}}))}startGuardian(){const t=_(y.base,"bin","upgrade-guardian.sh");if(process.platform==="win32"||!m(t)){o.info("upgrade","Guardian not available on this platform, skipping");return}try{const e=M(t,[],{detached:!0,stdio:"ignore"});e.unref(),n(`guardian started (pid ${e.pid})`)}catch(e){o.warn("upgrade",`Failed to start guardian: ${e instanceof Error?e.message:e}`)}}}export{tt as UpgradeChecker};
1
+ import{existsSync as m,mkdirSync as P,readFileSync as U,renameSync as I,writeFileSync as $}from"node:fs";import{join as _,dirname as E}from"node:path";import{spawn as M}from"node:child_process";import{log as o}from"../log/index.js";import{GRIX_PATHS as y}from"../log/index.js";import{resolveClientVersion as g}from"../util/client-version.js";import{UpgradeError as D,collectEnvInfo as L,getUpgradeLogTail as O,npmInstall as R,pendingExists as v,preflightCheck as N,readPending as x,removePending as f,upgradeLog as n,verifyInstalledVersion as F,writePending as B}from"./npm-upgrader.js";const G=360*60*1e3,V=300*1e3,w=1800*1e3,j=2,H=3,K="grix-connector",S=1e4;function k(c){return new URL(c.replace(/^wss:/,"https:").replace(/^ws:/,"http:")).origin}function T(){return _(y.data,"upgrade-state.json")}function A(){const c=T();if(!m(c))return{daily_attempts:{},version_attempts:{}};try{return JSON.parse(U(c,"utf-8"))}catch{return{daily_attempts:{},version_attempts:{}}}}function q(c){const t=T();P(E(t),{recursive:!0});const e=t+".tmp";$(e,JSON.stringify(c),"utf-8"),I(e,t)}function C(){return new Date().toISOString().slice(0,10)}class tt{agentConfigs;isBusy;timer=null;initialTimer=null;running=!1;stopped=!1;constructor(t,e){this.agentConfigs=t,this.isBusy=e}async start(){await this.handlePendingOnStartup(),this.initialTimer=setTimeout(()=>{this.stopped||(this.runCheck(),!this.stopped&&(this.timer=setInterval(()=>this.runCheck(),G)))},V)}stop(){this.stopped=!0,this.initialTimer&&(clearTimeout(this.initialTimer),this.initialTimer=null),this.timer&&(clearInterval(this.timer),this.timer=null)}triggerCheck(){this.stopped||this.runCheck()}async checkForUpdate(){return this.agentConfigs.length===0?{available:!1}:(await Promise.all(this.agentConfigs.map(a=>this.queryUpgrade(a)))).find(a=>a.available&&a.release)??{available:!1}}async handlePendingOnStartup(){if(!v())return;const t=x();if(!t){f();return}const e=g();e===t.target_version?(n(`daemon startup: version matches target ${t.target_version}, upgrade succeeded`),await this.reportUpgrade({from_version:t.from_version,to_version:t.target_version,status:"success"})):(n(`daemon startup: version ${e} != target ${t.target_version}, rolled back`),await this.reportUpgrade({from_version:t.from_version,to_version:t.target_version,status:"rolled_back",error_code:"STARTUP_CRASH",crash_count:t.crash_count})),f()}async runCheck(){if(!this.running){this.running=!0;try{await this.check()}catch(t){o.error("upgrade",`Check failed: ${t instanceof Error?t.message:t}`)}finally{this.running=!1}}}async check(){if(v()||this.agentConfigs.length===0)return;const a=(await Promise.all(this.agentConfigs.map(s=>this.queryUpgrade(s)))).find(s=>s.available&&s.release)?.release;if(!a)return;const r=a.version,i=g();if(!a.force&&!this.checkRateLimit(r))return;const d=N();if(!d.ok){await this.reportUpgrade({from_version:i,to_version:r,status:"failed",error_code:d.errorCode,error_msg:d.errorMsg});return}n(`upgrade start: ${i} -> ${r}`);const h=Date.now();try{if(B(i,r),await R(K,r),F(r),this.startGuardian(),n("npm install verified, reporting installed"),await this.reportUpgrade({from_version:i,to_version:r,status:"installed",duration_ms:Date.now()-h}),n("shutting down for restart"),this.isBusy?.()){n("active tasks detected, waiting for completion before restart");const s=3600*1e3,l=5e3,u=Date.now()+s;for(;this.isBusy()&&Date.now()<u&&!this.stopped;)await new Promise(b=>setTimeout(b,l));if(this.stopped){n("upgrade aborted: checker stopped during wait");return}this.isBusy()?n("active tasks still running after 1h max wait, forcing restart"):n("all tasks completed, proceeding with restart")}process.kill(process.pid,"SIGTERM")}catch(s){const l=s instanceof D?s.code:"NPM_INSTALL_FAILED",u=s instanceof Error?s.message:String(s);n(`upgrade failed: ${l} ${u}`),f(),this.recordFailure(r),await this.reportUpgrade({from_version:i,to_version:r,status:"failed",error_code:l,error_msg:u,duration_ms:Date.now()-h,upgrade_log:O()})}}checkRateLimit(t){const e=A();if(e.last_failure_at){const p=Date.now()-new Date(e.last_failure_at).getTime();if(p<w)return o.info("upgrade",`In cooldown, ${(w-p)/6e4}m remaining`),!1}const a=e.version_attempts[t]??0;if(a>=H)return o.info("upgrade",`Version ${t} already tried ${a} times, skipping`),!1;const r=C(),i=e.daily_attempts[r]??0;return i>=j?(o.info("upgrade",`Already ${i} attempts today, skipping`),!1):!0}recordFailure(t){const e=A(),a=C();e.last_failure_at=new Date().toISOString(),e.last_failure_version=t,e.daily_attempts[a]=(e.daily_attempts[a]??0)+1,e.version_attempts[t]=(e.version_attempts[t]??0)+1,q(e)}async queryUpgrade(t){const a=`${k(t.wsUrl)}/v1/agent-api/upgrade/check?`+new URLSearchParams({client_type:"grix-connector",client_version:g(),channel:t.channel??"stable",platform:process.platform,arch:process.arch}).toString();try{const r=await fetch(a,{headers:{Authorization:`Bearer ${t.apiKey}`},signal:AbortSignal.timeout(S)});if(!r.ok)return o.warn("upgrade",`Check API returned ${r.status}`),{available:!1};const i=await r.json();return i.code!==0?{available:!1}:i.data}catch(r){return o.warn("upgrade",`Check API error: ${r instanceof Error?r.message:r}`),{available:!1}}}async reportUpgrade(t){if(this.agentConfigs.length===0)return;const e={client_type:"grix-connector",...t.npm_version?t:{...t,...L()}};await Promise.all(this.agentConfigs.map(async a=>{const i=`${k(a.wsUrl)}/v1/agent-api/upgrade/report`;try{await fetch(i,{method:"POST",headers:{Authorization:`Bearer ${a.apiKey}`,"Content-Type":"application/json"},body:JSON.stringify(e),signal:AbortSignal.timeout(S)})}catch{}}))}startGuardian(){const t=_(y.base,"bin","upgrade-guardian.sh");if(process.platform==="win32"||!m(t)){o.info("upgrade","Guardian not available on this platform, skipping");return}try{const e=M(t,[],{detached:!0,stdio:"ignore"});e.unref(),n(`guardian started (pid ${e.pid})`)}catch(e){o.warn("upgrade",`Failed to start guardian: ${e instanceof Error?e.message:e}`)}}}export{tt as UpgradeChecker};
package/dist/manager.js CHANGED
@@ -1,2 +1,2 @@
1
- import{readFileSync as Q,readdirSync as F,writeFileSync as B}from"node:fs";import{join as x}from"node:path";import{AgentInstance as A}from"./bridge/bridge.js";import{buildSharedInstanceConfig as L,diffSharedOwners as R,sharedInstanceKey as z,SharedOwnersCache as G}from"./manager-share-config.js";import{GRIX_PATHS as S,log as d}from"./core/log/index.js";import{resolveClientVersion as K}from"./core/util/client-version.js";import{UpgradeChecker as W}from"./core/upgrade/upgrade-checker.js";import{AgentGlobalConfigStore as V}from"./core/persistence/agent-global-config-store.js";import{scanSkills as J,dedupeSkills as X}from"./adapter/claude/skill-scanner.js";import{scanDefaultSkills as Y,logDefaultSkillsCheck as Z,cleanupProjectedSkills as D}from"./default-skills/index.js";import{resolveCopilotCommand as q}from"./core/runtime/copilot-resolve.js";import{getCliVersion as ee,resolveCliPath as te}from"./core/util/cli-probe.js";import{AgentInstaller as ne}from"./core/installer/installer.js";import{reportInstallFailure as ae}from"./core/observability/sentry.js";const se=8e3;function oe(){const n=q();return[{clientType:"openclaw",command:"openclaw"},{clientType:"claude",command:"claude"},{clientType:"codex",command:"codex"},{clientType:"gemini",command:"gemini"},{clientType:"qwen",command:"qwen"},{clientType:"hermes",command:"hermes"},{clientType:"reasonix",command:"reasonix"},{clientType:"codewhale",command:"codewhale"},{clientType:"opencode",command:"opencode"},{clientType:"cursor",command:"agent"},{clientType:"pi",command:"pi"},{clientType:"openhuman",command:"openhuman-core"},{clientType:"kiro",command:"kiro-cli"},{clientType:"copilot",command:n.command},{clientType:"agy",command:"agy"}]}async function re(){return Promise.all(oe().map(async n=>{const e=await te(n.command);if(!e)return{client_type:n.clientType,command:n.command,installed:!1,path:null,version:null};const t=await ee(e,n.versionArgs??["--version"]);return{client_type:n.clientType,command:n.command,installed:!0,path:e,version:t.version,error:t.error}}))}function ie(n){switch(n){case"claude":return{adapterType:"claude",command:"claude"};case"codex":return{adapterType:"codex",command:"codex",options:{sandboxMode:"danger-full-access"}};case"gemini":return{adapterType:"acp",command:"gemini",autoInjectArgs:{acp:!0},enableSessionBinding:!0};case"qwen":return{adapterType:"acp",command:"qwen",adapterHint:"qwen/base",autoInjectArgs:{acp:!0},enableSessionBinding:!0};case"pi":return{adapterType:"pi",command:"pi"};case"cursor":return{adapterType:"cursor",command:"agent"};case"reasonix":return{adapterType:"acp",command:"reasonix",args:["acp"],enableSessionBinding:!0};case"codewhale":return{adapterType:"codewhale",command:"codewhale",enableSessionBinding:!0};case"openhuman":return{adapterType:"openhuman",command:"openhuman-core",enableSessionBinding:!0};case"kiro":return{adapterType:"acp",command:"kiro-cli",args:["acp"],enableSessionBinding:!0};case"opencode":return{adapterType:"opencode",command:"opencode",args:["serve"],enableSessionBinding:!0};case"copilot":{const e=q();return{adapterType:"acp",command:e.command,args:[...e.prefixArgs,"--acp"],enableSessionBinding:!0}}case"agy":return{adapterType:"agy",command:"agy",enableSessionBinding:!0};case"hermes":throw new Error('client_type "hermes" is not handled by grix-connector. Hermes runs as a separate project \u2014 see https://github.com/askie/grix-hermes-python');default:throw new Error(`Unsupported client_type: ${n}`)}}function ce(n){const e=String(n??"").trim().toLowerCase().replace(/[^a-z0-9._-]+/g,"-").replace(/-+/g,"-").replace(/^-|-$/g,"")||"default";return x(S.data,`session-bindings-${e}.json`)}function le(n){const e=String(n??"").trim().toLowerCase().replace(/[^a-z0-9._-]+/g,"-").replace(/-+/g,"-").replace(/^-|-$/g,"")||"default";return x(S.data,`active-events-${e}.json`)}function de(...n){const e=[],t=new Set;for(const s of n)for(const a of s??[]){const o=String(a??"").trim(),r=o.toLowerCase();!o||t.has(r)||(t.add(r),e.push(o))}return e.length>0?e:void 0}function ue(n,e){const t={claude:{maxConcurrent:1,maxQueued:5,queueTimeoutMs:0},codex:{maxConcurrent:1,maxQueued:5,queueTimeoutMs:0},cursor:{maxConcurrent:1,maxQueued:3,queueTimeoutMs:0},acp:{maxConcurrent:1,maxQueued:3,queueTimeoutMs:0},pi:{maxConcurrent:1,maxQueued:5,queueTimeoutMs:0},codewhale:{maxConcurrent:1,maxQueued:3,queueTimeoutMs:0},openhuman:{maxConcurrent:1,maxQueued:3,queueTimeoutMs:0},opencode:{maxConcurrent:1,maxQueued:3,queueTimeoutMs:0},agy:{maxConcurrent:1,maxQueued:3,queueTimeoutMs:0}},s=t[n]??t.acp;return{maxConcurrent:e?.max_concurrent??s.maxConcurrent??1,maxQueued:e?.max_queued??s.maxQueued??3,queueTimeoutMs:e?.queue_timeout_ms??s.queueTimeoutMs??0,cancelableQueued:!0,cancelableRunning:!0}}function O(n){const e=K(),t=String(n.client_type??"").trim().toLowerCase(),s=ie(t),a=String(n.ws_url??"").trim(),o="get_session_usage",r="get_rate_limits",i="get_agent_global_config";if(!n.name?.trim())throw new Error("agent name is required");if(!a)throw new Error(`agent ${n.name}: ws_url is required`);if(!n.agent_id?.trim())throw new Error(`agent ${n.name}: agent_id is required`);if(!n.api_key?.trim())throw new Error(`agent ${n.name}: api_key is required`);const l=s.adapterType,u=l==="acp",f=t==="qwen",m={...s.options??{}},c=l==="codex"?{capabilities:["local_action_v1","agent_invoke"],localActions:["session_control","get_context","set_model","set_mode","set_reasoning_effort","set_sandbox_mode","exec_approve","exec_reject","file_list","create_folder","turn_interrupt","permission_approve","permission_reject","thread_compact",o,r]}:null,p=l==="claude"?{localActions:["session_control","set_mode","set_model","claude_interaction_reply","exec_approve","exec_reject","file_list","create_folder","thread_compact",o,r]}:null,g=f?{capabilities:["stream_chunk","local_action_v1"],localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","file_list","create_folder",o],adapterHint:"qwen/base"}:null,_=l==="pi"?{adapterHint:"pi/base",capabilities:["local_action_v1"],localActions:["session_control","set_model","get_context","file_list","create_folder",o]}:null,h=l==="openhuman"?{adapterHint:"openhuman/base",capabilities:["local_action_v1"],localActions:["session_control","set_model","file_list","create_folder",o]}:null,w=l==="cursor"?{adapterHint:"cursor/base",capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","set_mode","get_context","file_list","create_folder",o,r]}:null,y=l==="codewhale"?{capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","file_list","create_folder",o]}:null,k=l==="opencode"?{adapterHint:"opencode/base",capabilities:["stream_chunk","local_action_v1"],localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","file_list","create_folder",o]}:null,T=l==="agy"?{adapterHint:"agy/base",capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","file_list","create_folder",o]}:null,P=u&&!f?{localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","file_list","create_folder",o]}:null,E=t==="kiro"?{localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","file_list","create_folder","thread_compact",o,r]}:null,H=l==="codex"||l==="claude"||t==="gemini"?["session_control","set_model","set_mode"]:void 0,N=[o,r];u&&m.raw_transport===void 0&&(m.raw_transport=t==="gemini");const U=`${t}/base`,$=l==="claude"?"claude":l==="codex"?"codex":l==="pi"?"pi":t==="kiro"?"kiro":"gemini";let b;try{const M=$==="kiro"?void 0:process.cwd();b=J({mode:$,projectDir:M})??void 0,b&&b.length===0&&(b=void 0)}catch{}const I=Y();if(I.length>0){const C=X([...b??[],...I]),j=C.filter(v=>v.source==="connector").map(v=>v.name);j.length>0&&d.info("manager",`[${n.name}] injecting connector skills: [${j.join(", ")}]`),b=C.length>0?C:void 0}return{name:n.name,adapterType:l,aibot:{url:a,agentId:n.agent_id,apiKey:n.api_key,clientType:t,clientVersion:e,adapterHint:s.adapterHint??g?.adapterHint??_?.adapterHint??h?.adapterHint??w?.adapterHint??k?.adapterHint??T?.adapterHint??U,capabilities:c?.capabilities??y?.capabilities??_?.capabilities??h?.capabilities??w?.capabilities??k?.capabilities??T?.capabilities??g?.capabilities??["stream_chunk","local_action_v1","connector_upgrade"],localActions:de(c?.localActions??y?.localActions??p?.localActions??_?.localActions??h?.localActions??w?.localActions??k?.localActions??T?.localActions??g?.localActions??E?.localActions??P?.localActions??["exec_approve","exec_reject"],H,N,["connector_rollback","connector_upgrade_push",i]),skills:b},agent:{command:s.command,args:[...s.args??[],...n.args??[]],env:n.env},adapterOptions:m,acpAuthMethod:m.auth_method,acpInitialMode:m.initial_mode,acpMcpTools:m.acp_mcp_tools,promptTimeoutMs:n.prompt_timeout_ms,bindingsPath:ce(n.name),activeEventStorePath:le(n.name),...s.enableSessionBinding||u?{enableSessionBinding:!0}:{},...s.autoInjectArgs?{autoInjectArgs:s.autoInjectArgs}:{},poolMaxSize:n.pool?.maxSize,poolIdleTimeoutMs:n.pool?.idleTimeoutMs,eventQueue:ue(l,n.event_queue),logDir:S.log,providerBaseUrl:n.provider_base_url?.trim()||void 0,providerApiKey:n.provider_api_key?.trim()||void 0}}function pe(){const n=process.env.GRIX_AGENT_STARTUP_WAIT_MS,e=Number(n);return Number.isFinite(e)&&e>=500?Math.floor(e):se}class Ie{instances=[];configMap=new Map;sharedInstances=new Map;shareSyncChains=new Map;stopping=!1;upgradeChecker=null;globalConfigStore;configDir=S.config;sharedOwnersCache=new G(S.data);installer=new ne;async start(e){const t=e??S.config;this.configDir=t,d.info("manager",`Loading configs from ${t}`),Z(),D(),this.globalConfigStore=new V(x(S.data,"agent-global-configs.json")),this.globalConfigStore.load();const s=F(t).filter(i=>i.endsWith(".json")).sort();if(s.length===0)throw new Error(`No config files found in ${t}`);const a=[];let o=0;for(const i of s)try{const l=Q(x(t,i),"utf-8"),u=JSON.parse(l);if(Array.isArray(u.agents)){if(u.agents.length===0){d.error("manager",`No agents array found in ${i}`),o++;continue}for(const f of u.agents)try{const m=O(f);a.push({config:m,file:i}),d.info("manager",`Loaded ${m.name} (${m.adapterType??"acp"}) from ${i}`)}catch(m){const c=typeof f?.name=="string"?f.name:"<unknown>";d.error("manager",`Invalid agent config in ${i} (name=${c}): ${m}`),o++}}else d.error("manager",`Unrecognized config format in ${i}`)}catch(l){d.error("manager",`Failed to load ${i}: ${l}`)}let r=0;if(a.length>0){const i=pe();d.info("manager",`Starting ${a.length} agent(s), startup wait=${i}ms`);const l=()=>this.upgradeChecker?.triggerCheck(),u=c=>{this.instances=this.instances.filter(p=>p!==c)},f=a.map(({config:c})=>{const p=new A(c,this.globalConfigStore);return p.setUpgradeTrigger(l),p.setShareSetHandler(g=>this.onShareSet(c,g)),this.instances.push(p),this.configMap.set(c.name,c),{config:c,instance:p,startPromise:p.start()}}),m=await Promise.all(f.map(async c=>{const p=await new Promise(g=>{let _=!1;const h=setTimeout(()=>{_||(_=!0,g({kind:"timeout"}))},i);c.startPromise.then(()=>{_||(_=!0,clearTimeout(h),g({kind:"started"}))}).catch(w=>{_||(_=!0,clearTimeout(h),g({kind:"failed",error:w}))})});return{task:c,outcome:p}}));for(const{task:c,outcome:p}of m){if(p.kind==="started"){this.restoreCachedSharedInstances(c.config);continue}if(p.kind==="failed"){u(c.instance),d.error("manager",`Failed to start ${c.config.name}: ${p.error}`);continue}r++,d.warn("manager",`Startup pending for ${c.config.name}, continue retrying in background`),c.startPromise.then(()=>{d.info("manager",`Delayed start succeeded: ${c.config.name}`),this.restoreCachedSharedInstances(c.config)}).catch(g=>{u(c.instance),d.error("manager",`Delayed start failed: ${c.config.name}: ${g}`)})}if(this.instances.length>0){const c=Math.max(0,this.instances.length-r);d.info("manager",`${c}/${a.length} agent(s) running now`)}r>0&&d.warn("manager",`${r} agent(s) still connecting in background`)}if(this.instances.length===0&&a.length>0)throw new Error("All agent configurations failed to start");a.length>0&&(this.upgradeChecker=new W(a.map(({config:i})=>({apiKey:i.aibot.apiKey,wsUrl:i.aibot.url})),()=>this.instances.some(i=>i.getStatus().busy)),await this.upgradeChecker.start())}async stop(){d.info("manager","Stopping all agents..."),this.stopping=!0,this.upgradeChecker?.stop(),await Promise.allSettled([...this.shareSyncChains.values()]),this.shareSyncChains.clear();const e=[...this.sharedInstances.values()];this.sharedInstances.clear(),await Promise.allSettled([...this.instances.map(t=>t.stop()),...e.map(t=>t.stop())]),await this.globalConfigStore?.flush(),this.instances=[],D(),d.info("manager","All stopped")}onShareSet(e,t){this.sharedOwnersCache.save(e.name,t);const a=(this.shareSyncChains.get(e.name)??Promise.resolve()).catch(()=>{}).then(()=>this.syncSharedInstances(e,t));this.shareSyncChains.set(e.name,a)}async syncSharedInstances(e,t){if(this.stopping)return;const{toAdd:s,toRemove:a}=R(e.name,this.sharedInstances.keys(),t);for(const o of s){if(this.stopping)break;const r=z(e.name,o);try{const i=L(e,o),l=new A(i,this.globalConfigStore);this.sharedInstances.set(r,l),await l.start(),d.info("manager",`shared instance started: ${r}`)}catch(i){this.sharedInstances.delete(r);const l=i instanceof Error?i.message:String(i);/Auth failed/i.test(l)?(this.sharedOwnersCache.remove(e.name,o),d.warn("manager",`shared instance auth rejected, removed from cache: ${r}`)):d.error("manager",`start shared instance failed ${r}: ${i}`)}}for(const{key:o}of a){const r=this.sharedInstances.get(o);r&&(this.sharedInstances.delete(o),r.stop().catch(i=>d.error("manager",`stop shared instance failed ${o}: ${i}`)),d.info("manager",`shared instance stopped: ${o}`))}}restoreCachedSharedInstances(e){const t=this.sharedOwnersCache.load(e.name);t.length!==0&&(d.info("manager",`restoring ${t.length} cached shared instance(s) for ${e.name}`),this.onShareSet(e,t))}getAgentsStatus(){return this.instances.map(e=>e.getStatus())}async addAgent(e){const t=O(e);if(this.instances.some(a=>a.name===t.name))throw new Error(`Agent "${t.name}" already exists`);const s=new A(t,this.globalConfigStore);s.setUpgradeTrigger(()=>this.upgradeChecker?.triggerCheck()),s.setShareSetHandler(a=>this.onShareSet(t,a)),await s.start(),this.instances.push(s),this.configMap.set(t.name,t),this.persistAgentsConfig(),d.info("manager",`Added agent: ${t.name}`)}async removeAgent(e){const t=this.instances.findIndex(r=>r.name===e);if(t===-1)throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});const s=this.instances[t];this.instances.splice(t,1),this.configMap.delete(e);const a=this.shareSyncChains.get(e);this.shareSyncChains.delete(e),a&&await a.catch(()=>{});const o=`${e}#shared:`;for(const[r,i]of[...this.sharedInstances.entries()])r.startsWith(o)&&(this.sharedInstances.delete(r),i.stop().catch(l=>d.error("manager",`stop shared instance failed ${r}: ${l}`)));this.sharedOwnersCache.delete(e),await s.stop(),this.persistAgentsConfig(),d.info("manager",`Removed agent: ${e}`)}persistAgentsConfig(){const e=x(this.configDir,"agents.json");try{const t=[];for(const[,a]of this.configMap)t.push({name:a.name,ws_url:a.aibot.url,agent_id:a.aibot.agentId,api_key:a.aibot.apiKey,client_type:a.aibot.clientType});B(e,JSON.stringify({agents:t},null,4)+`
2
- `,"utf-8")}catch(t){d.error("manager",`Failed to persist agents config: ${t}`)}}async restartAgent(e){const t=this.instances.findIndex(r=>r.name===e);if(t===-1)throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});const s=this.configMap.get(e);if(!s)throw Object.assign(new Error(`Config for "${e}" not found`),{code:"NOT_FOUND"});await this.instances[t].stop();const o=new A(s,this.globalConfigStore);o.setUpgradeTrigger(()=>this.upgradeChecker?.triggerCheck()),await o.start(),this.instances[t]=o,d.info("manager",`Restarted agent: ${e}`)}async checkUpgrade(){return this.upgradeChecker?this.upgradeChecker.checkForUpdate():{available:!1}}triggerUpgrade(){this.upgradeChecker?.triggerCheck()}async probeAll(e={}){return me(this.instances,e)}async probeOne(e,t={}){return ge(this.instances,e,t)}listInstallable(){return this.installer.listInstallable()}async installAgent(e){const t=await this.installer.install(e);return ae(t),t}getInstallProgress(e){return this.installer.getProgress(e)??null}}async function me(n,e){const t=e.concurrency??4,s=Date.now(),a=new Array(n.length);await new Promise(u=>{let f=0,m=0;const c=n.length;if(c===0){u();return}function p(h){const w=n[h];w.probe(e).then(y=>{a[h]=y,g()},y=>{a[h]={agent_name:w.name,client_type:"unknown",adapter_type:"acp",ok:!1,status:"error",probed_at:Date.now(),duration_ms:0,cached:!1,cli:{command:"",installed:!1,path:null,version:null,error:{code:"internal",message:y?.message??String(y)}},conversation:{attempted:!1,ok:!1,latency_ms:null},config:{model:null,base_url:null,source:{model:"unknown",base_url:"unknown"}},process:{started:!1,alive:!1,busy:!1}},g()})}function g(){m++,f<c?p(f++):m===c&&u()}const _=Math.min(t,c);for(let h=0;h<_;h++)p(f++)});const o=a.filter(u=>u.status==="healthy").length,r=a.filter(u=>u.status==="degraded").length,i=a.filter(u=>u.status==="unavailable").length,l=await re();return{ok:o===a.length&&a.length>0,total:a.length,healthy:o,degraded:r,unavailable:i,installed_clients:l,agents:a,probed_at:s,duration_ms:Date.now()-s}}async function ge(n,e,t){const s=n.find(a=>a.name===e);if(!s)throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});return s.probe(t)}export{Ie as Manager,re as probeInstalledClientCommands,me as probeInstances,ge as probeOneInstance};
1
+ import{readFileSync as F,readdirSync as B,writeFileSync as L}from"node:fs";import{join as x}from"node:path";import{AgentInstance as A}from"./bridge/bridge.js";import{buildSharedInstanceConfig as R,diffSharedOwners as z,sharedInstanceKey as G,SharedOwnersCache as K}from"./manager-share-config.js";import{GRIX_PATHS as S,log as d}from"./core/log/index.js";import{resolveClientVersion as W}from"./core/util/client-version.js";import{UpgradeChecker as V}from"./core/upgrade/upgrade-checker.js";import{AgentGlobalConfigStore as J}from"./core/persistence/agent-global-config-store.js";import{scanSkills as X,dedupeSkills as Y}from"./adapter/claude/skill-scanner.js";import{scanDefaultSkills as Z,logDefaultSkillsCheck as ee,cleanupProjectedSkills as D}from"./default-skills/index.js";import{resolveCopilotCommand as q}from"./core/runtime/copilot-resolve.js";import{getCliVersion as te,resolveCliPath as ne}from"./core/util/cli-probe.js";import{AgentInstaller as ae}from"./core/installer/installer.js";import{reportInstallFailure as se}from"./core/observability/sentry.js";const oe=8e3;function re(){const n=q();return[{clientType:"openclaw",command:"openclaw"},{clientType:"claude",command:"claude"},{clientType:"codex",command:"codex"},{clientType:"gemini",command:"gemini"},{clientType:"qwen",command:"qwen"},{clientType:"hermes",command:"hermes"},{clientType:"reasonix",command:"reasonix"},{clientType:"codewhale",command:"codewhale"},{clientType:"opencode",command:"opencode"},{clientType:"cursor",command:"agent"},{clientType:"pi",command:"pi"},{clientType:"openhuman",command:"openhuman-core"},{clientType:"kiro",command:"kiro-cli"},{clientType:"copilot",command:n.command},{clientType:"agy",command:"agy"}]}async function ie(){return Promise.all(re().map(async n=>{const e=await ne(n.command);if(!e)return{client_type:n.clientType,command:n.command,installed:!1,path:null,version:null};const t=await te(e,n.versionArgs??["--version"]);return{client_type:n.clientType,command:n.command,installed:!0,path:e,version:t.version,error:t.error}}))}function ce(n){switch(n){case"claude":return{adapterType:"claude",command:"claude"};case"codex":return{adapterType:"codex",command:"codex",options:{sandboxMode:"danger-full-access"}};case"gemini":return{adapterType:"acp",command:"gemini",autoInjectArgs:{acp:!0},enableSessionBinding:!0};case"qwen":return{adapterType:"acp",command:"qwen",adapterHint:"qwen/base",autoInjectArgs:{acp:!0},enableSessionBinding:!0};case"pi":return{adapterType:"pi",command:"pi"};case"cursor":return{adapterType:"cursor",command:"agent"};case"reasonix":return{adapterType:"acp",command:"reasonix",args:["acp"],enableSessionBinding:!0};case"codewhale":return{adapterType:"codewhale",command:"codewhale",enableSessionBinding:!0};case"openhuman":return{adapterType:"openhuman",command:"openhuman-core",enableSessionBinding:!0};case"kiro":return{adapterType:"acp",command:"kiro-cli",args:["acp"],enableSessionBinding:!0};case"opencode":return{adapterType:"opencode",command:"opencode",args:["serve"],enableSessionBinding:!0};case"copilot":{const e=q();return{adapterType:"acp",command:e.command,args:[...e.prefixArgs,"--acp"],enableSessionBinding:!0}}case"agy":return{adapterType:"agy",command:"agy",enableSessionBinding:!0};case"hermes":throw new Error('client_type "hermes" is not handled by grix-connector. Hermes runs as a separate project \u2014 see https://github.com/askie/grix-hermes-python');default:throw new Error(`Unsupported client_type: ${n}`)}}function O(n,e){return String(n??"").trim().toLowerCase().replace(/[^a-z0-9._-]+/g,"-").replace(/-+/g,"-").replace(/^-|-$/g,"")||String(e??"").trim().toLowerCase().replace(/[^a-z0-9._-]+/g,"-").replace(/-+/g,"-").replace(/^-|-$/g,"")||"default"}function le(n,e){return x(S.data,`session-bindings-${O(n,e)}.json`)}function de(n,e){return x(S.data,`active-events-${O(n,e)}.json`)}function ue(...n){const e=[],t=new Set;for(const s of n)for(const a of s??[]){const o=String(a??"").trim(),r=o.toLowerCase();!o||t.has(r)||(t.add(r),e.push(o))}return e.length>0?e:void 0}function pe(n,e){const t={claude:{maxConcurrent:1,maxQueued:5,queueTimeoutMs:0},codex:{maxConcurrent:1,maxQueued:5,queueTimeoutMs:0},cursor:{maxConcurrent:1,maxQueued:3,queueTimeoutMs:0},acp:{maxConcurrent:1,maxQueued:3,queueTimeoutMs:0},pi:{maxConcurrent:1,maxQueued:5,queueTimeoutMs:0},codewhale:{maxConcurrent:1,maxQueued:3,queueTimeoutMs:0},openhuman:{maxConcurrent:1,maxQueued:3,queueTimeoutMs:0},opencode:{maxConcurrent:1,maxQueued:3,queueTimeoutMs:0},agy:{maxConcurrent:1,maxQueued:3,queueTimeoutMs:0}},s=t[n]??t.acp;return{maxConcurrent:e?.max_concurrent??s.maxConcurrent??1,maxQueued:e?.max_queued??s.maxQueued??3,queueTimeoutMs:e?.queue_timeout_ms??s.queueTimeoutMs??0,cancelableQueued:!0,cancelableRunning:!0}}function P(n){const e=W(),t=String(n.client_type??"").trim().toLowerCase(),s=ce(t),a=String(n.ws_url??"").trim(),o="get_session_usage",r="get_rate_limits",i="get_agent_global_config";if(!n.name?.trim())throw new Error("agent name is required");if(!a)throw new Error(`agent ${n.name}: ws_url is required`);if(!n.agent_id?.trim())throw new Error(`agent ${n.name}: agent_id is required`);if(!n.api_key?.trim())throw new Error(`agent ${n.name}: api_key is required`);const l=s.adapterType,u=l==="acp",f=t==="qwen",m={...s.options??{}},c=l==="codex"?{capabilities:["local_action_v1","agent_invoke"],localActions:["session_control","get_context","set_model","set_mode","set_reasoning_effort","set_sandbox_mode","exec_approve","exec_reject","file_list","create_folder","turn_interrupt","permission_approve","permission_reject","thread_compact",o,r]}:null,p=l==="claude"?{localActions:["session_control","set_mode","set_model","claude_interaction_reply","exec_approve","exec_reject","file_list","create_folder","thread_compact",o,r]}:null,g=f?{capabilities:["stream_chunk","local_action_v1"],localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","file_list","create_folder",o],adapterHint:"qwen/base"}:null,_=l==="pi"?{adapterHint:"pi/base",capabilities:["local_action_v1"],localActions:["session_control","set_model","get_context","file_list","create_folder",o]}:null,h=l==="openhuman"?{adapterHint:"openhuman/base",capabilities:["local_action_v1"],localActions:["session_control","set_model","file_list","create_folder",o]}:null,w=l==="cursor"?{adapterHint:"cursor/base",capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","set_mode","get_context","file_list","create_folder",o,r]}:null,y=l==="codewhale"?{capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","file_list","create_folder",o]}:null,k=l==="opencode"?{adapterHint:"opencode/base",capabilities:["stream_chunk","local_action_v1"],localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","file_list","create_folder",o]}:null,T=l==="agy"?{adapterHint:"agy/base",capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","file_list","create_folder",o]}:null,E=u&&!f?{localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","file_list","create_folder",o]}:null,H=t==="kiro"?{localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","file_list","create_folder","thread_compact",o,r]}:null,N=l==="codex"||l==="claude"||t==="gemini"?["session_control","set_model","set_mode"]:void 0,U=[o,r];u&&m.raw_transport===void 0&&(m.raw_transport=t==="gemini");const Q=`${t}/base`,$=l==="claude"?"claude":l==="codex"?"codex":l==="pi"?"pi":t==="kiro"?"kiro":"gemini";let b;try{const M=$==="kiro"?void 0:process.cwd();b=X({mode:$,projectDir:M})??void 0,b&&b.length===0&&(b=void 0)}catch{}const I=Z();if(I.length>0){const C=Y([...b??[],...I]),j=C.filter(v=>v.source==="connector").map(v=>v.name);j.length>0&&d.info("manager",`[${n.name}] injecting connector skills: [${j.join(", ")}]`),b=C.length>0?C:void 0}return{name:n.name,adapterType:l,aibot:{url:a,agentId:n.agent_id,apiKey:n.api_key,clientType:t,clientVersion:e,adapterHint:s.adapterHint??g?.adapterHint??_?.adapterHint??h?.adapterHint??w?.adapterHint??k?.adapterHint??T?.adapterHint??Q,capabilities:c?.capabilities??y?.capabilities??_?.capabilities??h?.capabilities??w?.capabilities??k?.capabilities??T?.capabilities??g?.capabilities??["stream_chunk","local_action_v1","connector_upgrade"],localActions:ue(c?.localActions??y?.localActions??p?.localActions??_?.localActions??h?.localActions??w?.localActions??k?.localActions??T?.localActions??g?.localActions??H?.localActions??E?.localActions??["exec_approve","exec_reject"],N,U,["connector_rollback","connector_upgrade_push",i]),skills:b},agent:{command:s.command,args:[...s.args??[],...n.args??[]],env:n.env},adapterOptions:m,acpAuthMethod:m.auth_method,acpInitialMode:m.initial_mode,acpMcpTools:m.acp_mcp_tools,promptTimeoutMs:n.prompt_timeout_ms,bindingsPath:le(n.name,n.agent_id),activeEventStorePath:de(n.name,n.agent_id),...s.enableSessionBinding||u?{enableSessionBinding:!0}:{},...s.autoInjectArgs?{autoInjectArgs:s.autoInjectArgs}:{},poolMaxSize:n.pool?.maxSize,poolIdleTimeoutMs:n.pool?.idleTimeoutMs,eventQueue:pe(l,n.event_queue),logDir:S.log,providerBaseUrl:n.provider_base_url?.trim()||void 0,providerApiKey:n.provider_api_key?.trim()||void 0}}function me(){const n=process.env.GRIX_AGENT_STARTUP_WAIT_MS,e=Number(n);return Number.isFinite(e)&&e>=500?Math.floor(e):oe}class Me{instances=[];configMap=new Map;sharedInstances=new Map;shareSyncChains=new Map;stopping=!1;upgradeChecker=null;globalConfigStore;configDir=S.config;sharedOwnersCache=new K(S.data);installer=new ae;async start(e){const t=e??S.config;this.configDir=t,d.info("manager",`Loading configs from ${t}`),ee(),D(),this.globalConfigStore=new J(x(S.data,"agent-global-configs.json")),this.globalConfigStore.load();const s=B(t).filter(i=>i.endsWith(".json")).sort();if(s.length===0)throw new Error(`No config files found in ${t}`);const a=[];let o=0;for(const i of s)try{const l=F(x(t,i),"utf-8"),u=JSON.parse(l);if(Array.isArray(u.agents)){if(u.agents.length===0){d.error("manager",`No agents array found in ${i}`),o++;continue}for(const f of u.agents)try{const m=P(f);a.push({config:m,file:i}),d.info("manager",`Loaded ${m.name} (${m.adapterType??"acp"}) from ${i}`)}catch(m){const c=typeof f?.name=="string"?f.name:"<unknown>";d.error("manager",`Invalid agent config in ${i} (name=${c}): ${m}`),o++}}else d.error("manager",`Unrecognized config format in ${i}`)}catch(l){d.error("manager",`Failed to load ${i}: ${l}`)}let r=0;if(a.length>0){const i=me();d.info("manager",`Starting ${a.length} agent(s), startup wait=${i}ms`);const l=()=>this.upgradeChecker?.triggerCheck(),u=c=>{this.instances=this.instances.filter(p=>p!==c)},f=a.map(({config:c})=>{const p=new A(c,this.globalConfigStore);return p.setUpgradeTrigger(l),p.setShareSetHandler(g=>this.onShareSet(c,g)),this.instances.push(p),this.configMap.set(c.name,c),{config:c,instance:p,startPromise:p.start()}}),m=await Promise.all(f.map(async c=>{const p=await new Promise(g=>{let _=!1;const h=setTimeout(()=>{_||(_=!0,g({kind:"timeout"}))},i);c.startPromise.then(()=>{_||(_=!0,clearTimeout(h),g({kind:"started"}))}).catch(w=>{_||(_=!0,clearTimeout(h),g({kind:"failed",error:w}))})});return{task:c,outcome:p}}));for(const{task:c,outcome:p}of m){if(p.kind==="started"){this.restoreCachedSharedInstances(c.config);continue}if(p.kind==="failed"){u(c.instance),d.error("manager",`Failed to start ${c.config.name}: ${p.error}`);continue}r++,d.warn("manager",`Startup pending for ${c.config.name}, continue retrying in background`),c.startPromise.then(()=>{d.info("manager",`Delayed start succeeded: ${c.config.name}`),this.restoreCachedSharedInstances(c.config)}).catch(g=>{u(c.instance),d.error("manager",`Delayed start failed: ${c.config.name}: ${g}`)})}if(this.instances.length>0){const c=Math.max(0,this.instances.length-r);d.info("manager",`${c}/${a.length} agent(s) running now`)}r>0&&d.warn("manager",`${r} agent(s) still connecting in background`)}if(this.instances.length===0&&a.length>0)throw new Error("All agent configurations failed to start");a.length>0&&(this.upgradeChecker=new V(a.map(({config:i})=>({apiKey:i.aibot.apiKey,wsUrl:i.aibot.url})),()=>this.instances.some(i=>i.getStatus().busy)),await this.upgradeChecker.start())}async stop(){d.info("manager","Stopping all agents..."),this.stopping=!0,this.upgradeChecker?.stop(),await Promise.allSettled([...this.shareSyncChains.values()]),this.shareSyncChains.clear();const e=[...this.sharedInstances.values()];this.sharedInstances.clear(),await Promise.allSettled([...this.instances.map(t=>t.stop()),...e.map(t=>t.stop())]),await this.globalConfigStore?.flush(),this.instances=[],D(),d.info("manager","All stopped")}onShareSet(e,t){this.sharedOwnersCache.save(e.name,t);const a=(this.shareSyncChains.get(e.name)??Promise.resolve()).catch(()=>{}).then(()=>this.syncSharedInstances(e,t));this.shareSyncChains.set(e.name,a)}async syncSharedInstances(e,t){if(this.stopping)return;const{toAdd:s,toRemove:a}=z(e.name,this.sharedInstances.keys(),t);for(const o of s){if(this.stopping)break;const r=G(e.name,o);try{const i=R(e,o),l=new A(i,this.globalConfigStore);this.sharedInstances.set(r,l),await l.start(),d.info("manager",`shared instance started: ${r}`)}catch(i){this.sharedInstances.delete(r);const l=i instanceof Error?i.message:String(i);/Auth failed/i.test(l)?(this.sharedOwnersCache.remove(e.name,o),d.warn("manager",`shared instance auth rejected, removed from cache: ${r}`)):d.error("manager",`start shared instance failed ${r}: ${i}`)}}for(const{key:o}of a){const r=this.sharedInstances.get(o);r&&(this.sharedInstances.delete(o),r.stop().catch(i=>d.error("manager",`stop shared instance failed ${o}: ${i}`)),d.info("manager",`shared instance stopped: ${o}`))}}restoreCachedSharedInstances(e){const t=this.sharedOwnersCache.load(e.name);t.length!==0&&(d.info("manager",`restoring ${t.length} cached shared instance(s) for ${e.name}`),this.onShareSet(e,t))}getAgentsStatus(){return this.instances.map(e=>e.getStatus())}async addAgent(e){const t=P(e);if(this.instances.some(a=>a.name===t.name))throw new Error(`Agent "${t.name}" already exists`);const s=new A(t,this.globalConfigStore);s.setUpgradeTrigger(()=>this.upgradeChecker?.triggerCheck()),s.setShareSetHandler(a=>this.onShareSet(t,a)),await s.start(),this.instances.push(s),this.configMap.set(t.name,t),this.persistAgentsConfig(),d.info("manager",`Added agent: ${t.name}`)}async removeAgent(e){const t=this.instances.findIndex(r=>r.name===e);if(t===-1)throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});const s=this.instances[t];this.instances.splice(t,1),this.configMap.delete(e);const a=this.shareSyncChains.get(e);this.shareSyncChains.delete(e),a&&await a.catch(()=>{});const o=`${e}#shared:`;for(const[r,i]of[...this.sharedInstances.entries()])r.startsWith(o)&&(this.sharedInstances.delete(r),i.stop().catch(l=>d.error("manager",`stop shared instance failed ${r}: ${l}`)));this.sharedOwnersCache.delete(e),await s.stop(),this.persistAgentsConfig(),d.info("manager",`Removed agent: ${e}`)}persistAgentsConfig(){const e=x(this.configDir,"agents.json");try{const t=[];for(const[,a]of this.configMap)t.push({name:a.name,ws_url:a.aibot.url,agent_id:a.aibot.agentId,api_key:a.aibot.apiKey,client_type:a.aibot.clientType});L(e,JSON.stringify({agents:t},null,4)+`
2
+ `,"utf-8")}catch(t){d.error("manager",`Failed to persist agents config: ${t}`)}}async restartAgent(e){const t=this.instances.findIndex(r=>r.name===e);if(t===-1)throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});const s=this.configMap.get(e);if(!s)throw Object.assign(new Error(`Config for "${e}" not found`),{code:"NOT_FOUND"});await this.instances[t].stop();const o=new A(s,this.globalConfigStore);o.setUpgradeTrigger(()=>this.upgradeChecker?.triggerCheck()),await o.start(),this.instances[t]=o,d.info("manager",`Restarted agent: ${e}`)}async checkUpgrade(){return this.upgradeChecker?this.upgradeChecker.checkForUpdate():{available:!1}}triggerUpgrade(){this.upgradeChecker?.triggerCheck()}async probeAll(e={}){return ge(this.instances,e)}async probeOne(e,t={}){return he(this.instances,e,t)}listInstallable(){return this.installer.listInstallable()}async installAgent(e){const t=await this.installer.install(e);return se(t),t}getInstallProgress(e){return this.installer.getProgress(e)??null}}async function ge(n,e){const t=e.concurrency??4,s=Date.now(),a=new Array(n.length);await new Promise(u=>{let f=0,m=0;const c=n.length;if(c===0){u();return}function p(h){const w=n[h];w.probe(e).then(y=>{a[h]=y,g()},y=>{a[h]={agent_name:w.name,client_type:"unknown",adapter_type:"acp",ok:!1,status:"error",probed_at:Date.now(),duration_ms:0,cached:!1,cli:{command:"",installed:!1,path:null,version:null,error:{code:"internal",message:y?.message??String(y)}},conversation:{attempted:!1,ok:!1,latency_ms:null},config:{model:null,base_url:null,source:{model:"unknown",base_url:"unknown"}},process:{started:!1,alive:!1,busy:!1}},g()})}function g(){m++,f<c?p(f++):m===c&&u()}const _=Math.min(t,c);for(let h=0;h<_;h++)p(f++)});const o=a.filter(u=>u.status==="healthy").length,r=a.filter(u=>u.status==="degraded").length,i=a.filter(u=>u.status==="unavailable").length,l=await ie();return{ok:o===a.length&&a.length>0,total:a.length,healthy:o,degraded:r,unavailable:i,installed_clients:l,agents:a,probed_at:s,duration_ms:Date.now()-s}}async function he(n,e,t){const s=n.find(a=>a.name===e);if(!s)throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});return s.probe(t)}export{Me as Manager,ie as probeInstalledClientCommands,ge as probeInstances,he as probeOneInstance};
@@ -1 +1 @@
1
- function a(t){const o=new Set([`http://127.0.0.1:${t.serverPort}`,`http://localhost:${t.serverPort}`,...t.allowedOrigins]),e=new Set([`127.0.0.1:${t.serverPort}`,`localhost:${t.serverPort}`,...t.allowedHosts]);return{validateRequest(s){const r=i(s,o);if(!r.ok)return r;const n=l(s,e);return n.ok?{ok:!0}:n}}}function i(t,o){const e=t.headers.origin;return e?o.has(e)?{ok:!0}:{ok:!1,statusCode:403,message:`Origin not allowed: ${e}`}:{ok:!0}}function l(t,o){const e=t.headers.host;return e?o.has(e)?{ok:!0}:{ok:!1,statusCode:403,message:`Host not allowed: ${e}`}:{ok:!1,statusCode:403,message:"Missing Host header"}}export{a as createSecurityPolicy};
1
+ function a(o){const e=new Set([`http://127.0.0.1:${o.serverPort}`,`http://localhost:${o.serverPort}`,...o.allowedOrigins]),t=new Set([`127.0.0.1:${o.serverPort}`,`localhost:${o.serverPort}`,...o.allowedHosts]);return{validateRequest(s){const r=i(s,e);if(!r.ok)return r;const n=l(s,t);return n.ok?{ok:!0}:n}}}function i(o,e){const t=o.headers.origin;return t?e.has(t)?{ok:!0}:{ok:!1,statusCode:403,message:`Origin not allowed: ${t}`}:{ok:!0}}function l(o,e){const t=o.headers.host;return t?e.has(t)?{ok:!0}:{ok:!1,statusCode:403,message:`Host not allowed: ${t}`}:{ok:!1,statusCode:403,message:"Missing Host header"}}export{a as createSecurityPolicy};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "grix-connector",
3
- "version": "2.2.8",
3
+ "version": "2.2.10",
4
4
  "description": "Connect local AI coding agents (Claude, Codex, Gemini, Qwen, DeepSeek, Cursor, OpenCode, Pi, OpenHuman, Reasonix) to the Grix scheduling platform. Also serves as an OpenClaw plugin for Grix channel transport.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",