grix-connector 3.9.2 → 3.10.1
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/adapter/acp/acp-adapter.js +1 -1
- package/dist/adapter/claude/skill-scanner.js +2 -2
- package/dist/adapter/cursor/cursor-adapter.js +5 -5
- package/dist/adapter/opencode/opencode-adapter.js +4 -4
- package/dist/adapter/pi/pi-adapter.js +3 -3
- package/dist/bridge/bridge.js +9 -9
- package/dist/bridge/send-controller.js +3 -3
- package/dist/core/aibot/client.js +2 -2
- package/dist/core/aibot/connection-handle.js +1 -1
- package/dist/core/aibot/errors.js +1 -1
- package/dist/core/config/paths.js +1 -1
- package/dist/core/installer/installer.js +4 -4
- package/dist/core/installer/manual-guide.js +1 -1
- package/dist/core/installer/registry.js +1 -1
- package/dist/core/log/logger.js +6 -6
- package/dist/core/mcp/tools.js +1 -1
- package/dist/core/skill-sync/skill-syncer.js +1 -0
- package/dist/core/skill-sync/skill-upload.js +1 -0
- package/dist/core/skill-sync/sync-state.js +1 -0
- package/dist/default-skills/grix-agent-dispatch/SKILL.md +11 -5
- package/dist/default-skills/index.js +1 -1
- package/dist/manager.js +2 -2
- package/dist/mcp/stream-http/security.js +1 -1
- package/openclaw-plugin/index.js +65 -9
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
import{readFileSync as p,readdirSync as c,existsSync as a,mkdirSync as k,cpSync as y,writeFileSync as h,rmSync as g}from"node:fs";import{join as t,dirname as S}from"node:path";import{homedir as m}from"node:os";import{fileURLToPath as D}from"node:url";import{parseSkillFrontmatter as v,scanSkills as x,dedupeSkills as F}from"../adapter/claude/skill-scanner.js";import{resolveRuntimePaths as w}from"../core/config/index.js";import{log as u}from"../core/log/index.js";const l=S(D(import.meta.url)),f=".grix-managed";function d(){const e=[];let i;try{i=c(l,{withFileTypes:!0,encoding:"utf8"})}catch{return e}for(const r of i){if(!r.isDirectory())continue;const n=t(l,r.name,"SKILL.md");try{const s=p(n,"utf-8"),o=v(s);o?.name&&e.push({name:o.name,description:o.description,trigger:o.trigger,source:"connector"})}catch{}}return e}function T(e){let i=[];try{i=x(e)}catch{i=[]}return F([...i,...d()])}function j(e){const i=[];let r;try{r=c(l,{withFileTypes:!0,encoding:"utf8"})}catch{return i}for(const n of r){if(!n.isDirectory())continue;const s=t(l,n.name);if(a(t(s,"SKILL.md")))try{k(e,{recursive:!0});const o=t(e,n.name);y(s,o,{recursive:!0}),h(t(o,f),"","utf8"),i.push(n.name)}catch{}}return i}function C(){return(process.env.KIMI_CODE_HOME||"").trim()||t(m(),".kimi-code")}function L(){const e=m(),i=process.env.XDG_CONFIG_HOME||t(e,".config");return[t(w().dataDir,"claude-plugin","skills"),t(e,".codex","skills"),t(e,".gemini","skills"),t(e,".qwen","skills"),t(e,".pi","agent","skills"),t(e,".codewhale","skills"),t(i,"opencode","skills"),t(e,".cursor","skills"),t(e,".openhuman","skills"),t(e,".kiro","skills"),t(e,".reasonix","skills"),t(C(),"skills")]}function O(){const e=[];for(const i of L()){let r;try{r=c(i,{withFileTypes:!0,encoding:"utf8"})}catch{continue}for(const n of r){if(!n.isDirectory())continue;const s=t(i,n.name);if(a(t(s,f)))try{g(s,{recursive:!0,force:!0}),e.push(s)}catch{}}}return e.length>0&&u.info("default-skills",`Cleaned ${e.length} projected skill dir(s)`),e}function b(){const e=d();if(e.length===0){u.warn("default-skills","No connector system skills found \u2014 dist/default-skills/ may be missing SKILL.md files");return}u.info("default-skills",`Connector system skills available: [${e.map(i=>i.name).join(", ")}]`)}export{T as buildReportedSkills,O as cleanupProjectedSkills,b as logDefaultSkillsCheck,C as resolveKimiCodeHome,d as scanDefaultSkills,j as syncDefaultSkillsToDir};
|
|
1
|
+
import{readFileSync as p,readdirSync as c,existsSync as a,mkdirSync as k,cpSync as y,writeFileSync as h,rmSync as g}from"node:fs";import{join as t,dirname as S}from"node:path";import{homedir as m}from"node:os";import{fileURLToPath as D}from"node:url";import{parseSkillFrontmatter as v,scanSkills as x,dedupeSkills as F}from"../adapter/claude/skill-scanner.js";import{resolveRuntimePaths as w}from"../core/config/index.js";import{log as u}from"../core/log/index.js";const l=S(D(import.meta.url)),f=".grix-managed";function d(){const e=[];let i;try{i=c(l,{withFileTypes:!0,encoding:"utf8"})}catch{return e}for(const r of i){if(!r.isDirectory())continue;const n=t(l,r.name,"SKILL.md");try{const s=p(n,"utf-8"),o=v(s);o?.name&&e.push({name:o.name,description:o.description,trigger:o.trigger,source:"connector",managed:!0})}catch{}}return e}function T(e){let i=[];try{i=x(e)}catch{i=[]}return F([...i,...d()])}function j(e){const i=[];let r;try{r=c(l,{withFileTypes:!0,encoding:"utf8"})}catch{return i}for(const n of r){if(!n.isDirectory())continue;const s=t(l,n.name);if(a(t(s,"SKILL.md")))try{k(e,{recursive:!0});const o=t(e,n.name);y(s,o,{recursive:!0}),h(t(o,f),"","utf8"),i.push(n.name)}catch{}}return i}function C(){return(process.env.KIMI_CODE_HOME||"").trim()||t(m(),".kimi-code")}function L(){const e=m(),i=process.env.XDG_CONFIG_HOME||t(e,".config");return[t(w().dataDir,"claude-plugin","skills"),t(e,".codex","skills"),t(e,".gemini","skills"),t(e,".qwen","skills"),t(e,".pi","agent","skills"),t(e,".codewhale","skills"),t(i,"opencode","skills"),t(e,".cursor","skills"),t(e,".openhuman","skills"),t(e,".kiro","skills"),t(e,".reasonix","skills"),t(C(),"skills")]}function O(){const e=[];for(const i of L()){let r;try{r=c(i,{withFileTypes:!0,encoding:"utf8"})}catch{continue}for(const n of r){if(!n.isDirectory())continue;const s=t(i,n.name);if(a(t(s,f)))try{g(s,{recursive:!0,force:!0}),e.push(s)}catch{}}}return e.length>0&&u.info("default-skills",`Cleaned ${e.length} projected skill dir(s)`),e}function b(){const e=d();if(e.length===0){u.warn("default-skills","No connector system skills found \u2014 dist/default-skills/ may be missing SKILL.md files");return}u.info("default-skills",`Connector system skills available: [${e.map(i=>i.name).join(", ")}]`)}export{T as buildReportedSkills,O as cleanupProjectedSkills,b as logDefaultSkillsCheck,C as resolveKimiCodeHome,d as scanDefaultSkills,j as syncDefaultSkillsToDir};
|
package/dist/manager.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{readFileSync as W,readdirSync as K,writeFileSync as G}from"node:fs";import{join as S}from"node:path";import{AgentInstance as I}from"./bridge/bridge.js";import{buildSharedInstanceConfig as z,diffSharedOwners as J,sharedInstanceKey as V,SharedOwnersCache as X}from"./manager-share-config.js";import{isAgentDeletedError as j}from"./core/aibot/errors.js";import{GRIX_PATHS as A,log as l}from"./core/log/index.js";import{resolveClientVersion as Y}from"./core/util/client-version.js";import{UpgradeChecker as Z}from"./core/upgrade/upgrade-checker.js";import{AgentGlobalConfigStore as ee}from"./core/persistence/agent-global-config-store.js";import{resolveSkillScanMode as te}from"./adapter/claude/skill-scanner.js";import{buildReportedSkills as ne,logDefaultSkillsCheck as ae,cleanupProjectedSkills as D}from"./default-skills/index.js";import{supportsDirectProviderConfig as re}from"./core/config/provider-env.js";import{resolveCopilotCommand as P}from"./core/runtime/copilot-resolve.js";import{resolveCursorCommand as O}from"./core/runtime/cursor-resolve.js";import{getCliVersion as se,resolveCliPath as oe}from"./core/util/cli-probe.js";import{AgentInstaller as ie}from"./core/installer/installer.js";import{reportInstallFailure as ce}from"./core/observability/sentry.js";const le=8e3;function de(){const n=P();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:O()},{clientType:"pi",command:"pi"},{clientType:"openhuman",command:"openhuman-core"},{clientType:"kiro",command:"kiro-cli"},{clientType:"kimi",command:"kimi"},{clientType:"copilot",command:n.command},{clientType:"agy",command:"agy"}]}async function pe(){return Promise.all(de().map(async n=>{const e=await oe(n.command);if(!e)return{client_type:n.clientType,command:n.command,installed:!1,path:null,version:null};const t=await se(e,n.versionArgs??["--version"]);return{client_type:n.clientType,command:n.command,installed:!0,path:e,version:t.version,error:t.error}}))}function me(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:O()};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"kimi":return{adapterType:"acp",command:"kimi",args:["acp"],options:{initial_mode:"yolo"},enableSessionBinding:!0};case"opencode":return{adapterType:"opencode",command:"opencode",args:["serve"],enableSessionBinding:!0};case"copilot":{const e=P();return{adapterType:"acp",command:e.command,args:[...e.prefixArgs,"--acp"],enableSessionBinding:!0}}case"agy":return{adapterType:"agy",command:"agy",enableSessionBinding:!0};case"hermes":return{adapterType:"acp",command:"hermes",args:["acp"],enableSessionBinding:!0};default:throw new Error(`Unsupported client_type: ${n}`)}}function q(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 ue(n,e){return S(A.data,`session-bindings-${q(n,e)}.json`)}function ge(n,e){return S(A.data,`active-events-${q(n,e)}.json`)}function he(...n){const e=[],t=new Set;for(const r of n)for(const s of r??[]){const i=String(s??"").trim(),o=i.toLowerCase();!i||t.has(o)||(t.add(o),e.push(i))}return e.length>0?e:void 0}function fe(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}},r=t[n]??t.acp;return{maxConcurrent:e?.max_concurrent??r.maxConcurrent??1,maxQueued:e?.max_queued??r.maxQueued??3,queueTimeoutMs:e?.queue_timeout_ms??r.queueTimeoutMs??0,cancelableQueued:!0,cancelableRunning:!0}}function b(n,e={}){const t=Y(),r=String(n.client_type??"").trim().toLowerCase(),s=me(r),i=String(n.ws_url??"").trim(),o="get_session_usage",p="get_rate_limits",c="get_agent_global_config";if(!n.name?.trim())throw new Error("agent name is required");if(!i)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 a=s.adapterType,u=a==="acp",m=r==="qwen",_={...s.options??{}},w=a==="codex"?{capabilities:["local_action_v1","agent_invoke"],localActions:["session_control","get_context","set_model","set_mode","set_reasoning_effort","set_service_tier","set_sandbox_mode","exec_approve","exec_reject","file_list","create_folder","turn_interrupt","permission_approve","permission_reject","thread_compact",o,p]}:null,d=a==="claude"?{localActions:["session_control","set_mode","set_model","claude_interaction_reply","exec_approve","exec_reject","file_list","create_folder","thread_compact",o,p]}:null,f=m?{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,h=a==="pi"?{adapterHint:"pi/base",capabilities:["local_action_v1"],localActions:["session_control","set_model","get_context","file_list","create_folder",o]}:null,g=a==="openhuman"?{adapterHint:"openhuman/base",capabilities:["local_action_v1"],localActions:["session_control","set_model","file_list","create_folder",o]}:null,y=a==="cursor"?{adapterHint:"cursor/base",capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","set_mode","get_context","file_list","create_folder",o,p]}:null,v=a==="codewhale"?{capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","file_list","create_folder",o]}:null,k=a==="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,$=a==="agy"?{adapterHint:"agy/base",capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","file_list","create_folder",o]}:null,N=u&&!m?{localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","file_list","create_folder",o]}:null,R=r==="kiro"?{localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","file_list","create_folder","thread_compact",o,p]}:null,B=a==="codex"||a==="claude"||r==="gemini"?["session_control","set_model","set_mode"]:void 0,F=[o,p];u&&_.raw_transport===void 0&&(_.raw_transport=r==="gemini");const U=`${r}/base`,E=te(a,r),L=E==="kiro"?void 0:process.cwd(),T=ne({mode:E,projectDir:L}),M=T.filter(x=>x.source==="connector").map(x=>x.name);M.length>0&&!e.quiet&&l.info("manager",`[${n.name}] injecting connector skills: [${M.join(", ")}]`);let Q=T.length>0?T:void 0;return{name:n.name,adapterType:a,aibot:{url:i,agentId:n.agent_id,apiKey:n.api_key,clientType:r,clientVersion:t,adapterHint:s.adapterHint??f?.adapterHint??h?.adapterHint??g?.adapterHint??y?.adapterHint??k?.adapterHint??$?.adapterHint??U,capabilities:w?.capabilities??v?.capabilities??h?.capabilities??g?.capabilities??y?.capabilities??k?.capabilities??$?.capabilities??f?.capabilities??["stream_chunk","local_action_v1","connector_upgrade"],localActions:he(w?.localActions??v?.localActions??d?.localActions??h?.localActions??g?.localActions??y?.localActions??k?.localActions??$?.localActions??f?.localActions??R?.localActions??N?.localActions??["exec_approve","exec_reject"],B,F,["connector_rollback","connector_upgrade_push",c,"configure_gateway_provider"]),skills:Q},agent:{command:s.command,args:[...s.args??[],...n.args??[]],env:n.env,clientType:r,...n.provider?{provider:{...n.provider.base_url?.trim()?{baseUrl:n.provider.base_url.trim()}:{},...n.provider.api_key?.trim()?{apiKey:n.provider.api_key.trim()}:{},...n.provider.model?.trim()?{model:n.provider.model.trim()}:{}}}:{}},adapterOptions:_,acpAuthMethod:_.auth_method,acpInitialMode:_.initial_mode,acpMcpTools:_.acp_mcp_tools,promptTimeoutMs:n.prompt_timeout_ms,bindingsPath:ue(n.name,n.agent_id),activeEventStorePath:ge(n.name,n.agent_id),...s.enableSessionBinding||u?{enableSessionBinding:!0}:{},...s.autoInjectArgs||r==="reasonix"&&n.provider?.model?.trim()?{autoInjectArgs:{...s.autoInjectArgs??{},...r==="reasonix"&&n.provider?.model?.trim()?{model:n.provider.model.trim()}:{}}}:{},poolMaxSize:n.pool?.maxSize,poolIdleTimeoutMs:n.pool?.idleTimeoutMs,eventQueue:fe(a,n.event_queue),logDir:A.log,providerBaseUrl:n.provider_base_url?.trim()||void 0,providerApiKey:n.provider_api_key?.trim()||void 0}}function _e(){const n=process.env.GRIX_AGENT_STARTUP_WAIT_MS,e=Number(n);return Number.isFinite(e)&&e>=500?Math.floor(e):le}function H(n){const e=[],t=[],r=[],s=K(n).filter(i=>i.endsWith(".json")).sort();for(const i of s)try{const o=JSON.parse(W(S(n,i),"utf-8"));if(o===null||typeof o!="object"||Array.isArray(o)||!("agents"in o)){r.push(i);continue}if(!Array.isArray(o.agents)){t.push({file:i,error:"agents is not an array"});continue}if(o.agents.length===0){t.push({file:i,error:"agents array is empty"});continue}for(const p of o.agents)e.push({entry:p,file:i})}catch(o){t.push({file:i,error:o instanceof Error?o.message:String(o)})}return{entries:e,fileErrors:t,skipped:r}}function C(n){if(n===null||typeof n!="object")return JSON.stringify(n)??"null";if(Array.isArray(n))return`[${n.map(C).join(",")}]`;const e=n;return`{${Object.keys(e).sort().map(r=>`${JSON.stringify(r)}:${C(e[r])}`).join(",")}}`}function ye(n,e){return C(n)===C(e)}class Ne{instances=[];configMap=new Map;rawConfigMap=new Map;reloadChain=Promise.resolve();sharedInstances=new Map;shareSyncChains=new Map;stopping=!1;deletedAgentCleanups=new Set;upgradeChecker=null;globalConfigStore;configDir=A.config;sharedOwnersCache=new X(A.data);installer=new ie;async start(e){const t=e??A.config;this.configDir=t,l.info("manager",`Loading configs from ${t}`),ae(),D(),this.globalConfigStore=new ee(S(A.data,"agent-global-configs.json")),this.globalConfigStore.load();const{entries:r,fileErrors:s,skipped:i}=H(t);for(const a of s)l.error("manager",`Failed to load ${a.file}: ${a.error}`);for(const a of i)l.info("manager",`Skipped ${a}: not an agent config (no "agents" key)`);if(r.length===0&&s.length===0&&i.length===0)throw new Error(`No config files found in ${t}`);const o=[];let p=s.length;for(const{entry:a,file:u}of r)try{const m=b(a);o.push({config:m,entry:a,file:u}),l.info("manager",`Loaded ${m.name} (${m.adapterType??"acp"}) from ${u}`)}catch(m){const _=typeof a?.name=="string"?a.name:"<unknown>";l.error("manager",`Invalid agent config in ${u} (name=${_}): ${m}`),p++}let c=0;if(o.length>0){const a=_e();l.info("manager",`Starting ${o.length} agent(s), startup wait=${a}ms`);const u=()=>this.upgradeChecker?.triggerCheck(),m=d=>{this.instances=this.instances.filter(f=>f!==d)},_=o.map(({config:d,entry:f,file:h})=>{const g=new I(d,this.globalConfigStore);return g.setUpgradeTrigger(u),g.setShareSetHandler(y=>this.onShareSet(d,y)),g.setAgentDeletedHandler(()=>this.onAgentDeletedByPlatform(d.name)),this.instances.push(g),this.configMap.set(d.name,d),this.rawConfigMap.set(d.name,{entry:f,file:h}),{config:d,instance:g,startPromise:g.start()}}),w=await Promise.all(_.map(async d=>{const f=await new Promise(h=>{let g=!1;const y=setTimeout(()=>{g||(g=!0,h({kind:"timeout"}))},a);d.startPromise.then(()=>{g||(g=!0,clearTimeout(y),h({kind:"started"}))}).catch(v=>{g||(g=!0,clearTimeout(y),h({kind:"failed",error:v}))})});return{task:d,outcome:f}}));for(const{task:d,outcome:f}of w){if(f.kind==="started"){this.restoreCachedSharedInstances(d.config);continue}if(f.kind==="failed"){if(j(f.error)){this.onAgentDeletedByPlatform(d.config.name);continue}m(d.instance),l.error("manager",`Failed to start ${d.config.name}: ${f.error}`);continue}c++,l.warn("manager",`Startup pending for ${d.config.name}, continue retrying in background`),d.startPromise.then(()=>{l.info("manager",`Delayed start succeeded: ${d.config.name}`),this.restoreCachedSharedInstances(d.config)}).catch(h=>{if(j(h)){this.onAgentDeletedByPlatform(d.config.name);return}m(d.instance),l.error("manager",`Delayed start failed: ${d.config.name}: ${h}`)})}if(this.instances.length>0){const d=Math.max(0,this.instances.length-c);l.info("manager",`${d}/${o.length} agent(s) running now`)}c>0&&l.warn("manager",`${c} agent(s) still connecting in background`)}if(this.instances.length===0&&o.length>0)throw new Error("All agent configurations failed to start");o.length>0&&(this.upgradeChecker=new Z(o.map(({config:a})=>({apiKey:a.aibot.apiKey,wsUrl:a.aibot.url})),()=>this.instances.some(a=>a.hasPendingOrBackgroundWork())||[...this.sharedInstances.values()].some(a=>a.hasPendingOrBackgroundWork())),await this.upgradeChecker.start())}async stop(){l.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(),l.info("manager","All stopped")}onShareSet(e,t){this.sharedOwnersCache.save(e.name,t);const s=(this.shareSyncChains.get(e.name)??Promise.resolve()).catch(()=>{}).then(()=>this.syncSharedInstances(e,t));this.shareSyncChains.set(e.name,s)}async syncSharedInstances(e,t){if(this.stopping)return;const{toAdd:r,toRemove:s}=J(e.name,this.sharedInstances.keys(),t);for(const i of r){if(this.stopping)break;const o=V(e.name,i);try{const p=z(e,i),c=new I(p,this.globalConfigStore);this.sharedInstances.set(o,c),await c.start(),l.info("manager",`shared instance started: ${o}`)}catch(p){this.sharedInstances.delete(o);const c=p instanceof Error?p.message:String(p);/Auth failed/i.test(c)?(this.sharedOwnersCache.remove(e.name,i),l.warn("manager",`shared instance auth rejected, removed from cache: ${o}`)):l.error("manager",`start shared instance failed ${o}: ${p}`)}}for(const{key:i}of s){const o=this.sharedInstances.get(i);o&&(this.sharedInstances.delete(i),o.stop().catch(p=>l.error("manager",`stop shared instance failed ${i}: ${p}`)),l.info("manager",`shared instance stopped: ${i}`))}}restoreCachedSharedInstances(e){const t=this.sharedOwnersCache.load(e.name);t.length!==0&&(l.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){await this.addAgentInternal(e,"agents.json"),this.persistAgentsConfig(),l.info("manager",`Added agent: ${e.name}`)}async addAgentInternal(e,t){const r=b(e);if(this.instances.some(s=>s.name===r.name))throw new Error(`Agent "${r.name}" already exists`);await this.startInstanceFromConfig(r,e,t)}async startInstanceFromConfig(e,t,r){const s=new I(e,this.globalConfigStore);s.setUpgradeTrigger(()=>this.upgradeChecker?.triggerCheck()),s.setShareSetHandler(i=>this.onShareSet(e,i)),s.setAgentDeletedHandler(()=>this.onAgentDeletedByPlatform(e.name)),s.setProviderConfigHandler(i=>this.setAgentProvider(e.name,i)),s.setRelayEnvSettledHandler(()=>this.relayEnvSettledHandler?.()),await s.start(),this.instances.push(s),this.configMap.set(e.name,e),this.rawConfigMap.set(e.name,{entry:t,file:r}),this.relayEnvSettledHandler?.()}async removeAgent(e){if(!this.instances.some(t=>t.name===e))throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});await this.removeAgentInternal(e),this.persistAgentsConfig(),l.info("manager",`Removed agent: ${e}`),this.relayEnvSettledHandler?.()}async removeAgentInternal(e,t){const r=this.instances.findIndex(p=>p.name===e),s=r===-1?void 0:this.instances[r];r!==-1&&this.instances.splice(r,1),this.configMap.delete(e),this.rawConfigMap.delete(e);const i=this.shareSyncChains.get(e);this.shareSyncChains.delete(e),i&&await i.catch(()=>{});const o=`${e}#shared:`;for(const[p,c]of[...this.sharedInstances.entries()])p.startsWith(o)&&(this.sharedInstances.delete(p),c.stop().catch(a=>l.error("manager",`stop shared instance failed ${p}: ${a}`)));t?.keepShareCache||this.sharedOwnersCache.delete(e),s&&await s.stop()}onAgentDeletedByPlatform(e){this.stopping||this.deletedAgentCleanups.has(e)||(this.deletedAgentCleanups.add(e),(async()=>{try{if(!(this.rawConfigMap.has(e)||this.instances.some(r=>r.name===e)))return;l.warn("manager",`Agent "${e}" \u5DF2\u5728\u5E73\u53F0\u5220\u9664\uFF0C\u505C\u6B62\u5B9E\u4F8B\u5E76\u6E05\u7406\u672C\u5730\u914D\u7F6E`),await this.removeAgentInternal(e),this.persistAgentsConfig(),l.info("manager",`Agent "${e}" \u5DF2\u5728\u5E73\u53F0\u5220\u9664\uFF0C\u672C\u5730\u914D\u7F6E\u5DF2\u6E05\u7406\uFF08agents.json \u5DF2\u79FB\u9664\u8BE5\u6761\u76EE\uFF09`)}catch(t){l.error("manager",`cleanup for deleted agent "${e}" failed: ${t}`)}finally{this.deletedAgentCleanups.delete(e)}})())}persistAgentsConfig(){const e=S(this.configDir,"agents.json");try{const t=[];for(const[,s]of this.configMap){const i=this.rawConfigMap.get(s.name)?.entry.provider;t.push({name:s.name,ws_url:s.aibot.url,agent_id:s.aibot.agentId,api_key:s.aibot.apiKey,client_type:s.aibot.clientType,...i?{provider:i}:{}})}G(e,JSON.stringify({agents:t},null,4)+`
|
|
2
|
-
`,"utf-8")}catch(t){l.error("manager",`Failed to persist agents config: ${t}`)}}isAgentBusy(e){return this.instances.find(r=>r.name===e)?.hasPendingWork()??!1}getAgentsWithStaleRelayEnv(){return this.instances.filter(e=>e.hasStaleRelayEnv()).map(e=>e.name)}markAgentRelayEnvStale(e){const t=this.instances.find(r=>r.name===e);return t?(t.markRelayEnvStale(),!0):!1}relayEnvSettledHandler;setRelayEnvSettledHandler(e){this.relayEnvSettledHandler=e}async restartAgent(e){const t=this.rawConfigMap.get(e);if(!t)throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});await this.replaceInstance(e,t.entry,t.file),l.info("manager",`Restarted agent: ${e}`)}async setAgentProvider(e,t){const r=this.rawConfigMap.get(e);if(!r)throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});const s={...r.entry,provider:t};await this.replaceInstance(e,s,r.file),this.persistAgentsConfig(),l.info("manager",`Updated provider config for agent: ${e}`)}async clearDirectProviderConfigs(){const e=[...this.rawConfigMap.entries()].filter(([,r])=>!!r.entry.provider&&
|
|
1
|
+
import{readFileSync as K,readdirSync as G,writeFileSync as z}from"node:fs";import{join as A}from"node:path";import{AgentInstance as T}from"./bridge/bridge.js";import{buildSharedInstanceConfig as J,diffSharedOwners as V,sharedInstanceKey as X,SharedOwnersCache as Y}from"./manager-share-config.js";import{isAgentDeletedError as D}from"./core/aibot/errors.js";import{GRIX_PATHS as w,log as l}from"./core/log/index.js";import{resolveClientVersion as Z}from"./core/util/client-version.js";import{UpgradeChecker as ee}from"./core/upgrade/upgrade-checker.js";import{SkillSyncer as te}from"./core/skill-sync/skill-syncer.js";import{AgentGlobalConfigStore as ne}from"./core/persistence/agent-global-config-store.js";import{resolveSkillScanMode as ae}from"./adapter/claude/skill-scanner.js";import{buildWireSkills as re}from"./core/skill-sync/sync-state.js";import{buildReportedSkills as se,logDefaultSkillsCheck as oe,cleanupProjectedSkills as P}from"./default-skills/index.js";import{supportsDirectProviderConfig as ie}from"./core/config/provider-env.js";import{resolveCopilotCommand as O}from"./core/runtime/copilot-resolve.js";import{resolveCursorCommand as H}from"./core/runtime/cursor-resolve.js";import{getCliVersion as ce,resolveCliPath as le}from"./core/util/cli-probe.js";import{AgentInstaller as de}from"./core/installer/installer.js";import{reportInstallFailure as pe}from"./core/observability/sentry.js";const me=8e3;function ue(){const n=O();return[{clientType:"openclaw",command:"openclaw"},{clientType:"claude",command:"claude"},{clientType:"codex",command:"codex"},{clientType:"qwen",command:"qwen"},{clientType:"hermes",command:"hermes"},{clientType:"reasonix",command:"reasonix"},{clientType:"codewhale",command:"codewhale"},{clientType:"opencode",command:"opencode"},{clientType:"cursor",command:H()},{clientType:"pi",command:"pi"},{clientType:"openhuman",command:"openhuman-core"},{clientType:"kiro",command:"kiro-cli"},{clientType:"kimi",command:"kimi"},{clientType:"copilot",command:n.command},{clientType:"agy",command:"agy"}]}async function ge(){return Promise.all(ue().map(async n=>{const e=await le(n.command);if(!e)return{client_type:n.clientType,command:n.command,installed:!1,path:null,version:null};const t=await ce(e,n.versionArgs??["--version"]);return{client_type:n.clientType,command:n.command,installed:!0,path:e,version:t.version,error:t.error}}))}function he(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:H()};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"kimi":return{adapterType:"acp",command:"kimi",args:["acp"],options:{initial_mode:"yolo"},enableSessionBinding:!0};case"opencode":return{adapterType:"opencode",command:"opencode",args:["serve"],enableSessionBinding:!0};case"copilot":{const e=O();return{adapterType:"acp",command:e.command,args:[...e.prefixArgs,"--acp"],enableSessionBinding:!0}}case"agy":return{adapterType:"agy",command:"agy",enableSessionBinding:!0};case"hermes":return{adapterType:"acp",command:"hermes",args:["acp"],enableSessionBinding:!0};default:throw new Error(`Unsupported client_type: ${n}`)}}function q(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 fe(n,e){return A(w.data,`session-bindings-${q(n,e)}.json`)}function _e(n,e){return A(w.data,`active-events-${q(n,e)}.json`)}function ye(...n){const e=[],t=new Set;for(const r of n)for(const s of r??[]){const i=String(s??"").trim(),o=i.toLowerCase();!i||t.has(o)||(t.add(o),e.push(i))}return e.length>0?e:void 0}function we(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}},r=t[n]??t.acp;return{maxConcurrent:e?.max_concurrent??r.maxConcurrent??1,maxQueued:e?.max_queued??r.maxQueued??3,queueTimeoutMs:e?.queue_timeout_ms??r.queueTimeoutMs??0,cancelableQueued:!0,cancelableRunning:!0}}function b(n,e={}){const t=Z(),r=String(n.client_type??"").trim().toLowerCase(),s=he(r),i=String(n.ws_url??"").trim(),o="get_session_usage",p="get_rate_limits",c="get_agent_global_config";if(!n.name?.trim())throw new Error("agent name is required");if(!i)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 a=s.adapterType,g=a==="acp",m=r==="qwen",_={...s.options??{}},S=a==="codex"?{capabilities:["local_action_v1","agent_invoke"],localActions:["session_control","get_context","set_model","set_mode","set_reasoning_effort","set_service_tier","set_sandbox_mode","exec_approve","exec_reject","file_list","create_folder","turn_interrupt","permission_approve","permission_reject","thread_compact",o,p]}:null,d=a==="claude"?{localActions:["session_control","set_mode","set_model","claude_interaction_reply","exec_approve","exec_reject","file_list","create_folder","thread_compact",o,p]}:null,f=m?{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,h=a==="pi"?{adapterHint:"pi/base",capabilities:["local_action_v1"],localActions:["session_control","set_model","get_context","file_list","create_folder",o]}:null,u=a==="openhuman"?{adapterHint:"openhuman/base",capabilities:["local_action_v1"],localActions:["session_control","set_model","file_list","create_folder",o]}:null,y=a==="cursor"?{adapterHint:"cursor/base",capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","set_mode","get_context","file_list","create_folder",o,p]}:null,v=a==="codewhale"?{capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","file_list","create_folder",o]}:null,C=a==="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,$=a==="agy"?{adapterHint:"agy/base",capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","file_list","create_folder",o]}:null,R=g&&!m?{localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","file_list","create_folder",o]}:null,B=r==="kiro"?{localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","file_list","create_folder","thread_compact",o,p]}:null,F=a==="codex"||a==="claude"||r==="gemini"?["session_control","set_model","set_mode"]:void 0,U=[o,p];g&&_.raw_transport===void 0&&(_.raw_transport=r==="gemini");const L=`${r}/base`,I=ae(a,r),Q=I==="kiro"?void 0:process.cwd(),E=se({mode:I,projectDir:Q}),M=E.filter(x=>x.source==="connector").map(x=>x.name);M.length>0&&!e.quiet&&l.info("manager",`[${n.name}] injecting connector skills: [${M.join(", ")}]`);const j=re(E,w.skills);let W=j.length>0?j:void 0;return{name:n.name,adapterType:a,aibot:{url:i,agentId:n.agent_id,apiKey:n.api_key,clientType:r,clientVersion:t,adapterHint:s.adapterHint??f?.adapterHint??h?.adapterHint??u?.adapterHint??y?.adapterHint??C?.adapterHint??$?.adapterHint??L,capabilities:S?.capabilities??v?.capabilities??h?.capabilities??u?.capabilities??y?.capabilities??C?.capabilities??$?.capabilities??f?.capabilities??["stream_chunk","local_action_v1","connector_upgrade"],localActions:ye(S?.localActions??v?.localActions??d?.localActions??h?.localActions??u?.localActions??y?.localActions??C?.localActions??$?.localActions??f?.localActions??B?.localActions??R?.localActions??["exec_approve","exec_reject"],F,U,["connector_rollback","connector_upgrade_push",c,"configure_gateway_provider","skill_upload"]),skills:W},agent:{command:s.command,args:[...s.args??[],...n.args??[]],env:n.env,clientType:r,...n.provider?{provider:{...n.provider.base_url?.trim()?{baseUrl:n.provider.base_url.trim()}:{},...n.provider.api_key?.trim()?{apiKey:n.provider.api_key.trim()}:{},...n.provider.model?.trim()?{model:n.provider.model.trim()}:{}}}:{}},adapterOptions:_,acpAuthMethod:_.auth_method,acpInitialMode:_.initial_mode,acpMcpTools:_.acp_mcp_tools,promptTimeoutMs:n.prompt_timeout_ms,bindingsPath:fe(n.name,n.agent_id),activeEventStorePath:_e(n.name,n.agent_id),...s.enableSessionBinding||g?{enableSessionBinding:!0}:{},...s.autoInjectArgs||r==="reasonix"&&n.provider?.model?.trim()?{autoInjectArgs:{...s.autoInjectArgs??{},...r==="reasonix"&&n.provider?.model?.trim()?{model:n.provider.model.trim()}:{}}}:{},poolMaxSize:n.pool?.maxSize,poolIdleTimeoutMs:n.pool?.idleTimeoutMs,eventQueue:we(a,n.event_queue),logDir:w.log,providerBaseUrl:n.provider_base_url?.trim()||void 0,providerApiKey:n.provider_api_key?.trim()||void 0}}function Se(){const n=process.env.GRIX_AGENT_STARTUP_WAIT_MS,e=Number(n);return Number.isFinite(e)&&e>=500?Math.floor(e):me}function N(n){const e=[],t=[],r=[],s=G(n).filter(i=>i.endsWith(".json")).sort();for(const i of s)try{const o=JSON.parse(K(A(n,i),"utf-8"));if(o===null||typeof o!="object"||Array.isArray(o)||!("agents"in o)){r.push(i);continue}if(!Array.isArray(o.agents)){t.push({file:i,error:"agents is not an array"});continue}if(o.agents.length===0){t.push({file:i,error:"agents array is empty"});continue}for(const p of o.agents)e.push({entry:p,file:i})}catch(o){t.push({file:i,error:o instanceof Error?o.message:String(o)})}return{entries:e,fileErrors:t,skipped:r}}function k(n){if(n===null||typeof n!="object")return JSON.stringify(n)??"null";if(Array.isArray(n))return`[${n.map(k).join(",")}]`;const e=n;return`{${Object.keys(e).sort().map(r=>`${JSON.stringify(r)}:${k(e[r])}`).join(",")}}`}function Ae(n,e){return k(n)===k(e)}class Le{instances=[];configMap=new Map;rawConfigMap=new Map;reloadChain=Promise.resolve();sharedInstances=new Map;shareSyncChains=new Map;stopping=!1;deletedAgentCleanups=new Set;upgradeChecker=null;skillSyncer=null;globalConfigStore;configDir=w.config;sharedOwnersCache=new Y(w.data);installer=new de;async start(e){const t=e??w.config;this.configDir=t,l.info("manager",`Loading configs from ${t}`),oe(),P(),this.globalConfigStore=new ne(A(w.data,"agent-global-configs.json")),this.globalConfigStore.load();const{entries:r,fileErrors:s,skipped:i}=N(t);for(const a of s)l.error("manager",`Failed to load ${a.file}: ${a.error}`);for(const a of i)l.info("manager",`Skipped ${a}: not an agent config (no "agents" key)`);if(r.length===0&&s.length===0&&i.length===0)throw new Error(`No config files found in ${t}`);const o=[];let p=s.length;for(const{entry:a,file:g}of r)try{const m=b(a);o.push({config:m,entry:a,file:g}),l.info("manager",`Loaded ${m.name} (${m.adapterType??"acp"}) from ${g}`)}catch(m){const _=typeof a?.name=="string"?a.name:"<unknown>";l.error("manager",`Invalid agent config in ${g} (name=${_}): ${m}`),p++}let c=0;if(o.length>0){const a=Se();l.info("manager",`Starting ${o.length} agent(s), startup wait=${a}ms`);const g=()=>this.upgradeChecker?.triggerCheck(),m=d=>{this.instances=this.instances.filter(f=>f!==d)},_=o.map(({config:d,entry:f,file:h})=>{const u=new T(d,this.globalConfigStore);return u.setUpgradeTrigger(g),u.setShareSetHandler(y=>this.onShareSet(d,y)),u.setAgentDeletedHandler(()=>this.onAgentDeletedByPlatform(d.name)),u.setSkillSyncHandler(()=>this.skillSyncer?.triggerSync()),this.instances.push(u),this.configMap.set(d.name,d),this.rawConfigMap.set(d.name,{entry:f,file:h}),{config:d,instance:u,startPromise:u.start()}}),S=await Promise.all(_.map(async d=>{const f=await new Promise(h=>{let u=!1;const y=setTimeout(()=>{u||(u=!0,h({kind:"timeout"}))},a);d.startPromise.then(()=>{u||(u=!0,clearTimeout(y),h({kind:"started"}))}).catch(v=>{u||(u=!0,clearTimeout(y),h({kind:"failed",error:v}))})});return{task:d,outcome:f}}));for(const{task:d,outcome:f}of S){if(f.kind==="started"){this.restoreCachedSharedInstances(d.config);continue}if(f.kind==="failed"){if(D(f.error)){this.onAgentDeletedByPlatform(d.config.name);continue}m(d.instance),l.error("manager",`Failed to start ${d.config.name}: ${f.error}`);continue}c++,l.warn("manager",`Startup pending for ${d.config.name}, continue retrying in background`),d.startPromise.then(()=>{l.info("manager",`Delayed start succeeded: ${d.config.name}`),this.restoreCachedSharedInstances(d.config)}).catch(h=>{if(D(h)){this.onAgentDeletedByPlatform(d.config.name);return}m(d.instance),l.error("manager",`Delayed start failed: ${d.config.name}: ${h}`)})}if(this.instances.length>0){const d=Math.max(0,this.instances.length-c);l.info("manager",`${d}/${o.length} agent(s) running now`)}c>0&&l.warn("manager",`${c} agent(s) still connecting in background`)}if(this.instances.length===0&&o.length>0)throw new Error("All agent configurations failed to start");o.length>0&&(this.upgradeChecker=new ee(o.map(({config:a})=>({apiKey:a.aibot.apiKey,wsUrl:a.aibot.url})),()=>this.instances.some(a=>a.hasPendingOrBackgroundWork())||[...this.sharedInstances.values()].some(a=>a.hasPendingOrBackgroundWork())),await this.upgradeChecker.start()),o.length>0&&(this.skillSyncer=new te(o.map(({config:a})=>({apiKey:a.aibot.apiKey,wsUrl:a.aibot.url})),w.skills),await this.skillSyncer.start())}async stop(){l.info("manager","Stopping all agents..."),this.stopping=!0,this.upgradeChecker?.stop(),this.skillSyncer?.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=[],P(),l.info("manager","All stopped")}onShareSet(e,t){this.sharedOwnersCache.save(e.name,t);const s=(this.shareSyncChains.get(e.name)??Promise.resolve()).catch(()=>{}).then(()=>this.syncSharedInstances(e,t));this.shareSyncChains.set(e.name,s)}async syncSharedInstances(e,t){if(this.stopping)return;const{toAdd:r,toRemove:s}=V(e.name,this.sharedInstances.keys(),t);for(const i of r){if(this.stopping)break;const o=X(e.name,i);try{const p=J(e,i),c=new T(p,this.globalConfigStore);this.sharedInstances.set(o,c),await c.start(),l.info("manager",`shared instance started: ${o}`)}catch(p){this.sharedInstances.delete(o);const c=p instanceof Error?p.message:String(p);/Auth failed/i.test(c)?(this.sharedOwnersCache.remove(e.name,i),l.warn("manager",`shared instance auth rejected, removed from cache: ${o}`)):l.error("manager",`start shared instance failed ${o}: ${p}`)}}for(const{key:i}of s){const o=this.sharedInstances.get(i);o&&(this.sharedInstances.delete(i),o.stop().catch(p=>l.error("manager",`stop shared instance failed ${i}: ${p}`)),l.info("manager",`shared instance stopped: ${i}`))}}restoreCachedSharedInstances(e){const t=this.sharedOwnersCache.load(e.name);t.length!==0&&(l.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){await this.addAgentInternal(e,"agents.json"),this.persistAgentsConfig(),l.info("manager",`Added agent: ${e.name}`)}async addAgentInternal(e,t){const r=b(e);if(this.instances.some(s=>s.name===r.name))throw new Error(`Agent "${r.name}" already exists`);await this.startInstanceFromConfig(r,e,t)}async startInstanceFromConfig(e,t,r){const s=new T(e,this.globalConfigStore);s.setUpgradeTrigger(()=>this.upgradeChecker?.triggerCheck()),s.setShareSetHandler(i=>this.onShareSet(e,i)),s.setAgentDeletedHandler(()=>this.onAgentDeletedByPlatform(e.name)),s.setSkillSyncHandler(()=>this.skillSyncer?.triggerSync()),s.setProviderConfigHandler(i=>this.setAgentProvider(e.name,i)),s.setRelayEnvSettledHandler(()=>this.relayEnvSettledHandler?.()),await s.start(),this.instances.push(s),this.configMap.set(e.name,e),this.rawConfigMap.set(e.name,{entry:t,file:r}),this.relayEnvSettledHandler?.()}async removeAgent(e){if(!this.instances.some(t=>t.name===e))throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});await this.removeAgentInternal(e),this.persistAgentsConfig(),l.info("manager",`Removed agent: ${e}`),this.relayEnvSettledHandler?.()}async removeAgentInternal(e,t){const r=this.instances.findIndex(p=>p.name===e),s=r===-1?void 0:this.instances[r];r!==-1&&this.instances.splice(r,1),this.configMap.delete(e),this.rawConfigMap.delete(e);const i=this.shareSyncChains.get(e);this.shareSyncChains.delete(e),i&&await i.catch(()=>{});const o=`${e}#shared:`;for(const[p,c]of[...this.sharedInstances.entries()])p.startsWith(o)&&(this.sharedInstances.delete(p),c.stop().catch(a=>l.error("manager",`stop shared instance failed ${p}: ${a}`)));t?.keepShareCache||this.sharedOwnersCache.delete(e),s&&await s.stop()}onAgentDeletedByPlatform(e){this.stopping||this.deletedAgentCleanups.has(e)||(this.deletedAgentCleanups.add(e),(async()=>{try{if(!(this.rawConfigMap.has(e)||this.instances.some(r=>r.name===e)))return;l.warn("manager",`Agent "${e}" \u5DF2\u5728\u5E73\u53F0\u5220\u9664\uFF0C\u505C\u6B62\u5B9E\u4F8B\u5E76\u6E05\u7406\u672C\u5730\u914D\u7F6E`),await this.removeAgentInternal(e),this.persistAgentsConfig(),l.info("manager",`Agent "${e}" \u5DF2\u5728\u5E73\u53F0\u5220\u9664\uFF0C\u672C\u5730\u914D\u7F6E\u5DF2\u6E05\u7406\uFF08agents.json \u5DF2\u79FB\u9664\u8BE5\u6761\u76EE\uFF09`)}catch(t){l.error("manager",`cleanup for deleted agent "${e}" failed: ${t}`)}finally{this.deletedAgentCleanups.delete(e)}})())}persistAgentsConfig(){const e=A(this.configDir,"agents.json");try{const t=[];for(const[,s]of this.configMap){const i=this.rawConfigMap.get(s.name)?.entry.provider;t.push({name:s.name,ws_url:s.aibot.url,agent_id:s.aibot.agentId,api_key:s.aibot.apiKey,client_type:s.aibot.clientType,...i?{provider:i}:{}})}z(e,JSON.stringify({agents:t},null,4)+`
|
|
2
|
+
`,"utf-8")}catch(t){l.error("manager",`Failed to persist agents config: ${t}`)}}isAgentBusy(e){return this.instances.find(r=>r.name===e)?.hasPendingWork()??!1}getAgentsWithStaleRelayEnv(){return this.instances.filter(e=>e.hasStaleRelayEnv()).map(e=>e.name)}markAgentRelayEnvStale(e){const t=this.instances.find(r=>r.name===e);return t?(t.markRelayEnvStale(),!0):!1}relayEnvSettledHandler;setRelayEnvSettledHandler(e){this.relayEnvSettledHandler=e}async restartAgent(e){const t=this.rawConfigMap.get(e);if(!t)throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});await this.replaceInstance(e,t.entry,t.file),l.info("manager",`Restarted agent: ${e}`)}async setAgentProvider(e,t){const r=this.rawConfigMap.get(e);if(!r)throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});const s={...r.entry,provider:t};await this.replaceInstance(e,s,r.file),this.persistAgentsConfig(),l.info("manager",`Updated provider config for agent: ${e}`)}async clearDirectProviderConfigs(){const e=[...this.rawConfigMap.entries()].filter(([,r])=>!!r.entry.provider&&ie(r.entry.client_type)).map(([r])=>r),t=[];for(const r of e)try{await this.setAgentProvider(r,void 0),t.push(r)}catch(s){l.warn("manager",`Failed to clear provider config for "${r}": ${s instanceof Error?s.message:String(s)}`)}return t}async replaceInstance(e,t,r){const s=b(t);await this.removeAgentInternal(e,{keepShareCache:!0}),await this.startInstanceFromConfig(s,t,r),this.restoreCachedSharedInstances(s)}async reload(){const e=this.reloadChain.catch(()=>{}).then(()=>this.doReload());return this.reloadChain=e.catch(()=>{}),e}async doReload(){if(this.stopping)throw Object.assign(new Error("manager is stopping"),{code:"RELOAD_UNSAFE"});const{entries:e,fileErrors:t,skipped:r}=N(this.configDir);for(const c of r)l.info("manager",`reload: skipped ${c}: not an agent config (no "agents" key)`);if(t.length>0){const c=t.map(a=>`${a.file}: ${a.error}`).join("; ");throw Object.assign(new Error(`reload aborted: ${t.length} config file(s) failed to parse [${c}]`),{code:"RELOAD_UNSAFE"})}if(e.length===0)throw Object.assign(new Error("reload aborted: no valid agent config found"),{code:"RELOAD_UNSAFE"});const s=[];for(const{entry:c}of e)try{b(c,{quiet:!0})}catch(a){const g=c?.name,m=typeof g=="string"&&g.trim()?g:"<unknown>";s.push({name:m,error:a instanceof Error?a.message:String(a)})}if(s.length>0){const c=s.map(a=>`${a.name}: ${a.error}`).join("; ");throw Object.assign(new Error(`reload aborted: ${s.length} agent config(s) invalid [${c}]`),{code:"RELOAD_UNSAFE"})}const i=new Map;for(const c of e){const a=String(c.entry?.name??"").trim();a&&(i.has(a)&&l.warn("manager",`reload: duplicate agent name "${a}", last one wins`),i.set(a,c))}const o={added:[],removed:[],restarted:[],unchanged:[],failed:[]},p=new Set(this.rawConfigMap.keys());for(const c of p)if(!i.has(c))try{await this.removeAgentInternal(c),o.removed.push(c)}catch(a){o.failed.push({name:c,error:a instanceof Error?a.message:String(a)})}for(const[c,a]of i){const g=this.rawConfigMap.get(c);if(g)if(Ae(g.entry,a.entry))this.rawConfigMap.set(c,a),o.unchanged.push(c);else try{await this.replaceInstance(c,a.entry,a.file),o.restarted.push(c)}catch(m){o.failed.push({name:c,error:m instanceof Error?m.message:String(m)})}else try{await this.addAgentInternal(a.entry,a.file),o.added.push(c)}catch(m){o.failed.push({name:c,error:m instanceof Error?m.message:String(m)})}}return l.info("manager",`reload done: +${o.added.length} -${o.removed.length} ~${o.restarted.length} =${o.unchanged.length} !${o.failed.length}`),o}async checkUpgrade(){return this.upgradeChecker?this.upgradeChecker.checkForUpdate():{available:!1}}triggerUpgrade(){this.upgradeChecker?.triggerCheck()}async probeAll(e={}){return ve(this.instances,e)}async probeOne(e,t={}){return be(this.instances,e,t)}listInstallable(){return this.installer.listInstallable()}async installAgent(e){const t=await this.installer.install(e);return pe(t),t}getInstallProgress(e){return this.installer.getProgress(e)??null}}async function ve(n,e){const t=e.concurrency??4,r=Date.now(),s=new Array(n.length);await new Promise(a=>{let g=0,m=0;const _=n.length;if(_===0){a();return}function S(h){const u=n[h];u.probe(e).then(y=>{s[h]=y,d()},y=>{s[h]={agent_name:u.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}},d()})}function d(){m++,g<_?S(g++):m===_&&a()}const f=Math.min(t,_);for(let h=0;h<f;h++)S(g++)});const i=s.filter(a=>a.status==="healthy").length,o=s.filter(a=>a.status==="degraded").length,p=s.filter(a=>a.status==="unavailable").length,c=await ge();return{ok:i===s.length&&s.length>0,total:s.length,healthy:i,degraded:o,unavailable:p,installed_clients:c,agents:s,probed_at:r,duration_ms:Date.now()-r}}async function be(n,e,t){const r=n.find(s=>s.name===e);if(!r)throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});return r.probe(t)}export{Le as Manager,ge as probeInstalledClientCommands,ve as probeInstances,be as probeOneInstance};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
function a(
|
|
1
|
+
function a(e){const t=new Set([`http://127.0.0.1:${e.serverPort}`,`http://localhost:${e.serverPort}`,...e.allowedOrigins]),o=new Set([`127.0.0.1:${e.serverPort}`,`localhost:${e.serverPort}`,...e.allowedHosts]);return{validateRequest(s){const r=i(s,t);if(!r.ok)return r;const n=l(s,o);return n.ok?{ok:!0}:n}}}function i(e,t){const o=e.headers.origin;return o?t.has(o)?{ok:!0}:{ok:!1,statusCode:403,message:`Origin not allowed: ${o}`}:{ok:!0}}function l(e,t){const o=e.headers.host;return o?t.has(o)?{ok:!0}:{ok:!1,statusCode:403,message:`Host not allowed: ${o}`}:{ok:!1,statusCode:403,message:"Missing Host header"}}export{a as createSecurityPolicy};
|
package/openclaw-plugin/index.js
CHANGED
|
@@ -6170,6 +6170,7 @@ function resolveRuntimePaths(runtimeDir) {
|
|
|
6170
6170
|
logDir: join6(rootDir, "log"),
|
|
6171
6171
|
dataDir: join6(rootDir, "data"),
|
|
6172
6172
|
serviceDir: join6(rootDir, "service"),
|
|
6173
|
+
skillsDir: join6(rootDir, "skills"),
|
|
6173
6174
|
stateFile: join6(rootDir, "service.json"),
|
|
6174
6175
|
pidFile: join6(rootDir, "grix-connector.pid"),
|
|
6175
6176
|
daemonLockFile: join6(rootDir, "daemon.lock.json"),
|
|
@@ -6193,7 +6194,8 @@ var GRIX_PATHS = {
|
|
|
6193
6194
|
base: paths.rootDir,
|
|
6194
6195
|
config: paths.configDir,
|
|
6195
6196
|
log: paths.logDir,
|
|
6196
|
-
data: paths.dataDir
|
|
6197
|
+
data: paths.dataDir,
|
|
6198
|
+
skills: paths.skillsDir
|
|
6197
6199
|
};
|
|
6198
6200
|
var LEVEL_PRIORITY = {
|
|
6199
6201
|
trace: 0,
|
|
@@ -9252,19 +9254,21 @@ var TOOLS = [
|
|
|
9252
9254
|
},
|
|
9253
9255
|
{
|
|
9254
9256
|
name: "grix_agent_update",
|
|
9255
|
-
description: "Update the text introduction of one of your owner's agents, identified by its numeric agent ID.",
|
|
9257
|
+
description: "Update the display name and/or text introduction of one of your owner's agents, identified by its numeric agent ID. Provide agent_name, introduction, or both \u2014 at least one is required.",
|
|
9256
9258
|
inputSchema: {
|
|
9257
9259
|
type: "object",
|
|
9258
9260
|
properties: {
|
|
9259
9261
|
agent_id: { type: "string", description: "Target agent's numeric ID, passed as a string." },
|
|
9262
|
+
agent_name: { type: "string", description: "New display name (max 100 characters, must be unique among the owner's agents)." },
|
|
9260
9263
|
introduction: { type: "string", description: "New text introduction (max 300 characters)." }
|
|
9261
9264
|
},
|
|
9262
|
-
required: ["agent_id"
|
|
9265
|
+
required: ["agent_id"]
|
|
9263
9266
|
},
|
|
9264
9267
|
validation: {
|
|
9265
|
-
required: ["agent_id"
|
|
9268
|
+
required: ["agent_id"],
|
|
9266
9269
|
properties: {
|
|
9267
9270
|
agent_id: { type: "string" },
|
|
9271
|
+
agent_name: { type: "string", maxLength: 100 },
|
|
9268
9272
|
introduction: { type: "string", maxLength: 300 }
|
|
9269
9273
|
}
|
|
9270
9274
|
}
|
|
@@ -9374,6 +9378,41 @@ var TOOLS = [
|
|
|
9374
9378
|
policy: { type: "string", enum: ["allowlist", "open", "disabled"] }
|
|
9375
9379
|
}
|
|
9376
9380
|
}
|
|
9381
|
+
},
|
|
9382
|
+
{
|
|
9383
|
+
name: "grix_skill_set",
|
|
9384
|
+
description: "Create, update, or delete one of your owner's custom skills in the platform skill library. The skill library auto-syncs to every machine the owner runs an agent on (a per-machine `grix/skills` directory), so defining a skill here makes it available on all of them. A skill is a plain SKILL.md package (frontmatter + body), same standard as other Grix skills. Provide `name` and the full `content` (the SKILL.md text) to create or overwrite by name. To delete a skill, pass its `name` with an empty `content` string. How and when any agent actually loads/uses a synced skill is arranged by the owner (in a system prompt or in conversation); this tool only manages the library.",
|
|
9385
|
+
inputSchema: {
|
|
9386
|
+
type: "object",
|
|
9387
|
+
properties: {
|
|
9388
|
+
name: { type: "string", description: "Skill name (max 100 characters); unique within the owner. Used as the identifier and the synced directory name." },
|
|
9389
|
+
content: { type: "string", description: "Full SKILL.md text. Pass an empty string to delete the skill by name." }
|
|
9390
|
+
},
|
|
9391
|
+
required: ["name", "content"]
|
|
9392
|
+
},
|
|
9393
|
+
validation: {
|
|
9394
|
+
required: ["name", "content"],
|
|
9395
|
+
properties: {
|
|
9396
|
+
name: { type: "string", maxLength: 100 },
|
|
9397
|
+
content: { type: "string", maxLength: 262144 }
|
|
9398
|
+
}
|
|
9399
|
+
}
|
|
9400
|
+
},
|
|
9401
|
+
{
|
|
9402
|
+
name: "grix_skill_get",
|
|
9403
|
+
description: "Read one of your owner's custom skills by name (returns its full SKILL.md content), or list the owner's skill library when no name is given. Use this before editing to fetch current content, then call grix_skill_set with the revised text.",
|
|
9404
|
+
inputSchema: {
|
|
9405
|
+
type: "object",
|
|
9406
|
+
properties: {
|
|
9407
|
+
name: { type: "string", description: "Skill name to fetch. Omit to list all skills (names + versions) instead." }
|
|
9408
|
+
}
|
|
9409
|
+
},
|
|
9410
|
+
validation: {
|
|
9411
|
+
required: [],
|
|
9412
|
+
properties: {
|
|
9413
|
+
name: { type: "string", maxLength: 100 }
|
|
9414
|
+
}
|
|
9415
|
+
}
|
|
9377
9416
|
}
|
|
9378
9417
|
];
|
|
9379
9418
|
var EVENT_TOOLS = [
|
|
@@ -9566,6 +9605,10 @@ function toolCallToInvoke(toolName, args) {
|
|
|
9566
9605
|
return buildChatStateUpdateInvoke(args);
|
|
9567
9606
|
case "grix_access_control":
|
|
9568
9607
|
return buildAccessControlInvoke(args);
|
|
9608
|
+
case "grix_skill_set":
|
|
9609
|
+
return buildSkillSetInvoke(args);
|
|
9610
|
+
case "grix_skill_get":
|
|
9611
|
+
return buildSkillGetInvoke(args);
|
|
9569
9612
|
default:
|
|
9570
9613
|
throw new Error(`Unknown tool: ${toolName}`);
|
|
9571
9614
|
}
|
|
@@ -9658,10 +9701,13 @@ function buildCallOwnerInvoke(args) {
|
|
|
9658
9701
|
return { action: "call_owner", params: { session_id: args.session_id } };
|
|
9659
9702
|
}
|
|
9660
9703
|
function buildAgentUpdateInvoke(args) {
|
|
9661
|
-
|
|
9662
|
-
|
|
9663
|
-
|
|
9664
|
-
};
|
|
9704
|
+
if (args.agent_name == null && args.introduction == null) {
|
|
9705
|
+
throw new Error("grix_agent_update requires agent_name or introduction");
|
|
9706
|
+
}
|
|
9707
|
+
const params = { agent_id: args.agent_id };
|
|
9708
|
+
if (args.agent_name != null) params.agent_name = args.agent_name;
|
|
9709
|
+
if (args.introduction != null) params.introduction = args.introduction;
|
|
9710
|
+
return { action: "agent_introduction_update", params };
|
|
9665
9711
|
}
|
|
9666
9712
|
function buildDispatchAgentInvoke(args) {
|
|
9667
9713
|
return {
|
|
@@ -9701,6 +9747,14 @@ function buildChatStateUpdateInvoke(args) {
|
|
|
9701
9747
|
if (args.reason != null) params.reason = args.reason;
|
|
9702
9748
|
return { action: "chat_state_update", params };
|
|
9703
9749
|
}
|
|
9750
|
+
function buildSkillSetInvoke(args) {
|
|
9751
|
+
return { action: "skill_set", params: { name: args.name, content: args.content }, timeoutMs: 3e4 };
|
|
9752
|
+
}
|
|
9753
|
+
function buildSkillGetInvoke(args) {
|
|
9754
|
+
const params = {};
|
|
9755
|
+
if (args.name != null) params.name = args.name;
|
|
9756
|
+
return { action: "skill_get", params };
|
|
9757
|
+
}
|
|
9704
9758
|
function buildAdminInvoke(args) {
|
|
9705
9759
|
const action = String(args.action ?? "");
|
|
9706
9760
|
const invokeAction = ADMIN_ACTION_MAP[action];
|
|
@@ -9730,7 +9784,9 @@ var PHASE1_INVOKE_ACTIONS = /* @__PURE__ */ new Set([
|
|
|
9730
9784
|
"session_send",
|
|
9731
9785
|
"chat_state_query",
|
|
9732
9786
|
"chat_state_update",
|
|
9733
|
-
"claude_access_control"
|
|
9787
|
+
"claude_access_control",
|
|
9788
|
+
"skill_set",
|
|
9789
|
+
"skill_get"
|
|
9734
9790
|
]);
|
|
9735
9791
|
var ACCESS_CONTROL_ACTION_MAP = {
|
|
9736
9792
|
pair_approve: "pair_approve",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "grix-connector",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.10.1",
|
|
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",
|