grix-connector 4.11.13 → 4.12.0

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/manager.js CHANGED
@@ -1 +1 @@
1
- import{join as v}from"node:path";import{homedir as H}from"node:os";import{AgentInstance as F}from"./bridge/bridge.js";import{buildSharedInstanceConfig as de,diffSharedOwners as pe,sharedInstanceKey as he,SharedOwnersCache as ue}from"./manager-share-config.js";import{AgentDeletedError as ge,isAgentDeletedError as C}from"./core/aibot/errors.js";import{GRIX_PATHS as A,log as g}from"./core/log/index.js";import{resolveClientVersion as me}from"./core/util/client-version.js";import{UpgradeChecker as fe}from"./core/upgrade/upgrade-checker.js";import{SkillSyncer as _e}from"./core/skill-sync/skill-syncer.js";import{AgentGlobalConfigStore as we}from"./core/persistence/agent-global-config-store.js";import{resolveSkillScanMode as ye}from"./adapter/claude/skill-scanner.js";import{buildWireSkills as Ae}from"./core/skill-sync/sync-state.js";import{buildLibrarySkills as Se}from"./core/skill-sync/library-skills.js";import{AUDIT_LOCAL_ACTION_TYPES as be}from"./core/aibot/types.js";import{buildReportedSkills as ve,logDefaultSkillsCheck as ke,cleanupProjectedSkills as W}from"./default-skills/index.js";import{buildProviderEnv as Ie,supportsKiroCwRelay as K,supportsNonMitmRelayConfig as Ee}from"./core/config/provider-env.js";import{resolveHermesHomeDir as Ce}from"./adapter/shared/native-provider-config.js";import{ensureKiroRelay as z,stopKiroRelay as Q}from"./core/proxy/kiro-relay.js";import{resolveCopilotCommand as $e}from"./core/runtime/copilot-resolve.js";import{resolveCursorCommand as Pe}from"./core/runtime/cursor-resolve.js";import{probeInstalledClientCommands as V}from"./core/probe/installed-clients.js";import{AgentInstaller as Te}from"./core/installer/installer.js";import{reportInstallFailure as Me}from"./core/observability/sentry.js";import{createDefaultAuditProducer as xe}from"./audit/runtime/default-audit-recorder.js";import{normalizeCodexConfigOverrides as De}from"./adapter/codex/config-overrides.js";import{ACP_NEW_SESSION_RESERVED_KEYS as Re}from"./protocol/acp-client.js";import{resolveCodexDirectHome as Oe}from"./adapter/codex/direct-home.js";import{AgentConfigRepository as X}from"./core/config/agent-config-repository.js";function Y(a){if(a)return{...a.provider_id?.trim()?{providerId:a.provider_id.trim()}:{},...a.base_url?.trim()?{baseUrl:a.base_url.trim()}:{},...a.quota_base_url?.trim()?{quotaBaseUrl:a.quota_base_url.trim()}:{},...a.api_key?.trim()?{apiKey:a.api_key.trim()}:{},...a.model?.trim()?{model:a.model.trim()}:{},...a.fast_model?.trim()?{fastModel:a.fast_model.trim()}:{},...a.effort?.trim()?{effort:a.effort.trim()}:{}}}const je=8e3,Le=1e4,Ne=8e3;function Ge(a,e){switch(a){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"omp":return{adapterType:"pi",command:"omp",adapterHint:"omp/base",env:{PI_CODING_AGENT_DIR:v(H(),".omp","agent")}};case"cursor":return{adapterType:"cursor",command:Pe()};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"qodercli":return{adapterType:"acp",command:"qodercli",args:["--yolo","--acp"],enableSessionBinding:!0};case"qoderclicn":return{adapterType:"acp",command:"qoderclicn",args:["--yolo","--acp"],enableSessionBinding:!0};case"mcode":return{adapterType:"acp",command:"mcode",args:["acp"],enableSessionBinding:!0};case"dim":return{adapterType:"acp",command:"dim",args:["acp"],enableSessionBinding:!0};case"opencode":return{adapterType:"opencode",command:"opencode",args:["serve"],enableSessionBinding:!0};case"deveco":return{adapterType:"opencode",command:"deveco",args:["serve"],adapterHint:"deveco/base",enableSessionBinding:!0};case"copilot":{const n=$e();return{adapterType:"acp",command:n.command,args:[...n.prefixArgs,"--acp"],enableSessionBinding:!0}}case"agy":return{adapterType:"agy",command:"agy",enableSessionBinding:!0};case"deepseek":return{adapterType:"deepseek-harness",command:"dsh",adapterHint:"deepseek/grix-bridge-v1",options:{integrationMode:"profile_bridge",dshProfile:"web",autoInstallBridge:!0,autoStartProfile:!0},enableSessionBinding:!0};case"hermes":return{adapterType:"acp",command:"hermes",args:["acp"],enableSessionBinding:!0};case"traecli":return{adapterType:"acp",command:"traecli",args:["acp","serve"],adapterHint:"traecli/base",options:{initial_mode:"bypass_permissions"},enableSessionBinding:!0};case"codebuddy":return{adapterType:"acp",command:"codebuddy",args:["--acp"],adapterHint:"codebuddy/base",enableSessionBinding:!0};case"grok":return{adapterType:"acp",command:"grok",args:["--no-auto-update","agent","--always-approve","stdio"],enableSessionBinding:!0};case"qwenpaw":return{adapterType:"acp",command:"qwenpaw",args:["acp"],enableSessionBinding:!0};case"zeroclaw":return{adapterType:"acp",command:"zeroclaw",args:["acp"],enableSessionBinding:!0};case"acp":{const n=typeof e?.command=="string"?e.command.trim():"";if(!n)throw new Error(`agent ${e?.name??"<unknown>"}: client_type=acp requires "command" (the ACP CLI executable) in the agent config; put its ACP subcommand or flag in "args"`);return{adapterType:"acp",command:n,adapterHint:"acp/base",enableSessionBinding:!0}}default:throw new Error(`Unsupported client_type: ${a}`)}}function k(a,e){return String(a??"").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 qe(a){return v(A.data,`session-bindings-${k(a,"unknown")}.json`)}function Ue(a,e){return v(A.data,`session-bindings-${k(a,e)}.json`)}function Be(a,e){return v(A.data,`active-events-${k(a,e)}.json`)}function He(a){return v(A.data,`pending-events-${k(a,"unknown")}.json`)}function Fe(a,e){const n=k(a,"agent"),t=k(e,"unknown");return v(A.data,`terminal-outbox-${n}-${t}.json`)}function We(...a){const e=[],n=new Set;for(const t of a)for(const s of t??[]){const i=String(s??"").trim(),c=i.toLowerCase();!i||n.has(c)||(n.add(c),e.push(i))}return e.length>0?e:void 0}function Ke(a,e){const n={claude:{maxConcurrent:1,maxQueued:99,queueTimeoutMs:0},codex:{maxConcurrent:1,maxQueued:99,queueTimeoutMs:0},cursor:{maxConcurrent:1,maxQueued:99,queueTimeoutMs:0},acp:{maxConcurrent:1,maxQueued:99,queueTimeoutMs:0},pi:{maxConcurrent:1,maxQueued:99,queueTimeoutMs:0},codewhale:{maxConcurrent:1,maxQueued:99,queueTimeoutMs:0},openhuman:{maxConcurrent:1,maxQueued:99,queueTimeoutMs:0},opencode:{maxConcurrent:1,maxQueued:99,queueTimeoutMs:0},agy:{maxConcurrent:1,maxQueued:99,queueTimeoutMs:0},"deepseek-harness":{maxConcurrent:1,maxQueued:99,queueTimeoutMs:0}},t=n[a]??n.acp;let s=e?.max_concurrent??t.maxConcurrent??1;return a==="acp"&&s>1&&(g.warn("manager",`ACP adapter supports only one active run at a time; clamping max_concurrent from ${s} to 1`),s=1),{maxConcurrent:s,maxQueued:e?.max_queued??t.maxQueued??99,queueTimeoutMs:e?.queue_timeout_ms??t.queueTimeoutMs??0,cancelableQueued:!0,cancelableRunning:!0}}function ze(a,e={}){const n=me(),t=String(a.client_type??"").trim().toLowerCase(),s=Ge(t,a),i=t==="deepseek"?a.dsh?.integration_mode??"profile_bridge":void 0,c=i==="embedded_jsonrpc",o=String(a.ws_url??"").trim(),u=typeof a.name=="string"?a.name:"",r=u.trim(),l="get_session_usage",m="get_rate_limits",p="get_agent_global_config",d=be;if(!r)throw new Error("agent name is required");if(u!==r)throw new Error(`agent name must not contain leading or trailing whitespace: "${u}"`);if(!o)throw new Error(`agent ${a.name}: ws_url is required`);if(!a.agent_id?.trim())throw new Error(`agent ${a.name}: agent_id is required`);if(!a.api_key?.trim())throw new Error(`agent ${a.name}: api_key is required`);const h=s.adapterType,_=h==="acp",w=t==="qwen",y={...s.options??{}};if(h==="deepseek-harness"&&(y.integrationMode=i,y.dshProfile=a.dsh?.profile?.trim()||"web",y.autoInstallBridge=a.dsh?.auto_install_bridge??!c,y.autoStartProfile=a.dsh?.auto_start_profile??!c),h==="deepseek-harness"&&a.provider?.model?.trim()&&(y.defaultModel=a.provider.model.trim()),a.codex_config_overrides!==void 0){if(h!=="codex")throw new Error(`agent ${a.name}: codex_config_overrides is only supported for client_type=codex`);De(a.codex_config_overrides,`agent ${a.name}: codex_config_overrides`),y.codexConfigOverrides=a.codex_config_overrides}if(a.acp_new_session_params!==void 0){if(!_)throw new Error(`agent ${a.name}: acp_new_session_params is only supported for ACP-based client types`);const f=a.acp_new_session_params;if(typeof f!="object"||f===null||Array.isArray(f))throw new Error(`agent ${a.name}: acp_new_session_params must be a JSON object`);const b=Object.keys(f).find(le=>Re.has(le));if(b)throw new Error(`agent ${a.name}: acp_new_session_params cannot set reserved field "${b}" (Grix owns cwd/mcpServers/sessionId/additionalDirectories on every session/new)`);y.newSessionParams=f}if(t==="zeroclaw"){const b=a.acp_new_session_params?.agentAlias;if(typeof b!="string"||!b.trim()||b.trim().startsWith("<REPLACE"))throw new Error(`agent ${a.name}: client_type=zeroclaw requires acp_new_session_params.agentAlias (create one with \`zeroclaw agents create <alias>\` and set it in the agent config); without it session/new fails with -32602 and the connector would silently respawn-retry forever`)}if(h==="codex"&&a.provider?.wire_api!==void 0){const f=a.provider;if(f.wire_api!=="responses"||!f.base_url?.trim()||!f.api_key?.trim()||!f.model?.trim()||!f.catalog_version?.trim()||!f.catalog_sha256?.trim()||!f.catalog_json?.trim())throw Object.assign(new Error(`agent ${a.name}: incomplete Codex direct relay config`),{code:"INVALID_CODEX_DIRECT_CONFIG"});const b=Oe(A.data,a.name);y.codexHome=b,y.codexDirect={codexHome:b,config:{baseUrl:f.base_url.trim(),model:f.model.trim(),...f.reasoning_effort?.trim()?{reasoningEffort:f.reasoning_effort.trim()}:{},token:f.api_key.trim()},catalog:{version:f.catalog_version.trim(),sha256:f.catalog_sha256.trim(),json:f.catalog_json}}}const S=h==="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",l,m]}:null,Z=h==="claude"?{localActions:["session_control","set_mode","set_model","set_reasoning_effort","claude_interaction_reply","exec_approve","exec_reject","file_list","create_folder","thread_compact",l,m]}:null,T=w?{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",l],adapterHint:"qwen/base"}:null,M=h==="pi"?{adapterHint:"pi/base",capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_provider","set_model","get_context","file_list","create_folder",l,m]}:null,x=h==="openhuman"?{adapterHint:"openhuman/base",capabilities:["local_action_v1"],localActions:["session_control","set_model","file_list","create_folder"]}:null,D=h==="cursor"?{adapterHint:"cursor/base",capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","set_mode","get_context","file_list","create_folder","thread_compact","claude_interaction_reply",m]}:null,N=h==="codewhale"?{capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","file_list","create_folder",l]}:null,R=h==="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","claude_interaction_reply","file_list","create_folder"]}:null,O=h==="agy"?{adapterHint:"agy/base",capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","file_list","create_folder",m]}:null,j=h==="deepseek-harness"?{adapterHint:c?"deepseek/jsonrpc-v1":"deepseek/grix-bridge-v1",capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_provider","set_model","set_mode","set_thinking","set_reasoning_effort","set_preset","set_profile","create_profile",l,m,"exec_approve","exec_reject","permission_approve","permission_reject","file_list","create_folder","dsh_bridge_status","dsh_install_bridge","dsh_list_plugins","dsh_enable_plugin","dsh_disable_plugin","dsh_refresh_plugins","dsh_list_skills","dsh_enable_skill","dsh_disable_skill","dsh_list_mcp","dsh_list_tools","dsh_list_jobs","dsh_list_goals"]}:null,G=new Set(["hermes","reasonix","kimi","copilot","acp","qodercli","qoderclicn","mcode","dim","traecli","grok","qwenpaw","zeroclaw","codebuddy"]),ee=_&&!G.has(t),te=_&&!w?{localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","claude_interaction_reply","file_list","create_folder",...ee?[l]:[]]}:null,ne=t==="kiro"?{localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","file_list","create_folder","thread_compact",l,m]}:null,se=h==="codex"||h==="claude"||h==="opencode"||h==="deepseek-harness"||t==="gemini"?["session_control","set_model","set_mode",...h==="deepseek-harness"?["set_provider","set_thinking","set_reasoning_effort","set_preset","set_profile","create_profile","dsh_list_plugins","dsh_enable_plugin","dsh_disable_plugin","dsh_refresh_plugins","dsh_list_skills","dsh_enable_skill","dsh_disable_skill"]:[],...h==="claude"?["set_reasoning_effort"]:[]]:void 0,ae=h==="cursor"||h==="opencode"||h==="openhuman"||h==="agy"||_&&G.has(t)?[m]:[l,m];_&&y.raw_transport===void 0&&(y.raw_transport=t==="gemini");const re=`${t}/base`,E=ye(h,t),ie=E==="kiro"?void 0:process.cwd(),L=Y(a.provider),I={...process.env,...s.env,...a.env};if(E==="hermes"&&!I.HERMES_HOME?.trim()){const f=Ie(t,L);I.HERMES_HOME=Ce({...f,...I},a.name)}const q=ve({mode:E,projectDir:ie,env:I}),U=q.filter(f=>f.source==="connector").map(f=>f.name);U.length>0&&!e.quiet&&g.info("manager",`[${a.name}] injecting connector skills: [${U.join(", ")}]`);const B=Ae(q,A.skills);let oe=B.length>0?B:void 0;const ce=Se({skillsDir:A.skills,mode:E,home:H(),env:I});return{name:r,adapterType:h,aibot:{url:o,agentId:a.agent_id,apiKey:a.api_key,clientType:t,clientVersion:n,adapterHint:j?.adapterHint??s.adapterHint??T?.adapterHint??M?.adapterHint??x?.adapterHint??D?.adapterHint??R?.adapterHint??O?.adapterHint??re,capabilities:j?.capabilities??S?.capabilities??N?.capabilities??M?.capabilities??x?.capabilities??D?.capabilities??R?.capabilities??O?.capabilities??T?.capabilities??["stream_chunk","local_action_v1","connector_upgrade"],localActions:We(j?.localActions??S?.localActions??N?.localActions??Z?.localActions??M?.localActions??x?.localActions??D?.localActions??R?.localActions??O?.localActions??T?.localActions??ne?.localActions??te?.localActions??["exec_approve","exec_reject"],se,ae,["connector_rollback","connector_upgrade_push",p,"configure_gateway_provider","skill_upload","skill_delete","skill_enable","skill_disable","skill_refresh",...d]),skills:oe,librarySkills:ce,terminalOutboxPath:Fe(a.name,a.agent_id)},agent:{command:c?"dsh-jsonrpc-agent":s.command,args:[...s.args??[],...a.args??[]],env:s.env?{...s.env,...a.env}:a.env,clientType:t,...L?{provider:L}:{}},adapterOptions:y,acpAuthMethod:y.auth_method,acpInitialMode:y.initial_mode,acpMcpTools:y.acp_mcp_tools,promptTimeoutMs:a.prompt_timeout_ms,bindingsPath:qe(a.agent_id),legacyBindingsPath:Ue(a.name,a.agent_id),activeEventStorePath:Be(a.name,a.agent_id),pendingEventStorePath:He(a.agent_id),...s.enableSessionBinding||_?{enableSessionBinding:!0}:{},...s.autoInjectArgs||t==="reasonix"&&a.provider?.model?.trim()?{autoInjectArgs:{...s.autoInjectArgs??{},...t==="reasonix"&&a.provider?.model?.trim()?{model:a.provider.model.trim()}:{}}}:{},poolMaxSize:a.pool?.maxSize??(h==="deepseek-harness"?4:void 0),poolIdleTimeoutMs:a.pool?.idleTimeoutMs,eventQueue:Ke(h,a.event_queue),logDir:A.log,providerBaseUrl:a.provider_base_url?.trim()||void 0,providerApiKey:a.provider_api_key?.trim()||void 0}}function J(){const a=process.env.GRIX_AGENT_STARTUP_WAIT_MS,e=Number(a);return Number.isFinite(e)&&e>=500?Math.floor(e):je}function $(){const a=process.env.GRIX_AGENT_STOP_WAIT_MS,e=Number(a);return Number.isFinite(e)&&e>=500?Math.floor(e):Le}function Qe(){return Math.min($(),Ne)}function P(a){if(a===null||typeof a!="object")return JSON.stringify(a)??"null";if(Array.isArray(a))return`[${a.map(P).join(",")}]`;const e=a;return`{${Object.keys(e).sort().map(t=>`${JSON.stringify(t)}:${P(e[t])}`).join(",")}}`}function Ve(a,e){return P(a)===P(e)}class Et{options;instances=[];configMap=new Map;rawConfigMap=new Map;mutationTail=Promise.resolve();startingInstances=new Set;failedStartAgents=new Set;removingAgents=new Set;instanceGenerations=new WeakMap;sharedInstances=new Map;shareSyncChains=new Map;stopping=!1;lifecycleAdmissionClosed=!1;deletedAgentCleanups=new Set;pendingPlatformDeletes=new Map;upgradeChecker=null;skillSyncer=null;globalConfigStore;configRepository=new X(A.config);sharedOwnersCache=new ue(A.data);installer=new Te;relaySecretResolver;auditProducer=xe();constructor(e={}){this.options=e}runMutation(e,n=[]){const t=this.mutationTail.catch(()=>{}).then(async()=>{if(this.stopping)throw Object.assign(new Error("manager is stopping"),{code:"MANAGER_STOPPING"});await this.retryPendingPlatformDeletesUnlocked();const s=new Set([...this.pendingPlatformDeletes.values()].map(c=>c.name)),i=n.find(c=>s.has(c.trim()));if(i)throw Object.assign(new Error(`Agent "${i}" is pending permanent platform-deletion cleanup`),{code:"AGENT_DELETE_PENDING"});return e()});return this.mutationTail=t.then(()=>{},()=>{}),t}async startTrackedInstance(e){this.startingInstances.add(e);try{await e.start()}finally{this.startingInstances.delete(e)}}requestStartCancellation(e){const n=e.requestStop;typeof n=="function"&&n.call(e)}async stopInstance(e,n,t=$()){let s;try{await Promise.race([e.stop(n),new Promise((i,c)=>{s=setTimeout(()=>{c(Object.assign(new Error(`Agent "${e.name}" stop timed out after ${t}ms`),{code:"AGENT_STOP_TIMEOUT"}))},t)})])}finally{s!==void 0&&clearTimeout(s)}}async waitForLifecycleDrain(e,n,t=$()){let s;try{await Promise.race([Promise.allSettled([...e]).then(()=>{}),new Promise((i,c)=>{s=setTimeout(()=>{c(Object.assign(new Error(`${n} timed out after ${t}ms`),{code:"LIFECYCLE_DRAIN_TIMEOUT"}))},t)})])}finally{s!==void 0&&clearTimeout(s)}}async waitForShutdownOperation(e,n,t){let s;try{return await Promise.race([e,new Promise((i,c)=>{s=setTimeout(()=>{c(Object.assign(new Error(`${n} timed out after ${t}ms`),{code:"SHUTDOWN_OPERATION_TIMEOUT"}))},t)})])}finally{s!==void 0&&clearTimeout(s)}}isActiveGeneration(e){const n=this.instanceGenerations.get(e);return!!n&&!n.retired&&this.instances.find(t=>t.name===n.name)===e}retireInstance(e){const n=this.instanceGenerations.get(e);n&&(n.retired=!0)}async start(e){return this.runMutation(()=>this.startUnlocked(e))}async startUnlocked(e){const n=e??A.config;this.configRepository=new X(n),g.info("manager",`Loading configs from ${n}`),ke(),W(),this.globalConfigStore=new we(v(A.data,"agent-global-configs.json")),this.globalConfigStore.load();const{entries:t,fileErrors:s,skipped:i}=this.configRepository.scan();for(const r of s)g.error("manager",`Failed to load ${r.file}: ${r.error}`);for(const r of i)g.info("manager",`Skipped ${r}: not an agent config (no "agents" key)`);if(t.length===0&&s.length===0&&i.length===0){g.warn("manager",`No agent configurations found in ${n}; daemon idle (waiting for config). Add agents under this directory then run: grix-connector reload`);return}const c=[];let o=s.length;for(const{entry:r,file:l}of t)try{const m=this.toRuntimeInstanceConfig(r);c.push({config:m,entry:r,file:l}),g.info("manager",`Loaded ${m.name} (${m.adapterType??"acp"}) from ${l}`)}catch(m){const p=typeof r?.name=="string"?r.name:"<unknown>";g.error("manager",`Invalid agent config in ${l} (name=${p}): ${m}`),o++}let u=0;if(c.length>0){const r=J();g.info("manager",`Starting ${c.length} agent(s), startup wait=${r}ms`);const l=d=>{this.instances=this.instances.filter(h=>h!==d)},m=c.map(({config:d,entry:h,file:_})=>{const w=this.createConfiguredInstance(d);return this.instances.push(w),this.configMap.set(d.name,d),this.rawConfigMap.set(d.name,{entry:h,file:_}),{config:d,instance:w,startPromise:this.prepareProviderRuntime(d,h).then(()=>{if(this.stopping)throw this.requestStartCancellation(w),Object.assign(new Error("manager is stopping"),{code:"MANAGER_STOPPING"});return this.startTrackedInstance(w)})}}),p=await Promise.all(m.map(async d=>{const h=await new Promise(_=>{let w=!1;const y=setTimeout(()=>{w||(w=!0,_({kind:"timeout"}))},r);d.startPromise.then(()=>{w||(w=!0,clearTimeout(y),_({kind:"started"}))}).catch(S=>{w||(w=!0,clearTimeout(y),_({kind:"failed",error:S}))})});return{task:d,outcome:h}}));for(const{task:d,outcome:h}of p){if(h.kind==="started"){this.restoreCachedSharedInstances(d.config);continue}if(h.kind==="failed"){if(C(h.error)){this.onAgentDeletedByPlatform(d.instance);continue}try{await this.stopInstance(d.instance),l(d.instance),this.failedStartAgents.delete(d.config.name)}catch(_){this.failedStartAgents.add(d.config.name),g.error("manager",`Failed to clean up partially started ${d.config.name}: ${_}`)}g.error("manager",`Failed to start ${d.config.name}: ${h.error}`);continue}u++,g.warn("manager",`Startup pending for ${d.config.name}, continue retrying in background`),d.startPromise.then(()=>{this.stopping||this.removingAgents.has(d.config.name)||this.isActiveGeneration(d.instance)&&(g.info("manager",`Delayed start succeeded: ${d.config.name}`),this.restoreCachedSharedInstances(d.config))}).catch(async _=>{if(C(_)){this.onAgentDeletedByPlatform(d.instance);return}if(!(this.stopping||this.removingAgents.has(d.config.name))&&this.isActiveGeneration(d.instance)){try{await this.stopInstance(d.instance),l(d.instance),this.failedStartAgents.delete(d.config.name)}catch(w){this.failedStartAgents.add(d.config.name),g.error("manager",`Failed to clean up delayed start ${d.config.name}: ${w}`)}g.error("manager",`Delayed start failed: ${d.config.name}: ${_}`)}})}if(this.instances.length>0){const d=Math.max(0,this.instances.length-u);g.info("manager",`${d}/${c.length} agent(s) running now`)}u>0&&g.warn("manager",`${u} agent(s) still connecting in background`)}if(this.instances.length===0&&c.length>0)throw new Error("All agent configurations failed to start");await this.ensureAuxServices()}async ensureAuxServices(){if(this.stopping||this.configMap.size===0)return;const e=[...this.configMap.values()].map(n=>({apiKey:n.aibot.apiKey,wsUrl:n.aibot.url}));if(!this.upgradeChecker&&(this.upgradeChecker=new fe(e,()=>this.describeAnyPendingOrBackgroundWork(),()=>this.closeAllLifecycleAdmission(),{requestGracefulShutdown:this.options.requestGracefulShutdown,reopenLifecycleAdmission:()=>this.reopenAllLifecycleAdmission(),isConnected:()=>this.instances.length===0||this.instances.some(n=>n.getStatus().wsConnected),onTransactionSettled:this.options.onTransactionSettled}),await this.upgradeChecker.start(),this.stopping)){this.upgradeChecker.stop();return}this.skillSyncer||(this.skillSyncer=new _e(e,A.skills),this.skillSyncer.setOnSyncSuccess(()=>this.refreshOwnerLibrarySkills()),await this.skillSyncer.start(),this.stopping&&this.skillSyncer.stop())}refreshOwnerLibrarySkills(){const e=new Map,n=t=>{let s;try{s=t.skillsSyncGroupKey()}catch(c){g.warn("manager",`skillsSyncGroupKey failed for ${t.name}: ${c}`);return}const i=e.get(s)??[];i.push(t),e.set(s,i)};for(const t of this.instances)n(t);for(const t of this.sharedInstances.values())n(t);for(const[t,s]of e){let i=!1;const c=[];for(const o of s){if(i){c.push(o);continue}let u=!1;try{u=o.forceRefreshSkills(void 0,{force:!0,ownerLibrarySync:!0})===!0}catch(r){g.warn("manager",`owner library skills reporter threw group=${t} agent=${o.name}: ${r}`)}if(u){i=!0;continue}g.warn("manager",`owner library skills reporter failed group=${t} agent=${o.name}; trying next`)}if(!i){g.warn("manager",`owner library skills: no reporter succeeded group=${t}`);continue}for(const o of c)try{o.adoptSkillsWireHashFromDisk()}catch(u){g.warn("manager",`adoptSkillsWireHashFromDisk failed for ${o.name}: ${u}`)}}}async stop(){g.info("manager","Stopping all agents..."),this.stopping=!0;const e=Qe(),n=Date.now()+e,t=()=>Math.max(1,n-Date.now()),s=new Set([...this.instances,...this.sharedInstances.values(),...this.startingInstances]);for(const o of s)this.requestStartCancellation(o);const i=new Map([...s].map(o=>[o,this.stopInstance(o,void 0,e)])),c=[];this.upgradeChecker?.stop(),this.skillSyncer?.stop();try{await this.waitForLifecycleDrain([this.mutationTail,...this.shareSyncChains.values(),...i.values()],"manager shutdown lifecycle drain",t())}catch(o){c.push(o)}this.upgradeChecker?.stop(),this.skillSyncer?.stop(),this.shareSyncChains.clear(),await Promise.all([...[...this.instances].map(async o=>{try{await(i.get(o)??this.stopInstance(o,void 0,t())),this.retireInstance(o),this.instances=this.instances.filter(u=>u!==o),this.startingInstances.delete(o)}catch(u){c.push(u)}}),...[...this.sharedInstances.entries()].map(async([o,u])=>{try{await(i.get(u)??this.stopInstance(u,void 0,t())),this.sharedInstances.get(o)===u&&this.sharedInstances.delete(o),this.startingInstances.delete(u)}catch(r){c.push(r)}})]);try{await this.waitForLifecycleDrain([this.retryPendingPlatformDeletesUnlocked(n)],"platform-delete shutdown drain",t())}catch(o){c.push(o)}try{await this.waitForShutdownOperation(Promise.resolve().then(()=>this.globalConfigStore?.flush()),"global config shutdown flush",t())}catch(o){c.push(o)}if(await this.auditProducer.stop({timeoutMs:Math.min(5e3,t())}).catch(()=>!1),W(),c.length>0)throw new AggregateError(c,"Failed to stop all agents completely");g.info("manager","All stopped")}onShareSet(e,n,t){if(this.stopping||this.removingAgents.has(n.name)||!this.isActiveGeneration(e))return;this.sharedOwnersCache.save(n.name,t);const i=(this.shareSyncChains.get(n.name)??Promise.resolve()).catch(()=>{}).then(()=>{if(!(this.stopping||this.removingAgents.has(n.name))&&this.isActiveGeneration(e))return this.syncSharedInstances(n,t,e)});this.shareSyncChains.set(n.name,i)}async syncSharedInstances(e,n,t){if(this.stopping||this.removingAgents.has(e.name)||t&&!this.isActiveGeneration(t))return;const{toAdd:s,toRemove:i}=pe(e.name,this.sharedInstances.keys(),n);for(const c of s){if(this.stopping||this.removingAgents.has(e.name)||t&&!this.isActiveGeneration(t))break;const o=he(e.name,c);let u;try{const r=de(e,c);u=new F(r,this.globalConfigStore,this.auditProducer),u.setLifecycleBusyChecker(()=>this.describeAnyPendingOrBackgroundWork()),u.setLifecycleAdmissionCloser(()=>this.closeAllLifecycleAdmission()),u.setLifecycleAdmissionRestorer(()=>this.reopenAllLifecycleAdmission()),this.options.requestGracefulShutdown&&u.setDaemonShutdownRequester(this.options.requestGracefulShutdown),this.lifecycleAdmissionClosed&&u.closeLifecycleAdmission(),this.sharedInstances.set(o,u),await this.startTrackedInstance(u),u.forceRefreshSkills(void 0,{force:!0}),g.info("manager",`shared instance started: ${o}`)}catch(r){if(u&&!this.stopping&&!this.removingAgents.has(e.name))try{await this.stopInstance(u),this.sharedInstances.get(o)===u&&this.sharedInstances.delete(o)}catch(m){g.error("manager",`cleanup shared instance failed ${o}: ${m}`)}const l=r instanceof Error?r.message:String(r);/Auth failed/i.test(l)?(this.sharedOwnersCache.remove(e.name,c),g.warn("manager",`shared instance auth rejected, removed from cache: ${o}`)):g.error("manager",`start shared instance failed ${o}: ${r}`)}}for(const{key:c}of i){const o=this.sharedInstances.get(c);if(o)try{await this.stopInstance(o),this.sharedInstances.get(c)===o&&this.sharedInstances.delete(c),g.info("manager",`shared instance stopped: ${c}`)}catch(u){g.error("manager",`stop shared instance failed ${c}: ${u}`)}}}restoreCachedSharedInstances(e){const n=this.sharedOwnersCache.load(e.name);if(n.length===0)return;const t=this.instances.find(s=>s.name===e.name);!t||!this.isActiveGeneration(t)||(g.info("manager",`restoring ${n.length} cached shared instance(s) for ${e.name}`),this.onShareSet(t,e,n))}getAgentsStatus(){return this.instances.map(e=>e.getStatus())}async addAgent(e){return this.runMutation(()=>this.addAgentUnlocked(e),[String(e.name??"")])}async addAgentUnlocked(e){const n=this.toRuntimeInstanceConfig(e,{quiet:!0});if(this.instances.some(t=>t.name===n.name))throw new Error(`Agent "${n.name}" already exists`);this.configRepository.assertCanAdd(e),await this.addAgentInternal(e,"agents.json",{targetPersisted:!1});try{const t=this.configRepository.insert(e);this.rawConfigMap.set(e.name,t);const s=this.instances.find(c=>c.name===e.name),i=s?this.instanceGenerations.get(s):void 0;if(i){i.persisted=!0;const c=this.configMap.get(e.name);c&&this.restoreCachedSharedInstances(c)}}catch(t){try{await this.removeAgentInternal(e.name)}catch(s){throw new AggregateError([t,s],`Failed to persist and roll back agent "${e.name}"`)}throw t}await this.ensureAuxServices(),g.info("manager",`Added agent: ${e.name}`)}async addAgentInternal(e,n,t){const s=this.toRuntimeInstanceConfig(e);if(this.instances.some(i=>i.name===s.name))throw new Error(`Agent "${s.name}" already exists`);await this.startInstanceFromConfig(s,e,n,t)}createConfiguredInstance(e,n=!0){const t=new F(e,this.globalConfigStore,this.auditProducer);return this.instanceGenerations.set(t,{name:e.name,agentId:e.aibot.agentId,persisted:n,retired:!1,platformDeletedObserved:!1}),t.setUpgradeTrigger(()=>this.upgradeChecker?.triggerCheck()),this.options.requestGracefulShutdown&&t.setDaemonShutdownRequester(this.options.requestGracefulShutdown),t.setLifecycleBusyChecker(()=>this.describeAnyPendingOrBackgroundWork()),t.setLifecycleAdmissionCloser(()=>this.closeAllLifecycleAdmission()),t.setLifecycleAdmissionRestorer(()=>this.reopenAllLifecycleAdmission()),this.lifecycleAdmissionClosed&&t.closeLifecycleAdmission(),t.setInstallAgentHandler(s=>this.installAgent(s)),t.setRemoteAdminPorts({isEnabled:()=>this.remoteAdminGate?.()??!1,listInstallable:()=>this.listInstallable(),installAgent:s=>this.installAgent(s),getInstallProgress:s=>this.getInstallProgress(s),addAgent:s=>this.addAgent(s),probeAll:s=>this.probeAll(s)}),t.setShareSetHandler(s=>this.onShareSet(t,e,s)),t.setAgentDeletedHandler(()=>this.onAgentDeletedByPlatform(t)),t.setSkillSyncHandler(()=>this.skillSyncer?.triggerSync()),t.setProviderConfigHandler(s=>this.setAgentProvider(e.name,s)),t.setRelayStateApplyPorts({getLocalState:()=>this.relayStateApplier?.getLocalState(e.name)??{enabled:!1},applyEnable:(s,i)=>{const c=this.relayStateApplier;return c?c.applyEnable(e.name,s,i):Promise.reject(new Error("relay state applier is not configured"))},applyDisable:()=>{const s=this.relayStateApplier;return s?s.applyDisable(e.name):Promise.reject(new Error("relay state applier is not configured"))}}),t.setRelayEnvSettledHandler(()=>this.relayEnvSettledHandler?.()),t.setProviderResolver(()=>Y(this.getAgentProvider(e.name))),t}describeAnyPendingOrBackgroundWork(){return[...this.instances.flatMap(e=>e.describePendingOrBackgroundWork()),...[...this.sharedInstances.values()].flatMap(e=>e.describePendingOrBackgroundWork())]}closeAllLifecycleAdmission(){this.lifecycleAdmissionClosed=!0;for(const e of this.instances)e.closeLifecycleAdmission();for(const e of this.sharedInstances.values())e.closeLifecycleAdmission()}reopenAllLifecycleAdmission(){if(!this.stopping){this.lifecycleAdmissionClosed=!1;for(const e of this.instances)e.openLifecycleAdmission();for(const e of this.sharedInstances.values())e.openLifecycleAdmission()}}async prepareProviderRuntime(e,n){const t=this.resolveAgentProvider(n.provider);K(n.client_type)&&t?.base_url?.trim()&&t?.api_key?.trim()&&t?.model?.trim()&&await z(e.name,{baseUrl:t.base_url.trim(),apiKey:t.api_key.trim(),model:t.model.trim()})}async startInstanceFromConfig(e,n,t,s){if(this.stopping)throw Object.assign(new Error("manager is stopping"),{code:"MANAGER_STOPPING"});if(await this.prepareProviderRuntime(e,n),this.stopping)throw Object.assign(new Error("manager is stopping"),{code:"MANAGER_STOPPING"});const i=this.createConfiguredInstance(e,s.targetPersisted);this.instances.push(i);const c=this.startTrackedInstance(i),o=J(),u=await new Promise(p=>{let d=!1;const h=setTimeout(()=>{d||(d=!0,p({kind:"timeout"}))},o);c.then(()=>{d||(d=!0,clearTimeout(h),p({kind:"started"}))},_=>{d||(d=!0,clearTimeout(h),p({kind:"failed",error:_}))})}),r=this.instanceGenerations.get(i);if(r?.platformDeletedObserved&&!r.persisted){const p=new ge(`Agent "${e.name}" was deleted before its local configuration commit`);try{await this.stopInstance(i),this.retireInstance(i),this.instances=this.instances.filter(d=>d!==i),this.failedStartAgents.delete(e.name)}catch(d){throw this.failedStartAgents.add(e.name),new AggregateError([p,d],`Failed to clean up uncommitted deleted agent "${e.name}"`)}throw p}if(u.kind==="started"){this.configMap.set(e.name,e),this.rawConfigMap.set(e.name,{entry:n,file:t}),this.failedStartAgents.delete(e.name),s.targetPersisted&&this.restoreCachedSharedInstances(e),this.relayEnvSettledHandler?.();return}if(u.kind==="timeout"){this.configMap.set(e.name,e),this.rawConfigMap.set(e.name,{entry:n,file:t}),g.warn("manager",`Start pending for ${e.name}, continue retrying in background`),c.then(async()=>{if(!this.removingAgents.has(e.name)&&this.isActiveGeneration(i)){if(this.stopping){try{await this.stopInstance(i),this.instances=this.instances.filter(p=>p!==i)}catch(p){this.failedStartAgents.add(e.name),g.error("manager",`Failed to stop late-started ${e.name}: ${p}`)}return}this.failedStartAgents.delete(e.name),this.instanceGenerations.get(i)?.persisted&&this.restoreCachedSharedInstances(e),this.relayEnvSettledHandler?.(),g.info("manager",`Delayed start succeeded: ${e.name}`)}},async p=>{if(C(p)){this.onAgentDeletedByPlatform(i);return}if(!(this.stopping||this.removingAgents.has(e.name))&&this.isActiveGeneration(i)){try{await this.stopInstance(i),this.instances=this.instances.filter(d=>d!==i),this.failedStartAgents.delete(e.name)}catch(d){this.failedStartAgents.add(e.name),g.error("manager",`Failed to clean up delayed start ${e.name}: ${d}`)}g.error("manager",`Delayed start failed ${e.name}: ${p}`)}}).catch(p=>{g.error("manager",`Delayed start finalizer failed ${e.name}: ${p}`)});return}const l=u.error;C(l)&&this.onAgentDeletedByPlatform(i);let m;try{await this.stopInstance(i),this.instances=this.instances.filter(p=>p!==i),this.failedStartAgents.delete(e.name)}catch(p){m=p,this.failedStartAgents.add(e.name)}throw s.targetPersisted&&(this.configMap.set(e.name,e),this.rawConfigMap.set(e.name,{entry:n,file:t})),m!==void 0?new AggregateError([l,m],`Failed to start and clean up agent "${e.name}"`):l}async removeAgent(e){return this.runMutation(()=>this.removeAgentUnlocked(e),[e])}async removeAgentUnlocked(e){if(!(this.instances.some(t=>t.name===e)||this.rawConfigMap.has(e)||[...this.sharedInstances.keys()].some(t=>t.startsWith(`${e}#shared:`))))throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});this.configRepository.remove(e),await this.removeAgentInternal(e),g.info("manager",`Removed agent: ${e}`),this.relayEnvSettledHandler?.()}async removeAgentInternal(e,n){this.removingAgents.add(e);try{const t=()=>n?.deadlineMs===void 0?$():Math.max(1,n.deadlineMs-Date.now()),s=this.instances.findIndex(p=>p.name===e),i=s===-1?void 0:this.instances[s],c=`${e}#shared:`,o=n?.keepShareCache?void 0:{dropPendingEvents:!0},u=new Map;i&&(this.requestStartCancellation(i),this.startingInstances.has(i)&&u.set(i,this.stopInstance(i,o,t())));for(const[p,d]of this.sharedInstances)p.startsWith(c)&&(this.requestStartCancellation(d),this.startingInstances.has(d)&&u.set(d,this.stopInstance(d,o,t())));const r=this.shareSyncChains.get(e);this.shareSyncChains.delete(e);const l=[];try{await this.waitForLifecycleDrain([...r?[r]:[],...u.values()],`agent "${e}" shared lifecycle drain`,t())}catch(p){l.push(p)}const m=[];for(const[p,d]of[...this.sharedInstances.entries()])p.startsWith(c)&&m.push((u.get(d)??this.stopInstance(d,o,t())).then(()=>{this.sharedInstances.get(p)===d&&this.sharedInstances.delete(p),this.startingInstances.delete(d)}).catch(h=>{l.push(h),g.error("manager",`stop shared instance failed ${p}: ${h}`)}));if(i)try{await(u.get(i)??this.stopInstance(i,o,t())),this.retireInstance(i);const p=this.instances.indexOf(i);p!==-1&&this.instances.splice(p,1),this.startingInstances.delete(i)}catch(p){l.push(p)}if(await Promise.all(m),!n?.keepShareCache){const p=Q(e).catch(()=>{});if(n?.deadlineMs===void 0)await p;else try{await this.waitForLifecycleDrain([p],`agent "${e}" relay shutdown`,t())}catch(d){l.push(d)}}if(l.length>0)throw new AggregateError(l,`Failed to stop agent "${e}" completely`);this.configMap.delete(e),this.rawConfigMap.delete(e),this.failedStartAgents.delete(e),n?.keepShareCache||this.sharedOwnersCache.delete(e)}finally{this.removingAgents.delete(e)}}onAgentDeletedByPlatform(e){const n=this.instanceGenerations.get(e);if(!n||(n.platformDeletedObserved=!0,this.requestStartCancellation(e),!n.persisted))return;const t={source:e,name:n.name,agentId:n.agentId};this.pendingPlatformDeletes.set(e,t),!this.stopping&&(this.deletedAgentCleanups.has(e)||(this.deletedAgentCleanups.add(e),this.runMutation(async()=>{}).catch(()=>{}).finally(()=>{this.deletedAgentCleanups.delete(e)})))}async retryPendingPlatformDeletesUnlocked(e){if(this.pendingPlatformDeletes.size!==0)for(const[n,t]of[...this.pendingPlatformDeletes])try{await this.cleanupPlatformDeletedUnlocked(t,e),this.pendingPlatformDeletes.delete(n)}catch(s){g.error("manager",`cleanup for deleted agent "${t.name}" failed: ${s}`)}}async cleanupPlatformDeletedUnlocked(e,n){const{source:t,name:s,agentId:i}=e,c=this.instances.find(m=>m.name===s),o=c?this.instanceGenerations.get(c)?.agentId??String(this.rawConfigMap.get(s)?.entry.agent_id??"").trim():void 0;if(c&&c!==t&&o!==i)return;g.warn("manager",`Agent "${s}" \u5DF2\u5728\u5E73\u53F0\u5220\u9664\uFF0C\u505C\u6B62\u5B9E\u4F8B\u5E76\u6E05\u7406\u672C\u5730\u914D\u7F6E`);const u=this.rawConfigMap.get(s),r=this.configMap.get(s),l=[];if(c&&o===i)try{await this.removeAgentInternal(s,{deadlineMs:n})}catch(m){l.push(m)}try{const m=this.configRepository.scan();if(m.fileErrors.length>0){const h=m.fileErrors.map(_=>`${_.file}: ${_.error}`).join("; ");throw new Error(`cannot inspect deleted agent generation [${h}]`)}const p=m.entries.filter(h=>String(h.entry?.name??"").trim()===s);if(!p.some(h=>String(h.entry?.agent_id??"").trim()!==i)&&p.length>0&&this.configRepository.remove(s),l.length===0){const h=String(this.rawConfigMap.get(s)?.entry.agent_id??"").trim();(!h||h===i)&&(this.rawConfigMap.delete(s),this.configMap.delete(s),this.failedStartAgents.delete(s))}}catch(m){l.push(m),u&&this.rawConfigMap.set(s,u),r&&this.configMap.set(s,r)}if(l.length>0)throw new AggregateError(l,`Failed to fully clean up deleted agent "${s}"`);g.info("manager",`Agent "${s}" \u5DF2\u5728\u5E73\u53F0\u5220\u9664\uFF0C\u672C\u5730\u914D\u7F6E\u5DF2\u6E05\u7406`)}isAgentBusy(e){return this.instances.find(t=>t.name===e)?.hasPendingWork()??!1}getAgentsWithStaleRelayEnv(){return this.instances.filter(e=>e.hasStaleRelayEnv()).map(e=>e.name)}markAgentRelayEnvStale(e){const n=this.instances.find(t=>t.name===e);return n?(n.markRelayEnvStale(),!0):!1}relayEnvSettledHandler;setRelayEnvSettledHandler(e){this.relayEnvSettledHandler=e}remoteAdminGate;setRemoteAdminGate(e){this.remoteAdminGate=e}relayStateApplier;setRelayStateApplier(e){this.relayStateApplier=e}reportRelayStateLocalChange(e){this.instances.find(t=>t.name===e)?.reportRelayStateLocalChange()}setRelaySecretResolver(e){this.relaySecretResolver=e}resolveAgentProvider(e){if(!e?.api_key_ref?.trim())return e;const n=e.api_key_ref.trim(),t=this.relaySecretResolver?.(n);if(!t)throw Object.assign(new Error(`relay credential "${n}" is unavailable`),{code:"RELAY_SECRET_UNAVAILABLE"});return{...e,api_key:t}}toRuntimeInstanceConfig(e,n={}){const t=this.resolveAgentProvider(e.provider);return ze(t===e.provider?e:{...e,provider:t},n)}getAgentProvider(e){return this.resolveAgentProvider(this.rawConfigMap.get(e)?.entry.provider)}async restartAgent(e){return this.runMutation(()=>this.restartAgentUnlocked(e),[e])}async restartAgentUnlocked(e){const n=this.rawConfigMap.get(e);if(!n)throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});await this.replaceInstance(e,n.entry,n.file),g.info("manager",`Restarted agent: ${e}`)}async setAgentProvider(e,n,t){return this.runMutation(()=>this.setAgentProviderUnlocked(e,n,t),[e])}async setAgentProviderUnlocked(e,n,t){if(!this.rawConfigMap.get(e))throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});const i=n?.api_key_ref?.trim()?Object.fromEntries(Object.entries(n).filter(([r])=>r!=="api_key")):n,c=this.configRepository.updateExisting(e,r=>{const l={...r};return i===void 0?delete l.provider:l.provider=i,this.toRuntimeInstanceConfig(l,{quiet:!0}),l}),o=c.entry;this.rawConfigMap.set(e,c);const u=String(o.client_type??"").trim().toLowerCase();try{K(u)&&(n?.base_url?.trim()&&n?.api_key?.trim()&&n?.model?.trim()?await z(e,{baseUrl:n.base_url.trim(),apiKey:n.api_key.trim(),model:n.model.trim()}):await Q(e))}catch(r){throw this.failedStartAgents.add(e),this.instances.find(l=>l.name===e)?.markRelayEnvStale(),r}if(t?.deferRestart){this.instances.find(r=>r.name===e)?.markRelayEnvStale(),g.info("manager",`Updated provider config for agent (deferred restart): ${e}`);return}try{await this.replaceInstance(e,o,c.file)}catch(r){throw this.failedStartAgents.add(e),this.instances.find(l=>l.name===e)?.markRelayEnvStale(),r}g.info("manager",`Updated provider config for agent: ${e}`)}hasDirectRelayConfig(e){const n=this.rawConfigMap.get(e)?.entry.provider;return!!(n?.api_key_ref?.trim()||n?.api_key?.trim())}getDirectRelayConfigAgentNames(){return[...this.rawConfigMap.entries()].filter(([,e])=>!!e.entry.provider&&(Ee(e.entry.client_type)||["claude","codex"].includes(String(e.entry.client_type??"").trim().toLowerCase()))).map(([e])=>e)}async fetchRelayCredential(e,n){const t=this.instances.find(s=>s.name===e);if(!t)throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});return t.fetchRelayCredential(n)}async clearDirectProviderConfigs(){const e=this.getDirectRelayConfigAgentNames(),n=[];for(const t of e)try{await this.setAgentProvider(t,void 0,{deferRestart:this.isAgentBusy(t)}),n.push(t)}catch(s){g.warn("manager",`Failed to clear provider config for "${t}": ${s instanceof Error?s.message:String(s)}`)}return n}async replaceInstance(e,n,t){const s=this.toRuntimeInstanceConfig(n);try{await this.removeAgentInternal(e,{keepShareCache:!0})}catch(o){throw this.failedStartAgents.add(e),o}const i=String(n.agent_id??"").trim();if([...this.pendingPlatformDeletes.values()].some(o=>o.name===e&&o.agentId===i))throw await this.retryPendingPlatformDeletesUnlocked(),Object.assign(new Error(`Agent "${e}" was permanently deleted while its instance was stopping`),{code:"AGENT_DELETE_PENDING"});try{await this.startInstanceFromConfig(s,n,t,{targetPersisted:!0})}catch(o){throw this.configMap.set(e,s),this.rawConfigMap.set(e,{entry:n,file:t}),o}}async reload(){return this.runMutation(()=>this.doReload())}async doReload(){if(this.stopping)throw Object.assign(new Error("manager is stopping"),{code:"RELOAD_UNSAFE"});const{entries:e,fileErrors:n,skipped:t}=this.configRepository.scan();for(const r of t)g.info("manager",`reload: skipped ${r}: not an agent config (no "agents" key)`);if(n.length>0){const r=n.map(l=>`${l.file}: ${l.error}`).join("; ");throw Object.assign(new Error(`reload aborted: ${n.length} config file(s) failed to parse [${r}]`),{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:r}of e)try{this.toRuntimeInstanceConfig(r,{quiet:!0})}catch(l){const m=r?.name,p=typeof m=="string"&&m.trim()?m:"<unknown>";s.push({name:p,error:l instanceof Error?l.message:String(l)})}if(s.length>0){const r=s.map(l=>`${l.name}: ${l.error}`).join("; ");throw Object.assign(new Error(`reload aborted: ${s.length} agent config(s) invalid [${r}]`),{code:"RELOAD_UNSAFE"})}const i=new Map,c=new Set([...this.pendingPlatformDeletes.values()].map(r=>r.name));for(const r of e){const l=String(r.entry?.name??"").trim();l&&(c.has(l)||(i.has(l)&&g.warn("manager",`reload: duplicate agent name "${l}", last one wins`),i.set(l,r)))}const o={added:[],removed:[],restarted:[],unchanged:[],failed:[]},u=new Set([...this.rawConfigMap.keys()].filter(r=>!c.has(r)));for(const r of u)if(!i.has(r))try{await this.removeAgentInternal(r),o.removed.push(r)}catch(l){o.failed.push({name:r,error:l instanceof Error?l.message:String(l)})}for(const[r,l]of i){const m=this.rawConfigMap.get(r);if(m)if(!Ve(m.entry,l.entry)||!this.instances.some(p=>p.name===r)||this.failedStartAgents.has(r))try{await this.replaceInstance(r,l.entry,l.file),o.restarted.push(r)}catch(p){o.failed.push({name:r,error:p instanceof Error?p.message:String(p)})}else this.rawConfigMap.set(r,l),o.unchanged.push(r);else try{await this.addAgentInternal(l.entry,l.file,{targetPersisted:!0}),o.added.push(r)}catch(p){o.failed.push({name:r,error:p instanceof Error?p.message:String(p)})}}return g.info("manager",`reload done: +${o.added.length} -${o.removed.length} ~${o.restarted.length} =${o.unchanged.length} !${o.failed.length}`),await this.ensureAuxServices(),o}async checkUpgrade(){return this.upgradeChecker?this.upgradeChecker.checkForUpdate():{available:!1}}triggerUpgrade(){this.upgradeChecker?.triggerCheck()}async probeAll(e={}){return Xe(this.instances,e)}async probeOne(e,n={}){return Ye(this.instances,e,n)}listInstallable(){return this.installer.listInstallable()}async installAgent(e){const n=await this.installer.install(e);return Me(n),n}getInstallProgress(e){return this.installer.getProgress(e)??null}}async function Xe(a,e){const n=e.concurrency??4,t=Date.now(),s=new Array(a.length);await new Promise(r=>{let l=0,m=0;const p=a.length;if(p===0){r();return}function d(w){const y=a[w];y.probe(e).then(S=>{s[w]=S,h()},S=>{s[w]={agent_name:y.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:S?.message??String(S)}},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}},h()})}function h(){m++,l<p?d(l++):m===p&&r()}const _=Math.min(n,p);for(let w=0;w<_;w++)d(l++)});const i=s.filter(r=>r.status==="healthy").length,c=s.filter(r=>r.status==="degraded").length,o=s.filter(r=>r.status==="unavailable").length,u=await V();return{ok:i===s.length&&s.length>0,total:s.length,healthy:i,degraded:c,unavailable:o,installed_clients:u,agents:s,probed_at:t,duration_ms:Date.now()-t}}async function Ye(a,e,n){const t=a.find(s=>s.name===e);if(!t)throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});return t.probe(n)}export{Et as Manager,Y as normalizeAgentProviderForRuntime,V as probeInstalledClientCommands,Xe as probeInstances,Ye as probeOneInstance};
1
+ import{join as b}from"node:path";import{homedir as H}from"node:os";import{AgentInstance as F}from"./bridge/bridge.js";import{isCwdLockedClientType as de,resolveStaticCwd as pe}from"./bridge/cwd-policy.js";import{buildSharedInstanceConfig as he,diffSharedOwners as ue,sharedInstanceKey as ge,SharedOwnersCache as me}from"./manager-share-config.js";import{AgentDeletedError as fe,isAgentDeletedError as E}from"./core/aibot/errors.js";import{GRIX_PATHS as A,log as g}from"./core/log/index.js";import{resolveClientVersion as _e}from"./core/util/client-version.js";import{UpgradeChecker as we}from"./core/upgrade/upgrade-checker.js";import{SkillSyncer as ye}from"./core/skill-sync/skill-syncer.js";import{AgentGlobalConfigStore as Ae}from"./core/persistence/agent-global-config-store.js";import{resolveSkillScanMode as Se}from"./adapter/claude/skill-scanner.js";import{buildWireSkills as ve}from"./core/skill-sync/sync-state.js";import{buildLibrarySkills as be}from"./core/skill-sync/library-skills.js";import{AUDIT_LOCAL_ACTION_TYPES as ke}from"./core/aibot/types.js";import{buildReportedSkills as Ie,logDefaultSkillsCheck as Ce,cleanupProjectedSkills as W}from"./default-skills/index.js";import{buildProviderEnv as Ee,supportsKiroCwRelay as K,supportsNonMitmRelayConfig as $e}from"./core/config/provider-env.js";import{resolveHermesHomeDir as Pe}from"./adapter/shared/native-provider-config.js";import{ensureKiroRelay as z,stopKiroRelay as Q}from"./core/proxy/kiro-relay.js";import{resolveCopilotCommand as Te}from"./core/runtime/copilot-resolve.js";import{resolveCursorCommand as Me}from"./core/runtime/cursor-resolve.js";import{probeInstalledClientCommands as V}from"./core/probe/installed-clients.js";import{AgentInstaller as xe}from"./core/installer/installer.js";import{reportInstallFailure as De}from"./core/observability/sentry.js";import{createDefaultAuditProducer as Re}from"./audit/runtime/default-audit-recorder.js";import{normalizeCodexConfigOverrides as Oe}from"./adapter/codex/config-overrides.js";import{ACP_NEW_SESSION_RESERVED_KEYS as je}from"./protocol/acp-client.js";import{resolveCodexDirectHome as Le}from"./adapter/codex/direct-home.js";import{AgentConfigRepository as X}from"./core/config/agent-config-repository.js";function Y(a){if(a)return{...a.provider_id?.trim()?{providerId:a.provider_id.trim()}:{},...a.base_url?.trim()?{baseUrl:a.base_url.trim()}:{},...a.quota_base_url?.trim()?{quotaBaseUrl:a.quota_base_url.trim()}:{},...a.api_key?.trim()?{apiKey:a.api_key.trim()}:{},...a.model?.trim()?{model:a.model.trim()}:{},...a.fast_model?.trim()?{fastModel:a.fast_model.trim()}:{},...a.effort?.trim()?{effort:a.effort.trim()}:{}}}const Ne=8e3,Ge=1e4,qe=8e3;function Ue(a,e){switch(a){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"omp":return{adapterType:"pi",command:"omp",adapterHint:"omp/base",env:{PI_CODING_AGENT_DIR:b(H(),".omp","agent")}};case"cursor":return{adapterType:"cursor",command:Me()};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"qodercli":return{adapterType:"acp",command:"qodercli",args:["--yolo","--acp"],enableSessionBinding:!0};case"qoderclicn":return{adapterType:"acp",command:"qoderclicn",args:["--yolo","--acp"],enableSessionBinding:!0};case"mcode":return{adapterType:"acp",command:"mcode",args:["acp"],enableSessionBinding:!0};case"dim":return{adapterType:"acp",command:"dim",args:["acp"],enableSessionBinding:!0};case"opencode":return{adapterType:"opencode",command:"opencode",args:["serve"],enableSessionBinding:!0};case"deveco":return{adapterType:"opencode",command:"deveco",args:["serve"],adapterHint:"deveco/base",enableSessionBinding:!0};case"copilot":{const n=Te();return{adapterType:"acp",command:n.command,args:[...n.prefixArgs,"--acp"],enableSessionBinding:!0}}case"agy":return{adapterType:"agy",command:"agy",enableSessionBinding:!0};case"deepseek":return{adapterType:"deepseek-harness",command:"dsh",adapterHint:"deepseek/grix-bridge-v1",options:{integrationMode:"profile_bridge",dshProfile:"web",autoInstallBridge:!0,autoStartProfile:!0},enableSessionBinding:!0};case"hermes":return{adapterType:"acp",command:"hermes",args:["acp"],enableSessionBinding:!0};case"traecli":return{adapterType:"acp",command:"traecli",args:["acp","serve"],adapterHint:"traecli/base",options:{initial_mode:"bypass_permissions"},enableSessionBinding:!0};case"codebuddy":return{adapterType:"acp",command:"codebuddy",args:["--acp"],adapterHint:"codebuddy/base",enableSessionBinding:!0};case"grok":return{adapterType:"acp",command:"grok",args:["--no-auto-update","agent","--always-approve","stdio"],enableSessionBinding:!0};case"qwenpaw":return{adapterType:"acp",command:"qwenpaw",args:["acp"],enableSessionBinding:!0};case"zeroclaw":return{adapterType:"acp",command:"zeroclaw",args:["acp"],enableSessionBinding:!0};case"acp":{const n=typeof e?.command=="string"?e.command.trim():"";if(!n)throw new Error(`agent ${e?.name??"<unknown>"}: client_type=acp requires "command" (the ACP CLI executable) in the agent config; put its ACP subcommand or flag in "args"`);return{adapterType:"acp",command:n,adapterHint:"acp/base",enableSessionBinding:!0}}default:throw new Error(`Unsupported client_type: ${a}`)}}function k(a,e){return String(a??"").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 Be(a){return b(A.data,`session-bindings-${k(a,"unknown")}.json`)}function He(a,e){return b(A.data,`session-bindings-${k(a,e)}.json`)}function Fe(a,e){return b(A.data,`active-events-${k(a,e)}.json`)}function We(a){return b(A.data,`pending-events-${k(a,"unknown")}.json`)}function Ke(a,e){const n=k(a,"agent"),t=k(e,"unknown");return b(A.data,`terminal-outbox-${n}-${t}.json`)}function ze(...a){const e=[],n=new Set;for(const t of a)for(const s of t??[]){const i=String(s??"").trim(),c=i.toLowerCase();!i||n.has(c)||(n.add(c),e.push(i))}return e.length>0?e:void 0}function Qe(a,e){const n={claude:{maxConcurrent:1,maxQueued:99,queueTimeoutMs:0},codex:{maxConcurrent:1,maxQueued:99,queueTimeoutMs:0},cursor:{maxConcurrent:1,maxQueued:99,queueTimeoutMs:0},acp:{maxConcurrent:1,maxQueued:99,queueTimeoutMs:0},pi:{maxConcurrent:1,maxQueued:99,queueTimeoutMs:0},codewhale:{maxConcurrent:1,maxQueued:99,queueTimeoutMs:0},openhuman:{maxConcurrent:1,maxQueued:99,queueTimeoutMs:0},opencode:{maxConcurrent:1,maxQueued:99,queueTimeoutMs:0},agy:{maxConcurrent:1,maxQueued:99,queueTimeoutMs:0},"deepseek-harness":{maxConcurrent:1,maxQueued:99,queueTimeoutMs:0}},t=n[a]??n.acp;let s=e?.max_concurrent??t.maxConcurrent??1;return a==="acp"&&s>1&&(g.warn("manager",`ACP adapter supports only one active run at a time; clamping max_concurrent from ${s} to 1`),s=1),{maxConcurrent:s,maxQueued:e?.max_queued??t.maxQueued??99,queueTimeoutMs:e?.queue_timeout_ms??t.queueTimeoutMs??0,cancelableQueued:!0,cancelableRunning:!0}}function Ve(a,e={}){const n=_e(),t=String(a.client_type??"").trim().toLowerCase(),s=Ue(t,a),i=t==="deepseek"?a.dsh?.integration_mode??"profile_bridge":void 0,c=i==="embedded_jsonrpc",o=String(a.ws_url??"").trim(),u=typeof a.name=="string"?a.name:"",r=u.trim(),l="get_session_usage",m="get_rate_limits",p="get_agent_global_config",d=ke;if(!r)throw new Error("agent name is required");if(u!==r)throw new Error(`agent name must not contain leading or trailing whitespace: "${u}"`);if(!o)throw new Error(`agent ${a.name}: ws_url is required`);if(!a.agent_id?.trim())throw new Error(`agent ${a.name}: agent_id is required`);if(!a.api_key?.trim())throw new Error(`agent ${a.name}: api_key is required`);const h=s.adapterType,_=h==="acp",w=t==="qwen",y={...s.options??{}};if(h==="deepseek-harness"&&(y.integrationMode=i,y.dshProfile=a.dsh?.profile?.trim()||"web",y.autoInstallBridge=a.dsh?.auto_install_bridge??!c,y.autoStartProfile=a.dsh?.auto_start_profile??!c),h==="deepseek-harness"&&a.provider?.model?.trim()&&(y.defaultModel=a.provider.model.trim()),a.codex_config_overrides!==void 0){if(h!=="codex")throw new Error(`agent ${a.name}: codex_config_overrides is only supported for client_type=codex`);Oe(a.codex_config_overrides,`agent ${a.name}: codex_config_overrides`),y.codexConfigOverrides=a.codex_config_overrides}if(a.acp_new_session_params!==void 0){if(!_)throw new Error(`agent ${a.name}: acp_new_session_params is only supported for ACP-based client types`);const f=a.acp_new_session_params;if(typeof f!="object"||f===null||Array.isArray(f))throw new Error(`agent ${a.name}: acp_new_session_params must be a JSON object`);const v=Object.keys(f).find(le=>je.has(le));if(v)throw new Error(`agent ${a.name}: acp_new_session_params cannot set reserved field "${v}" (Grix owns cwd/mcpServers/sessionId/additionalDirectories on every session/new)`);y.newSessionParams=f}if(t==="zeroclaw"){const v=a.acp_new_session_params?.agentAlias;if(typeof v!="string"||!v.trim()||v.trim().startsWith("<REPLACE"))throw new Error(`agent ${a.name}: client_type=zeroclaw requires acp_new_session_params.agentAlias (create one with \`zeroclaw agents create <alias>\` and set it in the agent config); without it session/new fails with -32602 and the connector would silently respawn-retry forever`)}if(h==="codex"&&a.provider?.wire_api!==void 0){const f=a.provider;if(f.wire_api!=="responses"||!f.base_url?.trim()||!f.api_key?.trim()||!f.model?.trim()||!f.catalog_version?.trim()||!f.catalog_sha256?.trim()||!f.catalog_json?.trim())throw Object.assign(new Error(`agent ${a.name}: incomplete Codex direct relay config`),{code:"INVALID_CODEX_DIRECT_CONFIG"});const v=Le(A.data,a.name);y.codexHome=v,y.codexDirect={codexHome:v,config:{baseUrl:f.base_url.trim(),model:f.model.trim(),...f.reasoning_effort?.trim()?{reasoningEffort:f.reasoning_effort.trim()}:{},token:f.api_key.trim()},catalog:{version:f.catalog_version.trim(),sha256:f.catalog_sha256.trim(),json:f.catalog_json}}}const S=h==="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",l,m]}:null,Z=h==="claude"?{localActions:["session_control","set_mode","set_model","set_reasoning_effort","claude_interaction_reply","exec_approve","exec_reject","file_list","create_folder","thread_compact",l,m]}:null,T=w?{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",l],adapterHint:"qwen/base"}:null,M=h==="pi"?{adapterHint:"pi/base",capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_provider","set_model","get_context","file_list","create_folder",l,m]}:null,x=h==="openhuman"?{adapterHint:"openhuman/base",capabilities:["local_action_v1"],localActions:["session_control","set_model","file_list","create_folder"]}:null,D=h==="cursor"?{adapterHint:"cursor/base",capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","set_mode","get_context","file_list","create_folder","thread_compact","claude_interaction_reply",m]}:null,N=h==="codewhale"?{capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","file_list","create_folder",l]}:null,R=h==="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","claude_interaction_reply","file_list","create_folder"]}:null,O=h==="agy"?{adapterHint:"agy/base",capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","file_list","create_folder",m]}:null,j=h==="deepseek-harness"?{adapterHint:c?"deepseek/jsonrpc-v1":"deepseek/grix-bridge-v1",capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_provider","set_model","set_mode","set_thinking","set_reasoning_effort","set_preset","set_profile","create_profile",l,m,"exec_approve","exec_reject","permission_approve","permission_reject","file_list","create_folder","dsh_bridge_status","dsh_install_bridge","dsh_list_plugins","dsh_enable_plugin","dsh_disable_plugin","dsh_refresh_plugins","dsh_list_skills","dsh_enable_skill","dsh_disable_skill","dsh_list_mcp","dsh_list_tools","dsh_list_jobs","dsh_list_goals"]}:null,G=new Set(["hermes","reasonix","kimi","copilot","acp","qodercli","qoderclicn","mcode","dim","traecli","grok","qwenpaw","zeroclaw","codebuddy"]),ee=_&&!G.has(t),te=_&&!w?{localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","claude_interaction_reply","file_list","create_folder",...ee?[l]:[]]}:null,ne=t==="kiro"?{localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","file_list","create_folder","thread_compact",l,m]}:null,se=h==="codex"||h==="claude"||h==="opencode"||h==="deepseek-harness"||t==="gemini"?["session_control","set_model","set_mode",...h==="deepseek-harness"?["set_provider","set_thinking","set_reasoning_effort","set_preset","set_profile","create_profile","dsh_list_plugins","dsh_enable_plugin","dsh_disable_plugin","dsh_refresh_plugins","dsh_list_skills","dsh_enable_skill","dsh_disable_skill"]:[],...h==="claude"?["set_reasoning_effort"]:[]]:void 0,ae=h==="cursor"||h==="opencode"||h==="openhuman"||h==="agy"||_&&G.has(t)?[m]:[l,m];_&&y.raw_transport===void 0&&(y.raw_transport=t==="gemini");const re=`${t}/base`,C=Se(h,t),ie=C==="kiro"?void 0:process.cwd(),L=Y(a.provider),I={...process.env,...s.env,...a.env};if(C==="hermes"&&!I.HERMES_HOME?.trim()){const f=Ee(t,L);I.HERMES_HOME=Pe({...f,...I},a.name)}const q=Ie({mode:C,projectDir:ie,env:I}),U=q.filter(f=>f.source==="connector").map(f=>f.name);U.length>0&&!e.quiet&&g.info("manager",`[${a.name}] injecting connector skills: [${U.join(", ")}]`);const B=ve(q,A.skills);let oe=B.length>0?B:void 0;const ce=be({skillsDir:A.skills,mode:C,home:H(),env:I});return{name:r,adapterType:h,aibot:{url:o,agentId:a.agent_id,apiKey:a.api_key,clientType:t,clientVersion:n,adapterHint:j?.adapterHint??s.adapterHint??T?.adapterHint??M?.adapterHint??x?.adapterHint??D?.adapterHint??R?.adapterHint??O?.adapterHint??re,capabilities:j?.capabilities??S?.capabilities??N?.capabilities??M?.capabilities??x?.capabilities??D?.capabilities??R?.capabilities??O?.capabilities??T?.capabilities??["stream_chunk","local_action_v1","connector_upgrade"],localActions:ze(j?.localActions??S?.localActions??N?.localActions??Z?.localActions??M?.localActions??x?.localActions??D?.localActions??R?.localActions??O?.localActions??T?.localActions??ne?.localActions??te?.localActions??["exec_approve","exec_reject"],se,ae,["connector_rollback","connector_upgrade_push",p,"configure_gateway_provider","skill_upload","skill_delete","skill_enable","skill_disable","skill_refresh",...d]),skills:oe,librarySkills:ce,terminalOutboxPath:Ke(a.name,a.agent_id)},agent:{command:c?"dsh-jsonrpc-agent":s.command,args:[...s.args??[],...a.args??[]],env:s.env?{...s.env,...a.env}:a.env,clientType:t,...L?{provider:L}:{}},adapterOptions:y,acpAuthMethod:y.auth_method,acpInitialMode:y.initial_mode,acpMcpTools:y.acp_mcp_tools,promptTimeoutMs:a.prompt_timeout_ms,bindingsPath:Be(a.agent_id),legacyBindingsPath:He(a.name,a.agent_id),activeEventStorePath:Fe(a.name,a.agent_id),pendingEventStorePath:We(a.agent_id),...s.enableSessionBinding||_?{enableSessionBinding:!0}:{},...de(t)?{staticCwd:pe(a.cwd)}:{},...s.autoInjectArgs||t==="reasonix"&&a.provider?.model?.trim()?{autoInjectArgs:{...s.autoInjectArgs??{},...t==="reasonix"&&a.provider?.model?.trim()?{model:a.provider.model.trim()}:{}}}:{},poolMaxSize:a.pool?.maxSize??(h==="deepseek-harness"?4:void 0),poolIdleTimeoutMs:a.pool?.idleTimeoutMs,eventQueue:Qe(h,a.event_queue),logDir:A.log,providerBaseUrl:a.provider_base_url?.trim()||void 0,providerApiKey:a.provider_api_key?.trim()||void 0}}function J(){const a=process.env.GRIX_AGENT_STARTUP_WAIT_MS,e=Number(a);return Number.isFinite(e)&&e>=500?Math.floor(e):Ne}function $(){const a=process.env.GRIX_AGENT_STOP_WAIT_MS,e=Number(a);return Number.isFinite(e)&&e>=500?Math.floor(e):Ge}function Xe(){return Math.min($(),qe)}function P(a){if(a===null||typeof a!="object")return JSON.stringify(a)??"null";if(Array.isArray(a))return`[${a.map(P).join(",")}]`;const e=a;return`{${Object.keys(e).sort().map(t=>`${JSON.stringify(t)}:${P(e[t])}`).join(",")}}`}function Ye(a,e){return P(a)===P(e)}class Pt{options;instances=[];configMap=new Map;rawConfigMap=new Map;mutationTail=Promise.resolve();startingInstances=new Set;failedStartAgents=new Set;removingAgents=new Set;instanceGenerations=new WeakMap;sharedInstances=new Map;shareSyncChains=new Map;stopping=!1;lifecycleAdmissionClosed=!1;deletedAgentCleanups=new Set;pendingPlatformDeletes=new Map;upgradeChecker=null;skillSyncer=null;globalConfigStore;configRepository=new X(A.config);sharedOwnersCache=new me(A.data);installer=new xe;relaySecretResolver;auditProducer=Re();constructor(e={}){this.options=e}runMutation(e,n=[]){const t=this.mutationTail.catch(()=>{}).then(async()=>{if(this.stopping)throw Object.assign(new Error("manager is stopping"),{code:"MANAGER_STOPPING"});await this.retryPendingPlatformDeletesUnlocked();const s=new Set([...this.pendingPlatformDeletes.values()].map(c=>c.name)),i=n.find(c=>s.has(c.trim()));if(i)throw Object.assign(new Error(`Agent "${i}" is pending permanent platform-deletion cleanup`),{code:"AGENT_DELETE_PENDING"});return e()});return this.mutationTail=t.then(()=>{},()=>{}),t}async startTrackedInstance(e){this.startingInstances.add(e);try{await e.start()}finally{this.startingInstances.delete(e)}}requestStartCancellation(e){const n=e.requestStop;typeof n=="function"&&n.call(e)}async stopInstance(e,n,t=$()){let s;try{await Promise.race([e.stop(n),new Promise((i,c)=>{s=setTimeout(()=>{c(Object.assign(new Error(`Agent "${e.name}" stop timed out after ${t}ms`),{code:"AGENT_STOP_TIMEOUT"}))},t)})])}finally{s!==void 0&&clearTimeout(s)}}async waitForLifecycleDrain(e,n,t=$()){let s;try{await Promise.race([Promise.allSettled([...e]).then(()=>{}),new Promise((i,c)=>{s=setTimeout(()=>{c(Object.assign(new Error(`${n} timed out after ${t}ms`),{code:"LIFECYCLE_DRAIN_TIMEOUT"}))},t)})])}finally{s!==void 0&&clearTimeout(s)}}async waitForShutdownOperation(e,n,t){let s;try{return await Promise.race([e,new Promise((i,c)=>{s=setTimeout(()=>{c(Object.assign(new Error(`${n} timed out after ${t}ms`),{code:"SHUTDOWN_OPERATION_TIMEOUT"}))},t)})])}finally{s!==void 0&&clearTimeout(s)}}isActiveGeneration(e){const n=this.instanceGenerations.get(e);return!!n&&!n.retired&&this.instances.find(t=>t.name===n.name)===e}retireInstance(e){const n=this.instanceGenerations.get(e);n&&(n.retired=!0)}async start(e){return this.runMutation(()=>this.startUnlocked(e))}async startUnlocked(e){const n=e??A.config;this.configRepository=new X(n),g.info("manager",`Loading configs from ${n}`),Ce(),W(),this.globalConfigStore=new Ae(b(A.data,"agent-global-configs.json")),this.globalConfigStore.load();const{entries:t,fileErrors:s,skipped:i}=this.configRepository.scan();for(const r of s)g.error("manager",`Failed to load ${r.file}: ${r.error}`);for(const r of i)g.info("manager",`Skipped ${r}: not an agent config (no "agents" key)`);if(t.length===0&&s.length===0&&i.length===0){g.warn("manager",`No agent configurations found in ${n}; daemon idle (waiting for config). Add agents under this directory then run: grix-connector reload`);return}const c=[];let o=s.length;for(const{entry:r,file:l}of t)try{const m=this.toRuntimeInstanceConfig(r);c.push({config:m,entry:r,file:l}),g.info("manager",`Loaded ${m.name} (${m.adapterType??"acp"}) from ${l}`)}catch(m){const p=typeof r?.name=="string"?r.name:"<unknown>";g.error("manager",`Invalid agent config in ${l} (name=${p}): ${m}`),o++}let u=0;if(c.length>0){const r=J();g.info("manager",`Starting ${c.length} agent(s), startup wait=${r}ms`);const l=d=>{this.instances=this.instances.filter(h=>h!==d)},m=c.map(({config:d,entry:h,file:_})=>{const w=this.createConfiguredInstance(d);return this.instances.push(w),this.configMap.set(d.name,d),this.rawConfigMap.set(d.name,{entry:h,file:_}),{config:d,instance:w,startPromise:this.prepareProviderRuntime(d,h).then(()=>{if(this.stopping)throw this.requestStartCancellation(w),Object.assign(new Error("manager is stopping"),{code:"MANAGER_STOPPING"});return this.startTrackedInstance(w)})}}),p=await Promise.all(m.map(async d=>{const h=await new Promise(_=>{let w=!1;const y=setTimeout(()=>{w||(w=!0,_({kind:"timeout"}))},r);d.startPromise.then(()=>{w||(w=!0,clearTimeout(y),_({kind:"started"}))}).catch(S=>{w||(w=!0,clearTimeout(y),_({kind:"failed",error:S}))})});return{task:d,outcome:h}}));for(const{task:d,outcome:h}of p){if(h.kind==="started"){this.restoreCachedSharedInstances(d.config);continue}if(h.kind==="failed"){if(E(h.error)){this.onAgentDeletedByPlatform(d.instance);continue}try{await this.stopInstance(d.instance),l(d.instance),this.failedStartAgents.delete(d.config.name)}catch(_){this.failedStartAgents.add(d.config.name),g.error("manager",`Failed to clean up partially started ${d.config.name}: ${_}`)}g.error("manager",`Failed to start ${d.config.name}: ${h.error}`);continue}u++,g.warn("manager",`Startup pending for ${d.config.name}, continue retrying in background`),d.startPromise.then(()=>{this.stopping||this.removingAgents.has(d.config.name)||this.isActiveGeneration(d.instance)&&(g.info("manager",`Delayed start succeeded: ${d.config.name}`),this.restoreCachedSharedInstances(d.config))}).catch(async _=>{if(E(_)){this.onAgentDeletedByPlatform(d.instance);return}if(!(this.stopping||this.removingAgents.has(d.config.name))&&this.isActiveGeneration(d.instance)){try{await this.stopInstance(d.instance),l(d.instance),this.failedStartAgents.delete(d.config.name)}catch(w){this.failedStartAgents.add(d.config.name),g.error("manager",`Failed to clean up delayed start ${d.config.name}: ${w}`)}g.error("manager",`Delayed start failed: ${d.config.name}: ${_}`)}})}if(this.instances.length>0){const d=Math.max(0,this.instances.length-u);g.info("manager",`${d}/${c.length} agent(s) running now`)}u>0&&g.warn("manager",`${u} agent(s) still connecting in background`)}if(this.instances.length===0&&c.length>0)throw new Error("All agent configurations failed to start");await this.ensureAuxServices()}async ensureAuxServices(){if(this.stopping||this.configMap.size===0)return;const e=[...this.configMap.values()].map(n=>({apiKey:n.aibot.apiKey,wsUrl:n.aibot.url}));if(!this.upgradeChecker&&(this.upgradeChecker=new we(e,()=>this.describeAnyPendingOrBackgroundWork(),()=>this.closeAllLifecycleAdmission(),{requestGracefulShutdown:this.options.requestGracefulShutdown,reopenLifecycleAdmission:()=>this.reopenAllLifecycleAdmission(),isConnected:()=>this.instances.length===0||this.instances.some(n=>n.getStatus().wsConnected),onTransactionSettled:this.options.onTransactionSettled}),await this.upgradeChecker.start(),this.stopping)){this.upgradeChecker.stop();return}this.skillSyncer||(this.skillSyncer=new ye(e,A.skills),this.skillSyncer.setOnSyncSuccess(()=>this.refreshOwnerLibrarySkills()),await this.skillSyncer.start(),this.stopping&&this.skillSyncer.stop())}refreshOwnerLibrarySkills(){const e=new Map,n=t=>{let s;try{s=t.skillsSyncGroupKey()}catch(c){g.warn("manager",`skillsSyncGroupKey failed for ${t.name}: ${c}`);return}const i=e.get(s)??[];i.push(t),e.set(s,i)};for(const t of this.instances)n(t);for(const t of this.sharedInstances.values())n(t);for(const[t,s]of e){let i=!1;const c=[];for(const o of s){if(i){c.push(o);continue}let u=!1;try{u=o.forceRefreshSkills(void 0,{force:!0,ownerLibrarySync:!0})===!0}catch(r){g.warn("manager",`owner library skills reporter threw group=${t} agent=${o.name}: ${r}`)}if(u){i=!0;continue}g.warn("manager",`owner library skills reporter failed group=${t} agent=${o.name}; trying next`)}if(!i){g.warn("manager",`owner library skills: no reporter succeeded group=${t}`);continue}for(const o of c)try{o.adoptSkillsWireHashFromDisk()}catch(u){g.warn("manager",`adoptSkillsWireHashFromDisk failed for ${o.name}: ${u}`)}}}async stop(){g.info("manager","Stopping all agents..."),this.stopping=!0;const e=Xe(),n=Date.now()+e,t=()=>Math.max(1,n-Date.now()),s=new Set([...this.instances,...this.sharedInstances.values(),...this.startingInstances]);for(const o of s)this.requestStartCancellation(o);const i=new Map([...s].map(o=>[o,this.stopInstance(o,void 0,e)])),c=[];this.upgradeChecker?.stop(),this.skillSyncer?.stop();try{await this.waitForLifecycleDrain([this.mutationTail,...this.shareSyncChains.values(),...i.values()],"manager shutdown lifecycle drain",t())}catch(o){c.push(o)}this.upgradeChecker?.stop(),this.skillSyncer?.stop(),this.shareSyncChains.clear(),await Promise.all([...[...this.instances].map(async o=>{try{await(i.get(o)??this.stopInstance(o,void 0,t())),this.retireInstance(o),this.instances=this.instances.filter(u=>u!==o),this.startingInstances.delete(o)}catch(u){c.push(u)}}),...[...this.sharedInstances.entries()].map(async([o,u])=>{try{await(i.get(u)??this.stopInstance(u,void 0,t())),this.sharedInstances.get(o)===u&&this.sharedInstances.delete(o),this.startingInstances.delete(u)}catch(r){c.push(r)}})]);try{await this.waitForLifecycleDrain([this.retryPendingPlatformDeletesUnlocked(n)],"platform-delete shutdown drain",t())}catch(o){c.push(o)}try{await this.waitForShutdownOperation(Promise.resolve().then(()=>this.globalConfigStore?.flush()),"global config shutdown flush",t())}catch(o){c.push(o)}if(await this.auditProducer.stop({timeoutMs:Math.min(5e3,t())}).catch(()=>!1),W(),c.length>0)throw new AggregateError(c,"Failed to stop all agents completely");g.info("manager","All stopped")}onShareSet(e,n,t){if(this.stopping||this.removingAgents.has(n.name)||!this.isActiveGeneration(e))return;this.sharedOwnersCache.save(n.name,t);const i=(this.shareSyncChains.get(n.name)??Promise.resolve()).catch(()=>{}).then(()=>{if(!(this.stopping||this.removingAgents.has(n.name))&&this.isActiveGeneration(e))return this.syncSharedInstances(n,t,e)});this.shareSyncChains.set(n.name,i)}async syncSharedInstances(e,n,t){if(this.stopping||this.removingAgents.has(e.name)||t&&!this.isActiveGeneration(t))return;const{toAdd:s,toRemove:i}=ue(e.name,this.sharedInstances.keys(),n);for(const c of s){if(this.stopping||this.removingAgents.has(e.name)||t&&!this.isActiveGeneration(t))break;const o=ge(e.name,c);let u;try{const r=he(e,c);u=new F(r,this.globalConfigStore,this.auditProducer),u.setLifecycleBusyChecker(()=>this.describeAnyPendingOrBackgroundWork()),u.setLifecycleAdmissionCloser(()=>this.closeAllLifecycleAdmission()),u.setLifecycleAdmissionRestorer(()=>this.reopenAllLifecycleAdmission()),this.options.requestGracefulShutdown&&u.setDaemonShutdownRequester(this.options.requestGracefulShutdown),this.lifecycleAdmissionClosed&&u.closeLifecycleAdmission(),this.sharedInstances.set(o,u),await this.startTrackedInstance(u),u.forceRefreshSkills(void 0,{force:!0}),g.info("manager",`shared instance started: ${o}`)}catch(r){if(u&&!this.stopping&&!this.removingAgents.has(e.name))try{await this.stopInstance(u),this.sharedInstances.get(o)===u&&this.sharedInstances.delete(o)}catch(m){g.error("manager",`cleanup shared instance failed ${o}: ${m}`)}const l=r instanceof Error?r.message:String(r);/Auth failed/i.test(l)?(this.sharedOwnersCache.remove(e.name,c),g.warn("manager",`shared instance auth rejected, removed from cache: ${o}`)):g.error("manager",`start shared instance failed ${o}: ${r}`)}}for(const{key:c}of i){const o=this.sharedInstances.get(c);if(o)try{await this.stopInstance(o),this.sharedInstances.get(c)===o&&this.sharedInstances.delete(c),g.info("manager",`shared instance stopped: ${c}`)}catch(u){g.error("manager",`stop shared instance failed ${c}: ${u}`)}}}restoreCachedSharedInstances(e){const n=this.sharedOwnersCache.load(e.name);if(n.length===0)return;const t=this.instances.find(s=>s.name===e.name);!t||!this.isActiveGeneration(t)||(g.info("manager",`restoring ${n.length} cached shared instance(s) for ${e.name}`),this.onShareSet(t,e,n))}getAgentsStatus(){return this.instances.map(e=>e.getStatus())}async addAgent(e){return this.runMutation(()=>this.addAgentUnlocked(e),[String(e.name??"")])}async addAgentUnlocked(e){const n=this.toRuntimeInstanceConfig(e,{quiet:!0});if(this.instances.some(t=>t.name===n.name))throw new Error(`Agent "${n.name}" already exists`);this.configRepository.assertCanAdd(e),await this.addAgentInternal(e,"agents.json",{targetPersisted:!1});try{const t=this.configRepository.insert(e);this.rawConfigMap.set(e.name,t);const s=this.instances.find(c=>c.name===e.name),i=s?this.instanceGenerations.get(s):void 0;if(i){i.persisted=!0;const c=this.configMap.get(e.name);c&&this.restoreCachedSharedInstances(c)}}catch(t){try{await this.removeAgentInternal(e.name)}catch(s){throw new AggregateError([t,s],`Failed to persist and roll back agent "${e.name}"`)}throw t}await this.ensureAuxServices(),g.info("manager",`Added agent: ${e.name}`)}async addAgentInternal(e,n,t){const s=this.toRuntimeInstanceConfig(e);if(this.instances.some(i=>i.name===s.name))throw new Error(`Agent "${s.name}" already exists`);await this.startInstanceFromConfig(s,e,n,t)}createConfiguredInstance(e,n=!0){const t=new F(e,this.globalConfigStore,this.auditProducer);return this.instanceGenerations.set(t,{name:e.name,agentId:e.aibot.agentId,persisted:n,retired:!1,platformDeletedObserved:!1}),t.setUpgradeTrigger(()=>this.upgradeChecker?.triggerCheck()),this.options.requestGracefulShutdown&&t.setDaemonShutdownRequester(this.options.requestGracefulShutdown),t.setLifecycleBusyChecker(()=>this.describeAnyPendingOrBackgroundWork()),t.setLifecycleAdmissionCloser(()=>this.closeAllLifecycleAdmission()),t.setLifecycleAdmissionRestorer(()=>this.reopenAllLifecycleAdmission()),this.lifecycleAdmissionClosed&&t.closeLifecycleAdmission(),t.setInstallAgentHandler(s=>this.installAgent(s)),t.setRemoteAdminPorts({isEnabled:()=>this.remoteAdminGate?.()??!1,listInstallable:()=>this.listInstallable(),installAgent:s=>this.installAgent(s),getInstallProgress:s=>this.getInstallProgress(s),addAgent:s=>this.addAgent(s),probeAll:s=>this.probeAll(s)}),t.setShareSetHandler(s=>this.onShareSet(t,e,s)),t.setAgentDeletedHandler(()=>this.onAgentDeletedByPlatform(t)),t.setSkillSyncHandler(()=>this.skillSyncer?.triggerSync()),t.setProviderConfigHandler(s=>this.setAgentProvider(e.name,s)),t.setRelayStateApplyPorts({getLocalState:()=>this.relayStateApplier?.getLocalState(e.name)??{enabled:!1},applyEnable:(s,i)=>{const c=this.relayStateApplier;return c?c.applyEnable(e.name,s,i):Promise.reject(new Error("relay state applier is not configured"))},applyDisable:()=>{const s=this.relayStateApplier;return s?s.applyDisable(e.name):Promise.reject(new Error("relay state applier is not configured"))}}),t.setRelayEnvSettledHandler(()=>this.relayEnvSettledHandler?.()),t.setProviderResolver(()=>Y(this.getAgentProvider(e.name))),t}describeAnyPendingOrBackgroundWork(){return[...this.instances.flatMap(e=>e.describePendingOrBackgroundWork()),...[...this.sharedInstances.values()].flatMap(e=>e.describePendingOrBackgroundWork())]}closeAllLifecycleAdmission(){this.lifecycleAdmissionClosed=!0;for(const e of this.instances)e.closeLifecycleAdmission();for(const e of this.sharedInstances.values())e.closeLifecycleAdmission()}reopenAllLifecycleAdmission(){if(!this.stopping){this.lifecycleAdmissionClosed=!1;for(const e of this.instances)e.openLifecycleAdmission();for(const e of this.sharedInstances.values())e.openLifecycleAdmission()}}async prepareProviderRuntime(e,n){const t=this.resolveAgentProvider(n.provider);K(n.client_type)&&t?.base_url?.trim()&&t?.api_key?.trim()&&t?.model?.trim()&&await z(e.name,{baseUrl:t.base_url.trim(),apiKey:t.api_key.trim(),model:t.model.trim()})}async startInstanceFromConfig(e,n,t,s){if(this.stopping)throw Object.assign(new Error("manager is stopping"),{code:"MANAGER_STOPPING"});if(await this.prepareProviderRuntime(e,n),this.stopping)throw Object.assign(new Error("manager is stopping"),{code:"MANAGER_STOPPING"});const i=this.createConfiguredInstance(e,s.targetPersisted);this.instances.push(i);const c=this.startTrackedInstance(i),o=J(),u=await new Promise(p=>{let d=!1;const h=setTimeout(()=>{d||(d=!0,p({kind:"timeout"}))},o);c.then(()=>{d||(d=!0,clearTimeout(h),p({kind:"started"}))},_=>{d||(d=!0,clearTimeout(h),p({kind:"failed",error:_}))})}),r=this.instanceGenerations.get(i);if(r?.platformDeletedObserved&&!r.persisted){const p=new fe(`Agent "${e.name}" was deleted before its local configuration commit`);try{await this.stopInstance(i),this.retireInstance(i),this.instances=this.instances.filter(d=>d!==i),this.failedStartAgents.delete(e.name)}catch(d){throw this.failedStartAgents.add(e.name),new AggregateError([p,d],`Failed to clean up uncommitted deleted agent "${e.name}"`)}throw p}if(u.kind==="started"){this.configMap.set(e.name,e),this.rawConfigMap.set(e.name,{entry:n,file:t}),this.failedStartAgents.delete(e.name),s.targetPersisted&&this.restoreCachedSharedInstances(e),this.relayEnvSettledHandler?.();return}if(u.kind==="timeout"){this.configMap.set(e.name,e),this.rawConfigMap.set(e.name,{entry:n,file:t}),g.warn("manager",`Start pending for ${e.name}, continue retrying in background`),c.then(async()=>{if(!this.removingAgents.has(e.name)&&this.isActiveGeneration(i)){if(this.stopping){try{await this.stopInstance(i),this.instances=this.instances.filter(p=>p!==i)}catch(p){this.failedStartAgents.add(e.name),g.error("manager",`Failed to stop late-started ${e.name}: ${p}`)}return}this.failedStartAgents.delete(e.name),this.instanceGenerations.get(i)?.persisted&&this.restoreCachedSharedInstances(e),this.relayEnvSettledHandler?.(),g.info("manager",`Delayed start succeeded: ${e.name}`)}},async p=>{if(E(p)){this.onAgentDeletedByPlatform(i);return}if(!(this.stopping||this.removingAgents.has(e.name))&&this.isActiveGeneration(i)){try{await this.stopInstance(i),this.instances=this.instances.filter(d=>d!==i),this.failedStartAgents.delete(e.name)}catch(d){this.failedStartAgents.add(e.name),g.error("manager",`Failed to clean up delayed start ${e.name}: ${d}`)}g.error("manager",`Delayed start failed ${e.name}: ${p}`)}}).catch(p=>{g.error("manager",`Delayed start finalizer failed ${e.name}: ${p}`)});return}const l=u.error;E(l)&&this.onAgentDeletedByPlatform(i);let m;try{await this.stopInstance(i),this.instances=this.instances.filter(p=>p!==i),this.failedStartAgents.delete(e.name)}catch(p){m=p,this.failedStartAgents.add(e.name)}throw s.targetPersisted&&(this.configMap.set(e.name,e),this.rawConfigMap.set(e.name,{entry:n,file:t})),m!==void 0?new AggregateError([l,m],`Failed to start and clean up agent "${e.name}"`):l}async removeAgent(e){return this.runMutation(()=>this.removeAgentUnlocked(e),[e])}async removeAgentUnlocked(e){if(!(this.instances.some(t=>t.name===e)||this.rawConfigMap.has(e)||[...this.sharedInstances.keys()].some(t=>t.startsWith(`${e}#shared:`))))throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});this.configRepository.remove(e),await this.removeAgentInternal(e),g.info("manager",`Removed agent: ${e}`),this.relayEnvSettledHandler?.()}async removeAgentInternal(e,n){this.removingAgents.add(e);try{const t=()=>n?.deadlineMs===void 0?$():Math.max(1,n.deadlineMs-Date.now()),s=this.instances.findIndex(p=>p.name===e),i=s===-1?void 0:this.instances[s],c=`${e}#shared:`,o=n?.keepShareCache?void 0:{dropPendingEvents:!0},u=new Map;i&&(this.requestStartCancellation(i),this.startingInstances.has(i)&&u.set(i,this.stopInstance(i,o,t())));for(const[p,d]of this.sharedInstances)p.startsWith(c)&&(this.requestStartCancellation(d),this.startingInstances.has(d)&&u.set(d,this.stopInstance(d,o,t())));const r=this.shareSyncChains.get(e);this.shareSyncChains.delete(e);const l=[];try{await this.waitForLifecycleDrain([...r?[r]:[],...u.values()],`agent "${e}" shared lifecycle drain`,t())}catch(p){l.push(p)}const m=[];for(const[p,d]of[...this.sharedInstances.entries()])p.startsWith(c)&&m.push((u.get(d)??this.stopInstance(d,o,t())).then(()=>{this.sharedInstances.get(p)===d&&this.sharedInstances.delete(p),this.startingInstances.delete(d)}).catch(h=>{l.push(h),g.error("manager",`stop shared instance failed ${p}: ${h}`)}));if(i)try{await(u.get(i)??this.stopInstance(i,o,t())),this.retireInstance(i);const p=this.instances.indexOf(i);p!==-1&&this.instances.splice(p,1),this.startingInstances.delete(i)}catch(p){l.push(p)}if(await Promise.all(m),!n?.keepShareCache){const p=Q(e).catch(()=>{});if(n?.deadlineMs===void 0)await p;else try{await this.waitForLifecycleDrain([p],`agent "${e}" relay shutdown`,t())}catch(d){l.push(d)}}if(l.length>0)throw new AggregateError(l,`Failed to stop agent "${e}" completely`);this.configMap.delete(e),this.rawConfigMap.delete(e),this.failedStartAgents.delete(e),n?.keepShareCache||this.sharedOwnersCache.delete(e)}finally{this.removingAgents.delete(e)}}onAgentDeletedByPlatform(e){const n=this.instanceGenerations.get(e);if(!n||(n.platformDeletedObserved=!0,this.requestStartCancellation(e),!n.persisted))return;const t={source:e,name:n.name,agentId:n.agentId};this.pendingPlatformDeletes.set(e,t),!this.stopping&&(this.deletedAgentCleanups.has(e)||(this.deletedAgentCleanups.add(e),this.runMutation(async()=>{}).catch(()=>{}).finally(()=>{this.deletedAgentCleanups.delete(e)})))}async retryPendingPlatformDeletesUnlocked(e){if(this.pendingPlatformDeletes.size!==0)for(const[n,t]of[...this.pendingPlatformDeletes])try{await this.cleanupPlatformDeletedUnlocked(t,e),this.pendingPlatformDeletes.delete(n)}catch(s){g.error("manager",`cleanup for deleted agent "${t.name}" failed: ${s}`)}}async cleanupPlatformDeletedUnlocked(e,n){const{source:t,name:s,agentId:i}=e,c=this.instances.find(m=>m.name===s),o=c?this.instanceGenerations.get(c)?.agentId??String(this.rawConfigMap.get(s)?.entry.agent_id??"").trim():void 0;if(c&&c!==t&&o!==i)return;g.warn("manager",`Agent "${s}" \u5DF2\u5728\u5E73\u53F0\u5220\u9664\uFF0C\u505C\u6B62\u5B9E\u4F8B\u5E76\u6E05\u7406\u672C\u5730\u914D\u7F6E`);const u=this.rawConfigMap.get(s),r=this.configMap.get(s),l=[];if(c&&o===i)try{await this.removeAgentInternal(s,{deadlineMs:n})}catch(m){l.push(m)}try{const m=this.configRepository.scan();if(m.fileErrors.length>0){const h=m.fileErrors.map(_=>`${_.file}: ${_.error}`).join("; ");throw new Error(`cannot inspect deleted agent generation [${h}]`)}const p=m.entries.filter(h=>String(h.entry?.name??"").trim()===s);if(!p.some(h=>String(h.entry?.agent_id??"").trim()!==i)&&p.length>0&&this.configRepository.remove(s),l.length===0){const h=String(this.rawConfigMap.get(s)?.entry.agent_id??"").trim();(!h||h===i)&&(this.rawConfigMap.delete(s),this.configMap.delete(s),this.failedStartAgents.delete(s))}}catch(m){l.push(m),u&&this.rawConfigMap.set(s,u),r&&this.configMap.set(s,r)}if(l.length>0)throw new AggregateError(l,`Failed to fully clean up deleted agent "${s}"`);g.info("manager",`Agent "${s}" \u5DF2\u5728\u5E73\u53F0\u5220\u9664\uFF0C\u672C\u5730\u914D\u7F6E\u5DF2\u6E05\u7406`)}isAgentBusy(e){return this.instances.find(t=>t.name===e)?.hasPendingWork()??!1}getAgentsWithStaleRelayEnv(){return this.instances.filter(e=>e.hasStaleRelayEnv()).map(e=>e.name)}markAgentRelayEnvStale(e){const n=this.instances.find(t=>t.name===e);return n?(n.markRelayEnvStale(),!0):!1}relayEnvSettledHandler;setRelayEnvSettledHandler(e){this.relayEnvSettledHandler=e}remoteAdminGate;setRemoteAdminGate(e){this.remoteAdminGate=e}relayStateApplier;setRelayStateApplier(e){this.relayStateApplier=e}reportRelayStateLocalChange(e){this.instances.find(t=>t.name===e)?.reportRelayStateLocalChange()}setRelaySecretResolver(e){this.relaySecretResolver=e}resolveAgentProvider(e){if(!e?.api_key_ref?.trim())return e;const n=e.api_key_ref.trim(),t=this.relaySecretResolver?.(n);if(!t)throw Object.assign(new Error(`relay credential "${n}" is unavailable`),{code:"RELAY_SECRET_UNAVAILABLE"});return{...e,api_key:t}}toRuntimeInstanceConfig(e,n={}){const t=this.resolveAgentProvider(e.provider);return Ve(t===e.provider?e:{...e,provider:t},n)}getAgentProvider(e){return this.resolveAgentProvider(this.rawConfigMap.get(e)?.entry.provider)}async restartAgent(e){return this.runMutation(()=>this.restartAgentUnlocked(e),[e])}async restartAgentUnlocked(e){const n=this.rawConfigMap.get(e);if(!n)throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});await this.replaceInstance(e,n.entry,n.file),g.info("manager",`Restarted agent: ${e}`)}async setAgentProvider(e,n,t){return this.runMutation(()=>this.setAgentProviderUnlocked(e,n,t),[e])}async setAgentProviderUnlocked(e,n,t){if(!this.rawConfigMap.get(e))throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});const i=n?.api_key_ref?.trim()?Object.fromEntries(Object.entries(n).filter(([r])=>r!=="api_key")):n,c=this.configRepository.updateExisting(e,r=>{const l={...r};return i===void 0?delete l.provider:l.provider=i,this.toRuntimeInstanceConfig(l,{quiet:!0}),l}),o=c.entry;this.rawConfigMap.set(e,c);const u=String(o.client_type??"").trim().toLowerCase();try{K(u)&&(n?.base_url?.trim()&&n?.api_key?.trim()&&n?.model?.trim()?await z(e,{baseUrl:n.base_url.trim(),apiKey:n.api_key.trim(),model:n.model.trim()}):await Q(e))}catch(r){throw this.failedStartAgents.add(e),this.instances.find(l=>l.name===e)?.markRelayEnvStale(),r}if(t?.deferRestart){this.instances.find(r=>r.name===e)?.markRelayEnvStale(),g.info("manager",`Updated provider config for agent (deferred restart): ${e}`);return}try{await this.replaceInstance(e,o,c.file)}catch(r){throw this.failedStartAgents.add(e),this.instances.find(l=>l.name===e)?.markRelayEnvStale(),r}g.info("manager",`Updated provider config for agent: ${e}`)}hasDirectRelayConfig(e){const n=this.rawConfigMap.get(e)?.entry.provider;return!!(n?.api_key_ref?.trim()||n?.api_key?.trim())}getDirectRelayConfigAgentNames(){return[...this.rawConfigMap.entries()].filter(([,e])=>!!e.entry.provider&&($e(e.entry.client_type)||["claude","codex"].includes(String(e.entry.client_type??"").trim().toLowerCase()))).map(([e])=>e)}async fetchRelayCredential(e,n){const t=this.instances.find(s=>s.name===e);if(!t)throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});return t.fetchRelayCredential(n)}async clearDirectProviderConfigs(){const e=this.getDirectRelayConfigAgentNames(),n=[];for(const t of e)try{await this.setAgentProvider(t,void 0,{deferRestart:this.isAgentBusy(t)}),n.push(t)}catch(s){g.warn("manager",`Failed to clear provider config for "${t}": ${s instanceof Error?s.message:String(s)}`)}return n}async replaceInstance(e,n,t){const s=this.toRuntimeInstanceConfig(n);try{await this.removeAgentInternal(e,{keepShareCache:!0})}catch(o){throw this.failedStartAgents.add(e),o}const i=String(n.agent_id??"").trim();if([...this.pendingPlatformDeletes.values()].some(o=>o.name===e&&o.agentId===i))throw await this.retryPendingPlatformDeletesUnlocked(),Object.assign(new Error(`Agent "${e}" was permanently deleted while its instance was stopping`),{code:"AGENT_DELETE_PENDING"});try{await this.startInstanceFromConfig(s,n,t,{targetPersisted:!0})}catch(o){throw this.configMap.set(e,s),this.rawConfigMap.set(e,{entry:n,file:t}),o}}async reload(){return this.runMutation(()=>this.doReload())}async doReload(){if(this.stopping)throw Object.assign(new Error("manager is stopping"),{code:"RELOAD_UNSAFE"});const{entries:e,fileErrors:n,skipped:t}=this.configRepository.scan();for(const r of t)g.info("manager",`reload: skipped ${r}: not an agent config (no "agents" key)`);if(n.length>0){const r=n.map(l=>`${l.file}: ${l.error}`).join("; ");throw Object.assign(new Error(`reload aborted: ${n.length} config file(s) failed to parse [${r}]`),{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:r}of e)try{this.toRuntimeInstanceConfig(r,{quiet:!0})}catch(l){const m=r?.name,p=typeof m=="string"&&m.trim()?m:"<unknown>";s.push({name:p,error:l instanceof Error?l.message:String(l)})}if(s.length>0){const r=s.map(l=>`${l.name}: ${l.error}`).join("; ");throw Object.assign(new Error(`reload aborted: ${s.length} agent config(s) invalid [${r}]`),{code:"RELOAD_UNSAFE"})}const i=new Map,c=new Set([...this.pendingPlatformDeletes.values()].map(r=>r.name));for(const r of e){const l=String(r.entry?.name??"").trim();l&&(c.has(l)||(i.has(l)&&g.warn("manager",`reload: duplicate agent name "${l}", last one wins`),i.set(l,r)))}const o={added:[],removed:[],restarted:[],unchanged:[],failed:[]},u=new Set([...this.rawConfigMap.keys()].filter(r=>!c.has(r)));for(const r of u)if(!i.has(r))try{await this.removeAgentInternal(r),o.removed.push(r)}catch(l){o.failed.push({name:r,error:l instanceof Error?l.message:String(l)})}for(const[r,l]of i){const m=this.rawConfigMap.get(r);if(m)if(!Ye(m.entry,l.entry)||!this.instances.some(p=>p.name===r)||this.failedStartAgents.has(r))try{await this.replaceInstance(r,l.entry,l.file),o.restarted.push(r)}catch(p){o.failed.push({name:r,error:p instanceof Error?p.message:String(p)})}else this.rawConfigMap.set(r,l),o.unchanged.push(r);else try{await this.addAgentInternal(l.entry,l.file,{targetPersisted:!0}),o.added.push(r)}catch(p){o.failed.push({name:r,error:p instanceof Error?p.message:String(p)})}}return g.info("manager",`reload done: +${o.added.length} -${o.removed.length} ~${o.restarted.length} =${o.unchanged.length} !${o.failed.length}`),await this.ensureAuxServices(),o}async checkUpgrade(){return this.upgradeChecker?this.upgradeChecker.checkForUpdate():{available:!1}}triggerUpgrade(){this.upgradeChecker?.triggerCheck()}async probeAll(e={}){return Je(this.instances,e)}async probeOne(e,n={}){return Ze(this.instances,e,n)}listInstallable(){return this.installer.listInstallable()}async installAgent(e){const n=await this.installer.install(e);return De(n),n}getInstallProgress(e){return this.installer.getProgress(e)??null}}async function Je(a,e){const n=e.concurrency??4,t=Date.now(),s=new Array(a.length);await new Promise(r=>{let l=0,m=0;const p=a.length;if(p===0){r();return}function d(w){const y=a[w];y.probe(e).then(S=>{s[w]=S,h()},S=>{s[w]={agent_name:y.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:S?.message??String(S)}},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}},h()})}function h(){m++,l<p?d(l++):m===p&&r()}const _=Math.min(n,p);for(let w=0;w<_;w++)d(l++)});const i=s.filter(r=>r.status==="healthy").length,c=s.filter(r=>r.status==="degraded").length,o=s.filter(r=>r.status==="unavailable").length,u=await V();return{ok:i===s.length&&s.length>0,total:s.length,healthy:i,degraded:c,unavailable:o,installed_clients:u,agents:s,probed_at:t,duration_ms:Date.now()-t}}async function Ze(a,e,n){const t=a.find(s=>s.name===e);if(!t)throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});return t.probe(n)}export{Pt as Manager,Y as normalizeAgentProviderForRuntime,V as probeInstalledClientCommands,Je as probeInstances,Ze as probeOneInstance};
@@ -1 +1 @@
1
- import*as i from"node:net";const e={bind:"127.0.0.1",port:0,endpoint:"/mcp",sessionTimeoutMs:18e5,invokeTimeoutMs:3e4};function n(o){const u={bind:o?.bind??e.bind,port:o?.port??e.port,endpoint:o?.endpoint??e.endpoint,sessionTimeoutMs:o?.sessionTimeoutMs??e.sessionTimeoutMs,invokeTimeoutMs:o?.invokeTimeoutMs??e.invokeTimeoutMs,allowedOrigins:o?.allowedOrigins,allowedHosts:o?.allowedHosts};return s(u.bind),u.port!==0&&t(u.port),r(u.sessionTimeoutMs),u}function s(o){if(!o||!i.isIPv4(o)&&!i.isIPv6(o))throw new Error(`\u914D\u7F6E\u6821\u9A8C\u5931\u8D25: bind \u5730\u5740 "${o}" \u4E0D\u662F\u5408\u6CD5\u7684 IPv4 \u6216 IPv6 \u5730\u5740`)}function t(o){if(!Number.isInteger(o)||o<1||o>65535)throw new Error(`\u914D\u7F6E\u6821\u9A8C\u5931\u8D25: port \u503C ${o} \u4E0D\u5728\u5408\u6CD5\u8303\u56F4 1-65535 \u5185\u6216\u4E0D\u662F\u6574\u6570`)}function r(o){if(!Number.isInteger(o)||o<1e3||o>864e5)throw new Error(`\u914D\u7F6E\u6821\u9A8C\u5931\u8D25: session_timeout_ms \u503C ${o} \u4E0D\u5728\u5408\u6CD5\u8303\u56F4 1000-86400000 \u5185`)}export{n as createDefaultGatewayConfig};
1
+ import*as n from"node:net";const i={bind:"127.0.0.1",port:0,endpoint:"/mcp",sessionTimeoutMs:18e5,invokeTimeoutMs:3e4};function s(u){const e={bind:u?.bind??i.bind,port:u?.port??i.port,endpoint:u?.endpoint??i.endpoint,sessionTimeoutMs:u?.sessionTimeoutMs??i.sessionTimeoutMs,invokeTimeoutMs:u?.invokeTimeoutMs??i.invokeTimeoutMs,allowedOrigins:u?.allowedOrigins,allowedHosts:u?.allowedHosts};return t(e.bind),e.port!==0&&o(e.port),r(e.sessionTimeoutMs),e}function t(u){if(!u||!n.isIPv4(u)&&!n.isIPv6(u))throw new Error(`\u914D\u7F6E\u6821\u9A8C\u5931\u8D25: bind \u5730\u5740 "${u}" \u4E0D\u662F\u5408\u6CD5\u7684 IPv4 \u6216 IPv6 \u5730\u5740`)}function o(u){if(!Number.isInteger(u)||u<1||u>65535)throw new Error(`\u914D\u7F6E\u6821\u9A8C\u5931\u8D25: port \u503C ${u} \u4E0D\u5728\u5408\u6CD5\u8303\u56F4 1-65535 \u5185\u6216\u4E0D\u662F\u6574\u6570`)}function r(u){if(!Number.isInteger(u)||u<1e3||u>864e5)throw new Error(`\u914D\u7F6E\u6821\u9A8C\u5931\u8D25: session_timeout_ms \u503C ${u} \u4E0D\u5728\u5408\u6CD5\u8303\u56F4 1000-86400000 \u5185`)}export{s as createDefaultGatewayConfig};
@@ -1 +1 @@
1
- const r=3e4;class c{connectionManager;onDisconnected;bindings=new Map;constructor(n,i){this.connectionManager=n,this.onDisconnected=i}async bind(n,i){if(this.bindings.has(n))throw new Error(`Session ${n} is already bound to a connection`);const e=await this.connectWithTimeout(i),t=[],s=e.onDisconnected(()=>{this.removeBinding(n),this.onDisconnected(n)});return t.push(s),this.bindings.set(n,{sessionId:n,handle:e,subscriptions:t}),e}getHandle(n){return this.bindings.get(n)?.handle}unbind(n){const i=this.bindings.get(n);if(i){this.bindings.delete(n);for(const e of i.subscriptions)e();i.handle.disconnect()}}unbindAll(){const n=[...this.bindings.keys()];for(const i of n)this.unbind(i)}connectWithTimeout(n){return new Promise((i,e)=>{let t=!1;const s=setTimeout(()=>{t||(t=!0,e(new Error("Connection bind timeout after 30000ms")))},3e4);this.connectionManager.connect({agentId:n.agentId,apiKey:n.apiKey,url:n.wsUrl,clientType:n.clientType,capabilities:["agent_invoke"],adapterHint:`${n.clientType}/base`},{maxRetries:0}).then(o=>{t?o.disconnect():(t=!0,clearTimeout(s),i(o))}).catch(o=>{t||(t=!0,clearTimeout(s),e(o))})})}removeBinding(n){const i=this.bindings.get(n);if(i){this.bindings.delete(n);for(const e of i.subscriptions)e()}}}export{c as ConnectionBindingImpl};
1
+ const a=3e4;class c{connectionManager;onDisconnected;bindings=new Map;constructor(n,i){this.connectionManager=n,this.onDisconnected=i}async bind(n,i){if(this.bindings.has(n))throw new Error(`Session ${n} is already bound to a connection`);const e=await this.connectWithTimeout(i),t=[],s=e.onDisconnected(()=>{this.removeBinding(n),this.onDisconnected(n)});return t.push(s),this.bindings.set(n,{sessionId:n,handle:e,subscriptions:t}),e}getHandle(n){return this.bindings.get(n)?.handle}unbind(n){const i=this.bindings.get(n);if(i){this.bindings.delete(n);for(const e of i.subscriptions)e();i.handle.disconnect()}}unbindAll(){const n=[...this.bindings.keys()];for(const i of n)this.unbind(i)}connectWithTimeout(n){return new Promise((i,e)=>{let t=!1;const s=setTimeout(()=>{t||(t=!0,e(new Error("Connection bind timeout after 30000ms")))},3e4);this.connectionManager.connect({agentId:n.agentId,apiKey:n.apiKey,url:n.wsUrl,clientType:n.clientType,capabilities:["agent_invoke"],adapterHint:`${n.clientType}/base`},{maxRetries:0}).then(o=>{t?o.disconnect():(t=!0,clearTimeout(s),i(o))}).catch(o=>{t||(t=!0,clearTimeout(s),e(o))})})}removeBinding(n){const i=this.bindings.get(n);if(i){this.bindings.delete(n);for(const e of i.subscriptions)e()}}}export{c as ConnectionBindingImpl};
@@ -1 +1 @@
1
- import{toolCallToInvoke as i}from"../../core/mcp/tools.js";import{ToolRegistryImpl as l}from"./tool-registry.js";import{validateToolArgs as a}from"./tool-schemas.js";import{isEventTool as p,executeEventTool as d}from"./event-tool-executor.js";class y{registry;constructor(){this.registry=new l}async execute(t,e,r,n){if(!this.registry.hasTool(e))return this.errorResult(`\u672A\u77E5\u5DE5\u5177: ${e}`);const s=a(e,r);if(!s.valid)return this.errorResult(`\u53C2\u6570\u6821\u9A8C\u5931\u8D25: ${s.error}`);if(t.status!=="ready")return this.errorResult(`\u8FDE\u63A5\u4E0D\u53EF\u7528: \u5F53\u524D\u72B6\u6001\u4E3A ${t.status}`);if(p(e))return this.executeEventTool(t,e,r);const o=i(e,r);try{const u=await t.agentInvoke(o.action,o.params,n);return this.normalizeResult(u)}catch(u){const c=u instanceof Error?u.message:String(u);return c.toLowerCase().includes("timeout")?this.errorResult(`\u8C03\u7528\u8D85\u65F6: ${c}`):this.errorResult(`\u8C03\u7528\u5931\u8D25: ${c}`)}}normalizeResult(t){if(t==null||typeof t!="object")return this.successResult(t??null);const e=t,r=typeof e.code=="number"?e.code:0;if(r===0){const s="data"in e?e.data:null;return this.successResult(s??null)}const n=typeof e.msg=="string"?e.msg:"\u672A\u77E5\u9519\u8BEF";return this.errorResult(`\u4E0A\u6E38\u9519\u8BEF [code=${r}]: ${n}`)}successResult(t){return{content:[{type:"text",text:JSON.stringify(t)}],isError:!1}}errorResult(t){return{content:[{type:"text",text:t}],isError:!0}}async executeEventTool(t,e,r){return e==="grix_access_control"?this.executeAccessControl(t,r):d(t,e,r)}async executeAccessControl(t,e){const r=String(e.action??""),n={pair_approve:"pair_approve",pair_deny:"pair_deny",allow_sender:"sender_allow",remove_sender:"sender_remove",set_policy:"policy_set"}[r];if(!n)return this.errorResult(`\u672A\u77E5 access_control action: ${r}`);const s={};e.code!=null&&(s.code=e.code),e.sender_id!=null&&(s.sender_id=e.sender_id),e.policy!=null&&(s.policy=e.policy);try{const o=await t.agentInvoke("claude_access_control",{verb:n,payload:s},3e4);return this.successResult(o)}catch(o){const u=o instanceof Error?o.message:String(o);return this.errorResult(`access_control \u8C03\u7528\u5931\u8D25: ${u}`)}}}export{y as ToolExecutorImpl};
1
+ import{toolCallToInvoke as i}from"../../core/mcp/tools.js";import{ToolRegistryImpl as l}from"./tool-registry.js";import{validateToolArgs as a}from"./tool-schemas.js";import{isEventTool as p,executeEventTool as d}from"./event-tool-executor.js";class y{registry;constructor(){this.registry=new l}async execute(r,e,t,n){if(!this.registry.hasTool(e))return this.errorResult(`\u672A\u77E5\u5DE5\u5177: ${e}`);const s=a(e,t);if(!s.valid)return this.errorResult(`\u53C2\u6570\u6821\u9A8C\u5931\u8D25: ${s.error}`);if(r.status!=="ready")return this.errorResult(`\u8FDE\u63A5\u4E0D\u53EF\u7528: \u5F53\u524D\u72B6\u6001\u4E3A ${r.status}`);if(p(e))return this.executeEventTool(r,e,t);const o=i(e,t);try{const u=await r.agentInvoke(o.action,o.params,n);return this.normalizeResult(u)}catch(u){const c=u instanceof Error?u.message:String(u);return c.toLowerCase().includes("timeout")?this.errorResult(`\u8C03\u7528\u8D85\u65F6: ${c}`):this.errorResult(`\u8C03\u7528\u5931\u8D25: ${c}`)}}normalizeResult(r){if(r==null||typeof r!="object")return this.successResult(r??null);const e=r,t=typeof e.code=="number"?e.code:0;if(t===0){const s="data"in e?e.data:null;return this.successResult(s??null)}const n=typeof e.msg=="string"?e.msg:"\u672A\u77E5\u9519\u8BEF";return this.errorResult(`\u4E0A\u6E38\u9519\u8BEF [code=${t}]: ${n}`)}successResult(r){return{content:[{type:"text",text:JSON.stringify(r)}],isError:!1}}errorResult(r){return{content:[{type:"text",text:r}],isError:!0}}async executeEventTool(r,e,t){return e==="grix_access_control"?this.executeAccessControl(r,t):d(r,e,t)}async executeAccessControl(r,e){const t=String(e.action??""),n={pair_approve:"pair_approve",pair_deny:"pair_deny",allow_sender:"sender_allow",remove_sender:"sender_remove",set_policy:"policy_set"}[t];if(!n)return this.errorResult(`\u672A\u77E5 access_control action: ${t}`);const s={};e.code!=null&&(s.code=e.code),e.sender_id!=null&&(s.sender_id=e.sender_id),e.policy!=null&&(s.policy=e.policy);try{const o=await r.agentInvoke("claude_access_control",{verb:n,payload:s},3e4);return this.successResult(o)}catch(o){const u=o instanceof Error?o.message:String(o);return this.errorResult(`access_control \u8C03\u7528\u5931\u8D25: ${u}`)}}}export{y as ToolExecutorImpl};
@@ -1 +1 @@
1
- import{TOOLS as s,EVENT_TOOLS as t}from"../../core/mcp/tools.js";const e=new Set(["grix_query","grix_group","grix_message_send","grix_message_unsend","grix_admin"]),r=new Set(["grix_reply","grix_complete","grix_event_ack","grix_composing","grix_access_control","grix_status"]);class a{tools;toolMap;constructor(){this.tools=[...s.filter(o=>e.has(o.name)),...t.filter(o=>r.has(o.name))],this.toolMap=new Map(this.tools.map(o=>[o.name,o]))}getTools(){return this.tools}getTool(o){return this.toolMap.get(o)}hasTool(o){return this.toolMap.has(o)}}export{a as ToolRegistryImpl};
1
+ import{TOOLS as o,EVENT_TOOLS as s}from"../../core/mcp/tools.js";const e=new Set(["grix_query","grix_group","grix_message_send","grix_message_unsend","grix_admin"]),r=new Set(["grix_reply","grix_complete","grix_event_ack","grix_composing","grix_access_control","grix_status"]);class a{tools;toolMap;constructor(){this.tools=[...o.filter(t=>e.has(t.name)),...s.filter(t=>r.has(t.name))],this.toolMap=new Map(this.tools.map(t=>[t.name,t]))}getTools(){return this.tools}getTool(t){return this.toolMap.get(t)}hasTool(t){return this.toolMap.has(t)}}export{a as ToolRegistryImpl};
@@ -1 +1 @@
1
- const o={required:["action"],properties:{action:{type:"string",enum:["contact_search","session_search","message_history","message_search"]},id:{type:"string"},keyword:{type:"string",maxLength:200},limit:{type:"integer",minimum:1,maximum:100},offset:{type:"integer",minimum:0},sessionId:{type:"string"},beforeId:{type:"string"}}},a={required:["action"],properties:{action:{type:"string",enum:["create","detail","leave","add_members","remove_members","update_member_role","update_all_members_muted","update_member_speaking","dissolve"]},sessionId:{type:"string"},name:{type:"string",maxLength:128},memberIds:{type:"array",items:{type:"string"},maxItems:100},memberTypes:{type:"array",items:{type:"integer",enum:[1,2]}},memberId:{type:"string"},role:{type:"integer",enum:[1,2]},memberType:{type:"integer"},allMembersMuted:{type:"boolean"},isSpeakMuted:{type:"boolean"},canSpeakWhenAllMuted:{type:"boolean"}}},p={required:["sessionId","content"],properties:{sessionId:{type:"string"},content:{type:"string",maxLength:1e4},msgType:{type:"integer"},quotedMessageId:{type:"string"},threadId:{type:"string"}}},m={required:["sessionId","msgId"],properties:{sessionId:{type:"string"},msgId:{type:"string"}}},g={required:["action"],properties:{action:{type:"string",enum:["create_agent","list_categories","create_category","update_category","assign_category","rotate_api_key"]},agentName:{type:"string"},introduction:{type:"string"},isMain:{type:"boolean"},agentId:{type:"string"},categoryId:{type:"string"},name:{type:"string"},parentId:{type:"string"},sortOrder:{type:"integer"}}},y={required:["session_id","text"],properties:{event_id:{type:"string"},session_id:{type:"string"},text:{type:"string",maxLength:5e4},quoted_message_id:{type:"string"},is_final:{type:"boolean"}}},d={required:["event_id","status"],properties:{event_id:{type:"string"},status:{type:"string",enum:["responded","canceled","failed"]},msg:{type:"string",maxLength:500}}},c={required:["event_id"],properties:{event_id:{type:"string"},session_id:{type:"string"}}},l={required:["session_id","active"],properties:{session_id:{type:"string"},active:{type:"boolean"},event_id:{type:"string"}}},_={required:["action"],properties:{action:{type:"string",enum:["pair_approve","pair_deny","allow_sender","remove_sender","set_policy"]},code:{type:"string"},sender_id:{type:"string"},policy:{type:"string",enum:["allowlist","open","disabled"]}}},f={required:[],properties:{}},C={grix_query:o,grix_group:a,grix_message_send:p,grix_message_unsend:m,grix_admin:g,grix_reply:y,grix_complete:d,grix_event_ack:c,grix_composing:l,grix_access_control:_,grix_status:f};function B(u,t){const e=C[u];if(!e)return{valid:!1,error:`\u672A\u77E5\u5DE5\u5177: ${u}`};for(const i of e.required)if(t[i]===void 0||t[i]===null)return{valid:!1,error:`\u7F3A\u5C11\u5FC5\u586B\u53C2\u6570: ${i}`};for(const[i,r]of Object.entries(t)){if(r==null)continue;const n=e.properties[i];if(!n)continue;const s=$(i,r,n);if(s)return{valid:!1,error:s}}return{valid:!0}}function $(u,t,e){switch(e.type){case"string":if(typeof t!="string")return`\u53C2\u6570 ${u} \u7C7B\u578B\u9519\u8BEF: \u671F\u671B string\uFF0C\u5B9E\u9645 ${typeof t}`;if(e.maxLength!==void 0&&t.length>e.maxLength)return`\u53C2\u6570 ${u} \u8D85\u8FC7\u6700\u5927\u957F\u5EA6 ${e.maxLength}\uFF0C\u5B9E\u9645 ${t.length}`;if(e.enum&&!e.enum.includes(t))return`\u53C2\u6570 ${u} \u503C "${t}" \u4E0D\u5728\u5141\u8BB8\u8303\u56F4 [${e.enum.join(", ")}]`;break;case"integer":if(typeof t!="number"||!Number.isInteger(t))return`\u53C2\u6570 ${u} \u7C7B\u578B\u9519\u8BEF: \u671F\u671B integer\uFF0C\u5B9E\u9645 ${typeof t=="number"?"\u6D6E\u70B9\u6570":typeof t}`;if(e.minimum!==void 0&&t<e.minimum)return`\u53C2\u6570 ${u} \u503C ${t} \u5C0F\u4E8E\u6700\u5C0F\u503C ${e.minimum}`;if(e.maximum!==void 0&&t>e.maximum)return`\u53C2\u6570 ${u} \u503C ${t} \u5927\u4E8E\u6700\u5927\u503C ${e.maximum}`;if(e.enum&&!e.enum.includes(t))return`\u53C2\u6570 ${u} \u503C ${t} \u4E0D\u5728\u5141\u8BB8\u8303\u56F4 [${e.enum.join(", ")}]`;break;case"boolean":if(typeof t!="boolean")return`\u53C2\u6570 ${u} \u7C7B\u578B\u9519\u8BEF: \u671F\u671B boolean\uFF0C\u5B9E\u9645 ${typeof t}`;break;case"array":if(!Array.isArray(t))return`\u53C2\u6570 ${u} \u7C7B\u578B\u9519\u8BEF: \u671F\u671B array\uFF0C\u5B9E\u9645 ${typeof t}`;if(e.maxItems!==void 0&&t.length>e.maxItems)return`\u53C2\u6570 ${u} \u8D85\u8FC7\u6700\u5927\u5143\u7D20\u6570 ${e.maxItems}\uFF0C\u5B9E\u9645 ${t.length}`;if(e.items)for(let i=0;i<t.length;i++){const r=t[i];if(e.items.type==="string"&&typeof r!="string")return`\u53C2\u6570 ${u}[${i}] \u7C7B\u578B\u9519\u8BEF: \u671F\u671B string\uFF0C\u5B9E\u9645 ${typeof r}`;if(e.items.type==="integer"){if(typeof r!="number"||!Number.isInteger(r))return`\u53C2\u6570 ${u}[${i}] \u7C7B\u578B\u9519\u8BEF: \u671F\u671B integer\uFF0C\u5B9E\u9645 ${typeof r}`;if(e.items.enum&&!e.items.enum.includes(r))return`\u53C2\u6570 ${u}[${i}] \u503C ${r} \u4E0D\u5728\u5141\u8BB8\u8303\u56F4 [${e.items.enum.join(", ")}]`}}break}}export{B as validateToolArgs};
1
+ const o={required:["action"],properties:{action:{type:"string",enum:["contact_search","session_search","message_history","message_search"]},id:{type:"string"},keyword:{type:"string",maxLength:200},limit:{type:"integer",minimum:1,maximum:100},offset:{type:"integer",minimum:0},sessionId:{type:"string"},beforeId:{type:"string"}}},a={required:["action"],properties:{action:{type:"string",enum:["create","detail","leave","add_members","remove_members","update_member_role","update_all_members_muted","update_member_speaking","dissolve"]},sessionId:{type:"string"},name:{type:"string",maxLength:128},memberIds:{type:"array",items:{type:"string"},maxItems:100},memberTypes:{type:"array",items:{type:"integer",enum:[1,2]}},memberId:{type:"string"},role:{type:"integer",enum:[1,2]},memberType:{type:"integer"},allMembersMuted:{type:"boolean"},isSpeakMuted:{type:"boolean"},canSpeakWhenAllMuted:{type:"boolean"}}},p={required:["sessionId","content"],properties:{sessionId:{type:"string"},content:{type:"string",maxLength:1e4},msgType:{type:"integer"},quotedMessageId:{type:"string"},threadId:{type:"string"}}},m={required:["sessionId","msgId"],properties:{sessionId:{type:"string"},msgId:{type:"string"}}},g={required:["action"],properties:{action:{type:"string",enum:["create_agent","list_categories","create_category","update_category","assign_category","rotate_api_key"]},agentName:{type:"string"},introduction:{type:"string"},isMain:{type:"boolean"},agentId:{type:"string"},categoryId:{type:"string"},name:{type:"string"},parentId:{type:"string"},sortOrder:{type:"integer"}}},y={required:["session_id","text"],properties:{event_id:{type:"string"},session_id:{type:"string"},text:{type:"string",maxLength:5e4},quoted_message_id:{type:"string"},is_final:{type:"boolean"}}},d={required:["event_id","status"],properties:{event_id:{type:"string"},status:{type:"string",enum:["responded","canceled","failed"]},msg:{type:"string",maxLength:500}}},c={required:["event_id"],properties:{event_id:{type:"string"},session_id:{type:"string"}}},l={required:["session_id","active"],properties:{session_id:{type:"string"},active:{type:"boolean"},event_id:{type:"string"}}},_={required:["action"],properties:{action:{type:"string",enum:["pair_approve","pair_deny","allow_sender","remove_sender","set_policy"]},code:{type:"string"},sender_id:{type:"string"},policy:{type:"string",enum:["allowlist","open","disabled"]}}},f={required:[],properties:{}},C={grix_query:o,grix_group:a,grix_message_send:p,grix_message_unsend:m,grix_admin:g,grix_reply:y,grix_complete:d,grix_event_ack:c,grix_composing:l,grix_access_control:_,grix_status:f};function B(r,t){const e=C[r];if(!e)return{valid:!1,error:`\u672A\u77E5\u5DE5\u5177: ${r}`};for(const i of e.required)if(t[i]===void 0||t[i]===null)return{valid:!1,error:`\u7F3A\u5C11\u5FC5\u586B\u53C2\u6570: ${i}`};for(const[i,u]of Object.entries(t)){if(u==null)continue;const n=e.properties[i];if(!n)continue;const s=$(i,u,n);if(s)return{valid:!1,error:s}}return{valid:!0}}function $(r,t,e){switch(e.type){case"string":if(typeof t!="string")return`\u53C2\u6570 ${r} \u7C7B\u578B\u9519\u8BEF: \u671F\u671B string\uFF0C\u5B9E\u9645 ${typeof t}`;if(e.maxLength!==void 0&&t.length>e.maxLength)return`\u53C2\u6570 ${r} \u8D85\u8FC7\u6700\u5927\u957F\u5EA6 ${e.maxLength}\uFF0C\u5B9E\u9645 ${t.length}`;if(e.enum&&!e.enum.includes(t))return`\u53C2\u6570 ${r} \u503C "${t}" \u4E0D\u5728\u5141\u8BB8\u8303\u56F4 [${e.enum.join(", ")}]`;break;case"integer":if(typeof t!="number"||!Number.isInteger(t))return`\u53C2\u6570 ${r} \u7C7B\u578B\u9519\u8BEF: \u671F\u671B integer\uFF0C\u5B9E\u9645 ${typeof t=="number"?"\u6D6E\u70B9\u6570":typeof t}`;if(e.minimum!==void 0&&t<e.minimum)return`\u53C2\u6570 ${r} \u503C ${t} \u5C0F\u4E8E\u6700\u5C0F\u503C ${e.minimum}`;if(e.maximum!==void 0&&t>e.maximum)return`\u53C2\u6570 ${r} \u503C ${t} \u5927\u4E8E\u6700\u5927\u503C ${e.maximum}`;if(e.enum&&!e.enum.includes(t))return`\u53C2\u6570 ${r} \u503C ${t} \u4E0D\u5728\u5141\u8BB8\u8303\u56F4 [${e.enum.join(", ")}]`;break;case"boolean":if(typeof t!="boolean")return`\u53C2\u6570 ${r} \u7C7B\u578B\u9519\u8BEF: \u671F\u671B boolean\uFF0C\u5B9E\u9645 ${typeof t}`;break;case"array":if(!Array.isArray(t))return`\u53C2\u6570 ${r} \u7C7B\u578B\u9519\u8BEF: \u671F\u671B array\uFF0C\u5B9E\u9645 ${typeof t}`;if(e.maxItems!==void 0&&t.length>e.maxItems)return`\u53C2\u6570 ${r} \u8D85\u8FC7\u6700\u5927\u5143\u7D20\u6570 ${e.maxItems}\uFF0C\u5B9E\u9645 ${t.length}`;if(e.items)for(let i=0;i<t.length;i++){const u=t[i];if(e.items.type==="string"&&typeof u!="string")return`\u53C2\u6570 ${r}[${i}] \u7C7B\u578B\u9519\u8BEF: \u671F\u671B string\uFF0C\u5B9E\u9645 ${typeof u}`;if(e.items.type==="integer"){if(typeof u!="number"||!Number.isInteger(u))return`\u53C2\u6570 ${r}[${i}] \u7C7B\u578B\u9519\u8BEF: \u671F\u671B integer\uFF0C\u5B9E\u9645 ${typeof u}`;if(e.items.enum&&!e.items.enum.includes(u))return`\u53C2\u6570 ${r}[${i}] \u503C ${u} \u4E0D\u5728\u5141\u8BB8\u8303\u56F4 [${e.items.enum.join(", ")}]`}}break}}export{B as validateToolArgs};
@@ -1,2 +1,2 @@
1
- import{createHash as A}from"node:crypto";import l from"node:fs";import O from"node:os";import n from"node:path";import S from"node:process";import{mkdir as F}from"node:fs/promises";import{setTimeout as j}from"node:timers/promises";import{resolveRuntimePaths as y}from"../core/config/paths.js";import{inspectDaemonState as w}from"../runtime/service-state.js";import{isProcessRunning as E,runCommand as f,terminateProcessTree as U,waitForProcessExit as V}from"./process-control.js";import{isLockHolderIdentityVerified as R,readDaemonLock as L}from"../runtime/daemon-lock.js";import{ServiceInstallStore as J}from"./service-install-store.js";import{getPlatformServiceAdapter as X}from"./platform-adapter.js";import{captureLogCursor as _,formatLogDelta as $}from"./log-diagnostics.js";import{LAUNCHER_FILE as z,bootstrapVersionFromPackage as Y,compareVersions as I,ensureLauncher as K,launcherPath as T,packagedLauncherPath as Q,readLauncherTemplateVersion as Z,readPackageVersionAt as M,readPointer as H,switchCurrent as tt,versionEntry as N}from"../core/upgrade/version-store.js";import{buildServiceID as P,parseExecStartWords as et,parseProgramArgumentsFromPlistXML as it,resolveLinuxUserUnitPath as rt,resolveMacOSLaunchAgentPath as at,resolveWindowsServiceRuntimeDir as st,resolveServiceInstallRecordPath as ot,resolveServiceStderrPath as nt,resolveServiceStdoutPath as ct}from"./service-paths.js";import{resolveWindowsStartupShortcutPath as dt,resolveWindowsWrapperPath as b}from"./platform-adapter.js";import{readInstalledWrapperTemplate as lt}from"./windows-wrapper-migration.js";import{appendLegacyHealLog as ht,isLauncherStoreUsable as B,parseWrapperCliPath as ut,planLegacyStateHeal as mt}from"./legacy-state-heal.js";const pt=6e4;function ft(u=S.env){const e=String(u.GRIX_DAEMON_READY_TIMEOUT_MS??"").trim();return/^\d+$/.test(e)?Number(e):pt}function G(u,e,t){const i=e.pid!==u.status.pid||e.state!==u.status.state||e.updated_at!==u.status.updated_at||e.instance_id!==u.status.instance_id,r=!u.lock||t.pid!==u.lock.pid||t.acquired_at!==u.lock.acquired_at||t.instance_id!==u.lock.instance_id;return i&&r}function Dt(u){return u?`lock holder pid ${u.pid} ${E(u.pid)?"alive":"dead"}`:"lock holder: none"}function vt(u){try{return l.realpathSync(u)}catch{return u}}class Mt{adapter;platform;homeDir;uid;nodePath;cliPath;waitForExit;terminateTree;constructor(e){this.platform=e.platform??S.platform,this.homeDir=e.homeDir??O.homedir(),this.uid=e.uid??S.getuid?.()??0,this.nodePath=e.nodePath??S.execPath,this.cliPath=vt(e.cliPath),this.adapter=X(this.platform),this.waitForExit=e.processControl?.waitForExit??V,this.terminateTree=e.processControl?.terminateTree??U}normalizeRootDir(e){return n.resolve(String(e??"").trim())}resolveLocalServiceBaseDir(){if(this.platform==="darwin")return n.join(this.homeDir,"Library","Application Support","grix-connector");if(this.platform==="win32"){const e=String(S.env.LOCALAPPDATA??"").trim();return n.join(e&&n.isAbsolute(e)?e:n.join(this.homeDir,"AppData","Local"),"grix-connector")}return null}resolveServiceRuntimeDir(e){const t=this.resolveLocalServiceBaseDir();return t?this.platform==="win32"?st(this.normalizeRootDir(e),S.env,this.homeDir):n.join(t,"runtime"):null}resolveServiceCliPath(e){const t=this.resolveServiceRuntimeDir(e);return t?T(t):this.cliPath}resolveSourcePackageDir(){return n.dirname(n.dirname(this.cliPath))}async adoptLauncher(e){const t=this.normalizeRootDir(e),i=this.resolveServiceRuntimeDir(t);if(!i)return!1;const r=await this.createStore(t).load();return!r||r.cli_path===this.resolveServiceCliPath(t)&&B(i)?!1:(await this.refreshDescriptor(t,r),!0)}prepareServiceRuntime(e){const t=this.resolveServiceRuntimeDir(e);if(!t)return;const i=this.resolveSourcePackageDir();l.mkdirSync(t,{recursive:!0});const r=M(i);if(!r)throw new Error(`Cannot read package version at ${i} (entry ${this.cliPath}); refusing to install the service`);if(!K(t,i))throw new Error(`Package at ${i} does not ship ${z}; refusing to point the service at it`);const a=H(t,"current");if((!a||!l.existsSync(N(t,a))||I(r,a)>0)&&(Y(t,i,r),tt(t,r)),this.platform==="win32"||this.platform==="darwin"){const s=n.join(i,"scripts","upgrade-guardian.mjs");if(l.existsSync(s)){const o=n.join(this.normalizeRootDir(e),"bin"),d=n.join(o,"upgrade-guardian.mjs"),c=n.join(o,"upgrade-guardian.fallback.mjs"),h=`${d}.tmp-${S.pid}`;l.mkdirSync(o,{recursive:!0});try{l.copyFileSync(s,h),l.renameSync(h,d),l.copyFileSync(s,c)}finally{l.rmSync(h,{force:!0})}}if(this.platform==="darwin"){const o=n.join(i,"scripts","upgrade-guardian.sh");if(l.existsSync(o)){const d=n.join(this.normalizeRootDir(e),"bin","upgrade-guardian.sh"),c=`${d}.tmp-${S.pid}`;try{l.copyFileSync(o,c),l.chmodSync(c,493),l.renameSync(c,d)}finally{l.rmSync(c,{force:!0})}}}}}buildDescriptor(e,t,i=""){const r=this.normalizeRootDir(e),a=this.resolveServiceCliPath(r);return{schema_version:1,platform:this.platform,service_id:P(r,this.platform),node_path:this.nodePath,cli_path:a,cli_hash:this.computeServiceHash(a)??"",definition_path:i,config_dir:t??"",installed_at:0,updated_at:0}}createStore(e){return new J(ot(this.normalizeRootDir(e)))}toAdapterPayload(e,t){return{serviceID:e.service_id,nodePath:e.node_path,cliPath:e.cli_path,configDir:e.config_dir||void 0,rootDir:this.normalizeRootDir(t),environmentPath:this.buildServiceEnvironmentPath(e.node_path)}}async resolveServiceLogPaths(e){const t=this.resolveLocalServiceBaseDir();if(t){const i=n.join(this.platform==="darwin"?n.join(this.homeDir,"Library","Logs","grix-connector"):n.join(t,"logs"),P(this.normalizeRootDir(e),this.platform));return await F(i,{recursive:!0}),{stdoutPath:n.join(i,"daemon.out.log"),stderrPath:n.join(i,"daemon.err.log")}}return await F(n.join(e,"service"),{recursive:!0}),{stdoutPath:ct(e),stderrPath:nt(e)}}buildServiceEnvironmentPath(e){const t=[n.dirname(e),...this.platform==="darwin"?["/opt/homebrew/bin"]:[],"/usr/local/bin","/usr/bin","/bin","/usr/sbin","/sbin"],i=String(S.env.PATH??"").split(n.delimiter).filter(s=>s&&n.isAbsolute(s)).filter(s=>!s.includes("/node_modules/.bin")).filter(s=>!s.includes("/node-gyp-bin")),r=[],a=new Set;for(const s of[...t,...i]){if(a.has(s))continue;if(a.add(s),[...r,s].join(n.delimiter).length>2048)break;r.push(s)}return r.join(n.delimiter)}toStartParams(e){return{serviceID:e.service_id,definitionPath:e.definition_path,uid:this.uid,homeDir:this.homeDir,runCommand:f}}toStopParams(e){return{serviceID:e.service_id,uid:this.uid,homeDir:this.homeDir,runCommand:f}}toRestartParams(e,t={}){return{serviceID:e.service_id,definitionPath:e.definition_path,uid:this.uid,homeDir:this.homeDir,runCommand:f,reloadDefinition:t.reloadDefinition===!0}}toUninstallParams(e){return{serviceID:e.service_id,definitionPath:e.definition_path,uid:this.uid,homeDir:this.homeDir,runCommand:f}}async loadDescriptor(e){const t=this.createStore(e),i=await t.load();if(!i)throw new Error("\u540E\u53F0\u670D\u52A1\u8FD8\u6CA1\u6709\u5B89\u88C5\uFF0C\u8BF7\u5148\u6267\u884C grix-connector start\uFF08\u4F1A\u81EA\u52A8\u5B89\u88C5\u5E76\u542F\u52A8\u670D\u52A1\uFF09\u3002");return{store:t,descriptor:i}}resolveServiceNodePath(e){return e.node_path&&l.existsSync(e.node_path)?e.node_path:this.nodePath}isDescriptorCurrent(e,t){if(t.node_path!==this.resolveServiceNodePath(t)||t.cli_path!==this.resolveServiceCliPath(e)||!t.cli_hash)return!1;const i=this.computeServiceHash(t.cli_path),r=this.computeServiceHash(this.cliPath);return i!==null&&r!==null&&i===t.cli_hash&&r===t.cli_hash}computeFileHash(e){try{const t=l.readFileSync(e);return A("sha256").update(t).digest("hex")}catch{return null}}computeServiceHash(e){return this.resolveLocalServiceBaseDir()?this.computeLauncherHash(e):this.computeFileHash(e)}computeLauncherHash(e){try{const t=this.resolveSourcePackageDir(),i=M(t)??"",r=n.basename(e)===z,a=r?n.dirname(e):null,s=r?e:Q(t);if(!s)return null;const o=Z(s);if(o<0)return null;let d=!0;if(a){const c=H(a,"current");d=!!c&&l.existsSync(N(a,c))&&I(c,i)>=0}return d?A("sha256").update(`launcher:${o}`).update(`source:${i}`).digest("hex"):null}catch{return null}}computeRuntimeDirHash(e){try{const t=n.dirname(e),i=A("sha256"),r=s=>{const o=l.readdirSync(s,{withFileTypes:!0}).sort((d,c)=>d.name.localeCompare(c.name));for(const d of o){const c=n.join(s,d.name);i.update(n.relative(t,c)),d.isDirectory()?r(c):d.isFile()&&i.update(l.readFileSync(c))}};r(t);const a=n.join(n.dirname(t),"package.json");return l.existsSync(a)&&(i.update("package.json"),i.update(l.readFileSync(a))),i.digest("hex")}catch{return null}}async refreshDescriptor(e,t){const i=this.normalizeRootDir(e);await F(n.join(i,"service"),{recursive:!0}),this.prepareServiceRuntime(i);const r=this.resolveServiceCliPath(i),a={...t,platform:this.platform,service_id:t.service_id||P(i,this.platform),node_path:this.resolveServiceNodePath(t),cli_path:r,cli_hash:this.computeServiceHash(r)??"",config_dir:t.config_dir},s=await this.resolveServiceLogPaths(i),o=await this.adapter.install({...this.toAdapterPayload(a,i),stdoutPath:s.stdoutPath,stderrPath:s.stderrPath,homeDir:this.homeDir,uid:this.uid,runCommand:f}),d={...a,installed_at:t.installed_at,definition_path:o?.definitionPath||a.definition_path,updated_at:Date.now()};return await this.createStore(i).save(d),d}async resolveActiveDescriptor(e){return(await this.resolveActiveDescriptorState(e)).descriptor}async resolveActiveDescriptorState(e){const{descriptor:t}=await this.loadDescriptor(e);return this.isDescriptorCurrent(e,t)?{descriptor:t,definitionRewritten:!1}:{descriptor:await this.refreshDescriptor(e,t),definitionRewritten:!0}}async discoverConflictingServices(e){const t=this.normalizeRootDir(e),i=P(t,this.platform);return(await this.adapter.discoverServices({homeDir:this.homeDir,runCommand:f})).filter(a=>a.serviceID!==i)}async cleanupConflictingServices(e){const t=await this.discoverConflictingServices(e);if(t.length>0){const r=t.map(a=>a.serviceID).join(", ");console.log(`\u53D1\u73B0 ${t.length} \u4E2A\u6B8B\u7559\u670D\u52A1\uFF0C\u6B63\u5728\u68C0\u67E5: ${r}`)}let i=0;for(const r of t){if(await this.adapter.isServiceLoaded({serviceID:r.serviceID,uid:this.uid,homeDir:this.homeDir,runCommand:f})){console.log(`\u8DF3\u8FC7\u6B63\u5728\u8FD0\u884C\u7684\u670D\u52A1: ${r.serviceID}`);continue}try{await this.adapter.stop({serviceID:r.serviceID,uid:this.uid,homeDir:this.homeDir,runCommand:f})}catch{}try{await this.adapter.uninstall({serviceID:r.serviceID,definitionPath:r.definitionPath,uid:this.uid,homeDir:this.homeDir,runCommand:f}),i++}catch{}}return i}captureDaemonBaseline(e){return{status:w(e.daemonStatusFile),lock:L(e.daemonLockFile)}}readMatchedGeneration(e,t){const i=w(e.daemonStatusFile),r=L(e.daemonLockFile);return!i.running||!t.includes(i.state)||i.pid<=0||!r||r.pid!==i.pid||!E(r.pid)||typeof r.root_dir!="string"||n.resolve(r.root_dir)!==n.resolve(e.rootDir)||typeof r.instance_id!="string"||r.instance_id.length===0||i.instance_id!==r.instance_id?null:{inspection:i,lock:r}}isDaemonReady(e,t,i){const r=this.readMatchedGeneration(e,["running"]);if(!r)return!1;const{inspection:a,lock:s}=r,o=G(t,a,s),d=t.status.state==="starting"&&t.status.instance_id===s.instance_id&&t.lock?.pid===s.pid&&t.lock.instance_id===s.instance_id&&a.instance_id===s.instance_id&&a.updated_at!==t.status.updated_at,c=t.lock?.pid===s.pid&&t.lock.instance_id===s.instance_id&&t.lock.acquired_at===s.acquired_at&&(t.status.pid!==s.pid||t.status.instance_id!==s.instance_id||t.status.state!=="running")&&a.pid===s.pid&&a.instance_id===s.instance_id&&a.state==="running"&&a.updated_at!==t.status.updated_at;return!o&&!d&&!c&&!R(s)?!1:i?o||d||c:!0}findStartedGeneration(e,t,i,r){const a=this.readMatchedGeneration(e,["starting","running"]);if(!a)return null;const{inspection:s,lock:o}=a,d=t.lock?.pid===o.pid&&t.lock.instance_id===o.instance_id&&t.lock.acquired_at===o.acquired_at&&(t.status.pid!==o.pid||t.status.instance_id!==o.instance_id)&&s.updated_at!==t.status.updated_at;let c=G(t,s,o)||d;if(!c&&!i){const h=`${o.pid}:${o.instance_id}`;let p=r.get(h);p===void 0&&(p=R(o),r.set(h,p)),c=p}return c?{pid:o.pid,instance_id:o.instance_id,state:s.state}:null}diedHoldingLock(e,t){if(E(t.pid))return!1;const i=L(e.daemonLockFile);return i?.pid===t.pid&&i.instance_id===t.instance_id}canForceTerminateDaemon(e,t){const i=L(e.daemonLockFile);return t>0&&i!==null&&i.pid===t&&typeof i.root_dir=="string"&&n.resolve(i.root_dir)===n.resolve(e.rootDir)&&R(i)}defaultDaemonStartTimeoutMs(){return this.platform==="darwin"?5e4:this.platform==="win32"?45e3:2e4}async waitForDaemonStarted(e,t={}){const i=t.timeoutMs??this.defaultDaemonStartTimeoutMs(),r=t.readyTimeoutMs??ft(),a=t.baseline??this.captureDaemonBaseline(e),s=t.requireNewDaemon??!1,o=t.stdoutLogFile??e.stdoutLogFile,d=t.stderrLogFile??e.stderrLogFile,c=t.stdoutLogCursor??_(o),h=t.stderrLogCursor??_(d),p=new Map,v=(D,k=w(e.daemonStatusFile))=>{const x=L(e.daemonLockFile),q=[D,`status=${JSON.stringify(k)}`,`lock=${x?JSON.stringify(x):"missing"}`,Dt(x),$("stderr",d,h),$("stdout",o,c)].filter(Boolean);return new Error(q.join(`
2
- `))},m=()=>{const D=w(e.daemonStatusFile);if(D.state==="failed"&&(a.status.state!=="failed"||D.pid!==a.status.pid||D.updated_at!==a.status.updated_at||D.instance_id!==a.status.instance_id))throw v("daemon start failed",D)},C=Date.now();let g=null;for(;Date.now()-C<i;){if(this.isDaemonReady(e,a,s))return;if(m(),g=this.findStartedGeneration(e,a,s,p),g)break;await j(100)}if(!g)throw v(`daemon start timeout (${i}ms)`);const W=Date.now()+r;for(;;){if(this.isDaemonReady(e,a,s))return;m();const D=this.findStartedGeneration(e,a,s,p);if(D)g=D;else if(this.diedHoldingLock(e,g))throw v(`daemon exited during startup (pid ${g.pid})`);if(Date.now()>=W){if(D){const k=Math.round((Date.now()-C)/1e3);console.warn(`Warning: daemon \u4ECD\u5728\u542F\u52A8\u4E2D(pid ${D.pid}\uFF0C\u5DF2 ${k}s)\uFF0C\u7A0D\u540E\u7528 grix-connector status \u67E5\u770B`);return}throw v(`daemon start timeout (${i}ms + ${r}ms)`)}await j(100)}}readDefinitionCliPath(e,t){const i=t.service_id||P(e,this.platform),r=a=>{try{return l.readFileSync(a,"utf8")}catch{return null}};if(this.adapter.kind==="launchd"){const a=r(t.definition_path||at(i,this.homeDir));return a?it(a)[1]??null:null}if(this.adapter.kind==="systemd-user"){const a=r(t.definition_path||rt(i,this.homeDir));return a?et(a)[1]??null:null}if(this.adapter.kind==="task-scheduler"){const a=r(b(i,this.homeDir));return a?ut(a):null}return null}observeLegacyState(e,t){const i=this.resolveServiceRuntimeDir(e),r=t?.service_id||P(e,this.platform);return{platform:this.platform,record:t,expectedLauncherPath:i?T(i):null,launcherStoreUsable:i?B(i):!1,nodePathExists:!!t?.node_path&&l.existsSync(t.node_path),cliPathExists:!!t?.cli_path&&l.existsSync(t.cli_path),definitionCliPath:t?this.readDefinitionCliPath(e,t):null,upgradePending:l.existsSync(n.join(e,"data","upgrade-pending.json")),windowsWrapperTemplate:this.platform==="win32"?lt(b(r,this.homeDir)):-1,windowsStartupShortcutExists:this.platform==="win32"&&l.existsSync(dt(r,this.homeDir))}}async healLegacyState(e){const t=this.normalizeRootDir(e.rootDir),i=this.createStore(t),r=await i.load(),a=mt(this.observeLegacyState(t,r));if(!a.heal||!r)return{healed:!1,reasons:[]};const s=o=>{ht(t,o),console.log(`\u9057\u7559\u72B6\u6001\u81EA\u6108: ${o}`)};for(const o of a.reasons)s(`detected ${o}`);try{await this.stopServiceAndDaemon(t,r),s("stopped the legacy service and daemon")}catch(o){s(`stop failed (continuing): ${o instanceof Error?o.message:o}`)}try{await this.adapter.uninstall(this.toUninstallParams(r)),s("removed the legacy service definition")}catch(o){s(`uninstall failed (continuing): ${o instanceof Error?o.message:o}`)}return await i.clear(),s("cleared the install record; rebuilding via install"),{healed:!0,reasons:a.reasons,configDir:r.config_dir||void 0}}async install(e){const t=this.normalizeRootDir(e.rootDir);await this.cleanupConflictingServices(t),await F(n.join(t,"service"),{recursive:!0}),this.prepareServiceRuntime(t);const i=y(t),r=this.captureDaemonBaseline(i),a=this.buildDescriptor(t,e.configDir),s=await this.resolveServiceLogPaths(t),o=_(s.stdoutPath),d=_(s.stderrPath),c=await this.adapter.isServiceLoaded({serviceID:a.service_id,uid:this.uid,homeDir:this.homeDir,runCommand:f}),h=await this.adapter.install({...this.toAdapterPayload(a,t),stdoutPath:s.stdoutPath,stderrPath:s.stderrPath,homeDir:this.homeDir,uid:this.uid,runCommand:f}),p={...a,definition_path:h?.definitionPath??"",installed_at:Date.now(),updated_at:Date.now()};return await this.createStore(t).save(p),c?await this.adapter.restart(this.toRestartParams(p,{reloadDefinition:!0})):await this.adapter.start(this.toStartParams(p)),await this.waitForDaemonStarted(i,{baseline:r,requireNewDaemon:!0,stdoutLogFile:s.stdoutPath,stderrLogFile:s.stderrPath,stdoutLogCursor:o,stderrLogCursor:d}),this.status({rootDir:t})}async start(e){const t=this.normalizeRootDir(e.rootDir);await this.cleanupConflictingServices(t);const i=y(t),r=this.captureDaemonBaseline(i),a=r.status,s=_(i.stdoutLogFile),o=_(i.stderrLogFile),c=await this.createStore(t).load();if(a.running&&c&&this.isDescriptorCurrent(t,c)){if(a.state==="starting"){const m=r.lock;if(m!==null&&m.pid===a.pid&&typeof m.root_dir=="string"&&n.resolve(m.root_dir)===n.resolve(i.rootDir)&&typeof m.instance_id=="string"&&m.instance_id.length>0&&a.instance_id===m.instance_id&&R(m)){const g=await this.resolveServiceLogPaths(t);return await this.waitForDaemonStarted(i,{baseline:r,stdoutLogFile:g.stdoutPath,stderrLogFile:g.stderrPath,stdoutLogCursor:s,stderrLogCursor:o}),this.status({rootDir:t})}}if(this.isDaemonReady(i,r,!1))return this.status({rootDir:t})}let h,p=!1;if(c)this.isDescriptorCurrent(t,c)&&a.running?h=c:(h=await this.refreshDescriptor(t,c),p=!0);else{const m=await this.resolveActiveDescriptorState(t);h=m.descriptor,p=m.definitionRewritten}const v=await this.resolveServiceLogPaths(t);try{a.running?await this.adapter.restart(this.toRestartParams(h,{reloadDefinition:p})):await this.adapter.start(this.toStartParams(h))}catch(m){if(m instanceof Error&&m.message.includes("plist missing"))await this.adapter.install({...this.toAdapterPayload(h,t),stdoutPath:v.stdoutPath,stderrPath:v.stderrPath,homeDir:this.homeDir,uid:this.uid,runCommand:f}),await this.adapter.start(this.toStartParams(h));else throw m}return await this.waitForDaemonStarted(i,{baseline:r,requireNewDaemon:!0,stdoutLogFile:v.stdoutPath,stderrLogFile:v.stderrPath,stdoutLogCursor:s,stderrLogCursor:o}),this.status({rootDir:t})}async stopServiceAndDaemon(e,t){const i=y(e),r=w(i.daemonStatusFile);await this.adapter.stop(this.toStopParams(t)),!(!r.running||!r.pid||await this.waitForExit(r.pid,{timeoutMs:5e3}))&&this.canForceTerminateDaemon(i,r.pid)&&(await this.terminateTree(r.pid,{platform:this.platform,runCommandImpl:f}),await this.waitForExit(r.pid,{timeoutMs:5e3}))}async stop(e){const t=this.normalizeRootDir(e.rootDir),{descriptor:i}=await this.loadDescriptor(t);return await this.stopServiceAndDaemon(t,i),this.status({rootDir:t})}async restart(e){const t=this.normalizeRootDir(e.rootDir);await this.cleanupConflictingServices(t);const{descriptor:i,definitionRewritten:r}=await this.resolveActiveDescriptorState(t),a=y(t),s=this.captureDaemonBaseline(a),o=await this.resolveServiceLogPaths(t),d=_(o.stdoutPath),c=_(o.stderrPath);try{await this.adapter.restart(this.toRestartParams(i,{reloadDefinition:r}))}catch(h){if(h instanceof Error&&h.message.includes("plist missing"))await this.adapter.install({...this.toAdapterPayload(i,t),stdoutPath:o.stdoutPath,stderrPath:o.stderrPath,homeDir:this.homeDir,uid:this.uid,runCommand:f}),await this.adapter.start(this.toStartParams(i));else throw h}return await this.waitForDaemonStarted(a,{baseline:s,requireNewDaemon:!0,stdoutLogFile:o.stdoutPath,stderrLogFile:o.stderrPath,stdoutLogCursor:d,stderrLogCursor:c}),this.status({rootDir:t})}async uninstall(e){const t=this.normalizeRootDir(e.rootDir),i=this.createStore(t),r=await i.load();return r&&(await this.adapter.uninstall(this.toUninstallParams(r)),await i.clear()),this.status({rootDir:t})}async status(e){const t=this.normalizeRootDir(e.rootDir),r=await this.createStore(t).load(),a=y(t),s=w(a.daemonStatusFile);return r?{installed:!0,install_state:this.isDescriptorCurrent(t,r)?"current":"stale",service_kind:this.adapter.kind,service_id:r.service_id,definition_path:r.definition_path,root_dir:t,daemon_state:s.state,pid:s.pid,connection_state:s.connection_state,updated_at:s.updated_at,reason:s.reason}:{installed:!1,install_state:"missing",service_kind:this.adapter.kind,root_dir:t,daemon_state:s.state,pid:s.pid,reason:s.reason}}}export{Mt as ServiceManager};
1
+ import{createHash as j}from"node:crypto";import l from"node:fs";import W from"node:os";import o from"node:path";import D from"node:process";import{mkdir as C}from"node:fs/promises";import{setTimeout as z}from"node:timers/promises";import{resolveRuntimePaths as L}from"../core/config/paths.js";import{inspectDaemonState as P}from"../runtime/service-state.js";import{isProcessRunning as E,runCommand as f,terminateProcessTree as q,waitForProcessExit as O}from"./process-control.js";import{isLockHolderIdentityVerified as x,readDaemonLock as k}from"../runtime/daemon-lock.js";import{ServiceInstallStore as V}from"./service-install-store.js";import{getPlatformServiceAdapter as J}from"./platform-adapter.js";import{captureLogCursor as S,formatLogDelta as $}from"./log-diagnostics.js";import{LAUNCHER_FILE as M,bootstrapVersionFromPackage as X,compareVersions as H,ensureLauncher as Y,launcherPath as B,packagedLauncherPath as K,readLauncherTemplateVersion as Q,readPackageVersionAt as T,readPointer as N,switchCurrent as Z,versionEntry as tt}from"../core/upgrade/version-store.js";import{buildServiceID as y,parseExecStartWords as et,parseProgramArgumentsFromPlistXML as it,resolveLinuxUserUnitPath as rt,resolveMacOSLaunchAgentPath as at,resolveWindowsServiceRuntimeDir as st,resolveServiceInstallRecordPath as nt,resolveServiceStderrPath as ot,resolveServiceStdoutPath as ct}from"./service-paths.js";import{resolveWindowsStartupShortcutPath as dt,resolveWindowsWrapperPath as b}from"./platform-adapter.js";import{readInstalledWrapperTemplate as lt}from"./windows-wrapper-migration.js";import{appendLegacyHealLog as ht,isLauncherStoreUsable as I,parseWrapperCliPath as ut,planLegacyStateHeal as mt}from"./legacy-state-heal.js";const pt=6e4;function ft(u=D.env){const e=String(u.GRIX_DAEMON_READY_TIMEOUT_MS??"").trim();return/^\d+$/.test(e)?Number(e):pt}function G(u,e,t){const i=e.pid!==u.status.pid||e.state!==u.status.state||e.updated_at!==u.status.updated_at||e.instance_id!==u.status.instance_id,r=!u.lock||t.pid!==u.lock.pid||t.acquired_at!==u.lock.acquired_at||t.instance_id!==u.lock.instance_id;return i&&r}function vt(u){return u?`lock holder pid ${u.pid} ${E(u.pid)?"alive":"dead"}`:"lock holder: none"}function Dt(u){try{return l.realpathSync(u)}catch{return u}}class Mt{adapter;platform;homeDir;uid;nodePath;cliPath;waitForExit;terminateTree;constructor(e){this.platform=e.platform??D.platform,this.homeDir=e.homeDir??W.homedir(),this.uid=e.uid??D.getuid?.()??0,this.nodePath=e.nodePath??D.execPath,this.cliPath=Dt(e.cliPath),this.adapter=J(this.platform),this.waitForExit=e.processControl?.waitForExit??O,this.terminateTree=e.processControl?.terminateTree??q}normalizeRootDir(e){return o.resolve(String(e??"").trim())}resolveLocalServiceBaseDir(){if(this.platform==="darwin")return o.join(this.homeDir,"Library","Application Support","grix-connector");if(this.platform==="win32"){const e=String(D.env.LOCALAPPDATA??"").trim();return o.join(e&&o.isAbsolute(e)?e:o.join(this.homeDir,"AppData","Local"),"grix-connector")}return null}resolveServiceRuntimeDir(e){const t=this.resolveLocalServiceBaseDir();return t?this.platform==="win32"?st(this.normalizeRootDir(e),D.env,this.homeDir):o.join(t,"runtime"):null}resolveServiceCliPath(e){const t=this.resolveServiceRuntimeDir(e);return t?B(t):this.cliPath}resolveSourcePackageDir(){return o.dirname(o.dirname(this.cliPath))}async adoptLauncher(e){const t=this.normalizeRootDir(e),i=this.resolveServiceRuntimeDir(t);if(!i)return!1;const r=await this.createStore(t).load();return!r||r.cli_path===this.resolveServiceCliPath(t)&&I(i)?!1:(await this.refreshDescriptor(t,r),!0)}prepareServiceRuntime(e){const t=this.resolveServiceRuntimeDir(e);if(!t)return;const i=this.resolveSourcePackageDir();l.mkdirSync(t,{recursive:!0});const r=T(i);if(!r)throw new Error(`Cannot read package version at ${i} (entry ${this.cliPath}); refusing to install the service`);if(!Y(t,i))throw new Error(`Package at ${i} does not ship ${M}; refusing to point the service at it`);const a=N(t,"current");if((!a||!l.existsSync(tt(t,a))||H(r,a)>0)&&(X(t,i,r),Z(t,r)),this.platform==="win32"||this.platform==="darwin"){const s=o.join(i,"scripts","upgrade-guardian.mjs");if(l.existsSync(s)){const n=o.join(this.normalizeRootDir(e),"bin"),d=o.join(n,"upgrade-guardian.mjs"),c=o.join(n,"upgrade-guardian.fallback.mjs"),p=`${d}.tmp-${D.pid}`;l.mkdirSync(n,{recursive:!0});try{l.copyFileSync(s,p),l.renameSync(p,d),l.copyFileSync(s,c)}finally{l.rmSync(p,{force:!0})}}if(this.platform==="darwin"){const n=o.join(i,"scripts","upgrade-guardian.sh");if(l.existsSync(n)){const d=o.join(this.normalizeRootDir(e),"bin","upgrade-guardian.sh"),c=`${d}.tmp-${D.pid}`;try{l.copyFileSync(n,c),l.chmodSync(c,493),l.renameSync(c,d)}finally{l.rmSync(c,{force:!0})}}}}}buildDescriptor(e,t,i=""){const r=this.normalizeRootDir(e),a=this.resolveServiceCliPath(r);return{schema_version:1,platform:this.platform,service_id:y(r,this.platform),node_path:this.nodePath,cli_path:a,cli_hash:this.computeServiceHash(a)??"",definition_path:i,config_dir:t??"",installed_at:0,updated_at:0}}createStore(e){return new V(nt(this.normalizeRootDir(e)))}toAdapterPayload(e,t){return{serviceID:e.service_id,nodePath:e.node_path,cliPath:e.cli_path,configDir:e.config_dir||void 0,rootDir:this.normalizeRootDir(t),environmentPath:this.buildServiceEnvironmentPath(e.node_path)}}async resolveServiceLogPaths(e){const t=this.resolveLocalServiceBaseDir();if(t){const i=o.join(this.platform==="darwin"?o.join(this.homeDir,"Library","Logs","grix-connector"):o.join(t,"logs"),y(this.normalizeRootDir(e),this.platform));return await C(i,{recursive:!0}),{stdoutPath:o.join(i,"daemon.out.log"),stderrPath:o.join(i,"daemon.err.log")}}return await C(o.join(e,"service"),{recursive:!0}),{stdoutPath:ct(e),stderrPath:ot(e)}}buildServiceEnvironmentPath(e){const t=[o.dirname(e),...this.platform==="darwin"?["/opt/homebrew/bin"]:[],"/usr/local/bin","/usr/bin","/bin","/usr/sbin","/sbin"],i=String(D.env.PATH??"").split(o.delimiter).filter(s=>s&&o.isAbsolute(s)).filter(s=>!s.includes("/node_modules/.bin")).filter(s=>!s.includes("/node-gyp-bin")),r=[],a=new Set;for(const s of[...t,...i]){if(a.has(s))continue;if(a.add(s),[...r,s].join(o.delimiter).length>2048)break;r.push(s)}return r.join(o.delimiter)}toStartParams(e){return{serviceID:e.service_id,definitionPath:e.definition_path,uid:this.uid,homeDir:this.homeDir,runCommand:f}}toStopParams(e){return{serviceID:e.service_id,uid:this.uid,homeDir:this.homeDir,runCommand:f}}toRestartParams(e,t={}){return{serviceID:e.service_id,definitionPath:e.definition_path,uid:this.uid,homeDir:this.homeDir,runCommand:f,reloadDefinition:t.reloadDefinition===!0}}toUninstallParams(e){return{serviceID:e.service_id,definitionPath:e.definition_path,uid:this.uid,homeDir:this.homeDir,runCommand:f}}async loadDescriptor(e){const t=this.createStore(e),i=await t.load();if(!i)throw new Error("\u540E\u53F0\u670D\u52A1\u8FD8\u6CA1\u6709\u5B89\u88C5\uFF0C\u8BF7\u5148\u6267\u884C grix-connector start\uFF08\u4F1A\u81EA\u52A8\u5B89\u88C5\u5E76\u542F\u52A8\u670D\u52A1\uFF09\u3002");return{store:t,descriptor:i}}resolveServiceNodePath(e){return e.node_path&&l.existsSync(e.node_path)?e.node_path:this.nodePath}isDescriptorCurrent(e,t){if(t.node_path!==this.resolveServiceNodePath(t)||t.cli_path!==this.resolveServiceCliPath(e)||!t.cli_hash)return!1;if(this.resolveLocalServiceBaseDir())return this.isLauncherCurrent(t.cli_path);const i=this.computeServiceHash(t.cli_path),r=this.computeServiceHash(this.cliPath);return i!==null&&r!==null&&i===t.cli_hash&&r===t.cli_hash}computeFileHash(e){try{const t=l.readFileSync(e);return j("sha256").update(t).digest("hex")}catch{return null}}computeServiceHash(e){return this.resolveLocalServiceBaseDir()?this.computeLauncherHash(e):this.computeFileHash(e)}readLauncherTemplate(e){const t=o.basename(e)===M?e:K(this.resolveSourcePackageDir());return t?Q(t):-1}computeLauncherHash(e){const t=this.readLauncherTemplate(e);return t<0?null:j("sha256").update(`launcher:${t}`).digest("hex")}isLauncherCurrent(e){if(!I(o.dirname(e)))return!1;const t=this.readLauncherTemplate(this.cliPath);return t>=0&&t<=this.readLauncherTemplate(e)}prepareRuntimeIfBehind(e){const t=this.resolveServiceRuntimeDir(e);if(!t)return;const i=T(this.resolveSourcePackageDir()),r=N(t,"current");i&&r&&H(i,r)>0&&this.prepareServiceRuntime(e)}isUpgradeActivationTarget(e){try{const t=JSON.parse(l.readFileSync(o.join(e.dataDir,"upgrade-pending.json"),"utf8")),i=T(this.resolveSourcePackageDir());return!!i&&t?.target_version===i}catch{return!1}}computeRuntimeDirHash(e){try{const t=o.dirname(e),i=j("sha256"),r=s=>{const n=l.readdirSync(s,{withFileTypes:!0}).sort((d,c)=>d.name.localeCompare(c.name));for(const d of n){const c=o.join(s,d.name);i.update(o.relative(t,c)),d.isDirectory()?r(c):d.isFile()&&i.update(l.readFileSync(c))}};r(t);const a=o.join(o.dirname(t),"package.json");return l.existsSync(a)&&(i.update("package.json"),i.update(l.readFileSync(a))),i.digest("hex")}catch{return null}}async refreshDescriptor(e,t){const i=this.normalizeRootDir(e);await C(o.join(i,"service"),{recursive:!0}),this.prepareServiceRuntime(i);const r=this.resolveServiceCliPath(i),a={...t,platform:this.platform,service_id:t.service_id||y(i,this.platform),node_path:this.resolveServiceNodePath(t),cli_path:r,cli_hash:this.computeServiceHash(r)??"",config_dir:t.config_dir},s=await this.resolveServiceLogPaths(i),n=await this.adapter.install({...this.toAdapterPayload(a,i),stdoutPath:s.stdoutPath,stderrPath:s.stderrPath,homeDir:this.homeDir,uid:this.uid,runCommand:f}),d={...a,installed_at:t.installed_at,definition_path:n?.definitionPath||a.definition_path,updated_at:Date.now()};return await this.createStore(i).save(d),d}async resolveActiveDescriptor(e){return(await this.resolveActiveDescriptorState(e)).descriptor}async resolveActiveDescriptorState(e){const{descriptor:t}=await this.loadDescriptor(e);return this.isDescriptorCurrent(e,t)?{descriptor:t,definitionRewritten:!1}:{descriptor:await this.refreshDescriptor(e,t),definitionRewritten:!0}}async discoverConflictingServices(e){const t=this.normalizeRootDir(e),i=y(t,this.platform);return(await this.adapter.discoverServices({homeDir:this.homeDir,runCommand:f})).filter(a=>a.serviceID!==i)}async cleanupConflictingServices(e){const t=await this.discoverConflictingServices(e);if(t.length>0){const r=t.map(a=>a.serviceID).join(", ");console.log(`\u53D1\u73B0 ${t.length} \u4E2A\u6B8B\u7559\u670D\u52A1\uFF0C\u6B63\u5728\u68C0\u67E5: ${r}`)}let i=0;for(const r of t){if(await this.adapter.isServiceLoaded({serviceID:r.serviceID,uid:this.uid,homeDir:this.homeDir,runCommand:f})){console.log(`\u8DF3\u8FC7\u6B63\u5728\u8FD0\u884C\u7684\u670D\u52A1: ${r.serviceID}`);continue}try{await this.adapter.stop({serviceID:r.serviceID,uid:this.uid,homeDir:this.homeDir,runCommand:f})}catch{}try{await this.adapter.uninstall({serviceID:r.serviceID,definitionPath:r.definitionPath,uid:this.uid,homeDir:this.homeDir,runCommand:f}),i++}catch{}}return i}captureDaemonBaseline(e){return{status:P(e.daemonStatusFile),lock:k(e.daemonLockFile)}}readMatchedGeneration(e,t){const i=P(e.daemonStatusFile),r=k(e.daemonLockFile);return!i.running||!t.includes(i.state)||i.pid<=0||!r||r.pid!==i.pid||!E(r.pid)||typeof r.root_dir!="string"||o.resolve(r.root_dir)!==o.resolve(e.rootDir)||typeof r.instance_id!="string"||r.instance_id.length===0||i.instance_id!==r.instance_id?null:{inspection:i,lock:r}}isDaemonReady(e,t,i){const r=this.readMatchedGeneration(e,["running"]);if(!r)return!1;const{inspection:a,lock:s}=r,n=G(t,a,s),d=t.status.state==="starting"&&t.status.instance_id===s.instance_id&&t.lock?.pid===s.pid&&t.lock.instance_id===s.instance_id&&a.instance_id===s.instance_id&&a.updated_at!==t.status.updated_at,c=t.lock?.pid===s.pid&&t.lock.instance_id===s.instance_id&&t.lock.acquired_at===s.acquired_at&&(t.status.pid!==s.pid||t.status.instance_id!==s.instance_id||t.status.state!=="running")&&a.pid===s.pid&&a.instance_id===s.instance_id&&a.state==="running"&&a.updated_at!==t.status.updated_at;return!n&&!d&&!c&&!x(s)?!1:i?n||d||c:!0}findStartedGeneration(e,t,i,r){const a=this.readMatchedGeneration(e,["starting","running"]);if(!a)return null;const{inspection:s,lock:n}=a,d=t.lock?.pid===n.pid&&t.lock.instance_id===n.instance_id&&t.lock.acquired_at===n.acquired_at&&(t.status.pid!==n.pid||t.status.instance_id!==n.instance_id)&&s.updated_at!==t.status.updated_at;let c=G(t,s,n)||d;if(!c&&!i){const p=`${n.pid}:${n.instance_id}`;let h=r.get(p);h===void 0&&(h=x(n),r.set(p,h)),c=h}return c?{pid:n.pid,instance_id:n.instance_id,state:s.state}:null}diedHoldingLock(e,t){if(E(t.pid))return!1;const i=k(e.daemonLockFile);return i?.pid===t.pid&&i.instance_id===t.instance_id}canForceTerminateDaemon(e,t){const i=k(e.daemonLockFile);return t>0&&i!==null&&i.pid===t&&typeof i.root_dir=="string"&&o.resolve(i.root_dir)===o.resolve(e.rootDir)&&x(i)}defaultDaemonStartTimeoutMs(){return this.platform==="darwin"?5e4:this.platform==="win32"?45e3:2e4}async waitForDaemonStarted(e,t={}){const i=t.timeoutMs??this.defaultDaemonStartTimeoutMs(),r=t.readyTimeoutMs??ft(),a=t.baseline??this.captureDaemonBaseline(e),s=t.requireNewDaemon??!1,n=t.stdoutLogFile??e.stdoutLogFile,d=t.stderrLogFile??e.stderrLogFile,c=t.stdoutLogCursor??S(n),p=t.stderrLogCursor??S(d),h=new Map,g=(v,R=P(e.daemonStatusFile))=>{const A=k(e.daemonLockFile),U=[v,`status=${JSON.stringify(R)}`,`lock=${A?JSON.stringify(A):"missing"}`,vt(A),$("stderr",d,p),$("stdout",n,c)].filter(Boolean);return new Error(U.join(`
2
+ `))},_=()=>{const v=P(e.daemonStatusFile);if(v.state==="failed"&&(a.status.state!=="failed"||v.pid!==a.status.pid||v.updated_at!==a.status.updated_at||v.instance_id!==a.status.instance_id))throw g("daemon start failed",v)},m=Date.now();let w=null;for(;Date.now()-m<i;){if(this.isDaemonReady(e,a,s))return;if(_(),w=this.findStartedGeneration(e,a,s,h),w)break;await z(100)}if(!w)throw g(`daemon start timeout (${i}ms)`);const F=Date.now()+r;for(;;){if(this.isDaemonReady(e,a,s))return;_();const v=this.findStartedGeneration(e,a,s,h);if(v)w=v;else if(this.diedHoldingLock(e,w))throw g(`daemon exited during startup (pid ${w.pid})`);if(Date.now()>=F){if(v){const R=Math.round((Date.now()-m)/1e3);console.warn(`Warning: daemon \u4ECD\u5728\u542F\u52A8\u4E2D(pid ${v.pid}\uFF0C\u5DF2 ${R}s)\uFF0C\u7A0D\u540E\u7528 grix-connector status \u67E5\u770B`);return}throw g(`daemon start timeout (${i}ms + ${r}ms)`)}await z(100)}}readDefinitionCliPath(e,t){const i=t.service_id||y(e,this.platform),r=a=>{try{return l.readFileSync(a,"utf8")}catch{return null}};if(this.adapter.kind==="launchd"){const a=r(t.definition_path||at(i,this.homeDir));return a?it(a)[1]??null:null}if(this.adapter.kind==="systemd-user"){const a=r(t.definition_path||rt(i,this.homeDir));return a?et(a)[1]??null:null}if(this.adapter.kind==="task-scheduler"){const a=r(b(i,this.homeDir));return a?ut(a):null}return null}observeLegacyState(e,t){const i=this.resolveServiceRuntimeDir(e),r=t?.service_id||y(e,this.platform);return{platform:this.platform,record:t,expectedLauncherPath:i?B(i):null,launcherStoreUsable:i?I(i):!1,nodePathExists:!!t?.node_path&&l.existsSync(t.node_path),cliPathExists:!!t?.cli_path&&l.existsSync(t.cli_path),definitionCliPath:t?this.readDefinitionCliPath(e,t):null,upgradePending:l.existsSync(o.join(e,"data","upgrade-pending.json")),windowsWrapperTemplate:this.platform==="win32"?lt(b(r,this.homeDir)):-1,windowsStartupShortcutExists:this.platform==="win32"&&l.existsSync(dt(r,this.homeDir))}}async healLegacyState(e){const t=this.normalizeRootDir(e.rootDir),i=this.createStore(t),r=await i.load(),a=mt(this.observeLegacyState(t,r));if(!a.heal||!r)return{healed:!1,reasons:[]};const s=n=>{ht(t,n),console.log(`\u9057\u7559\u72B6\u6001\u81EA\u6108: ${n}`)};for(const n of a.reasons)s(`detected ${n}`);try{await this.stopServiceAndDaemon(t,r),s("stopped the legacy service and daemon")}catch(n){s(`stop failed (continuing): ${n instanceof Error?n.message:n}`)}try{await this.adapter.uninstall(this.toUninstallParams(r)),s("removed the legacy service definition")}catch(n){s(`uninstall failed (continuing): ${n instanceof Error?n.message:n}`)}return await i.clear(),s("cleared the install record; rebuilding via install"),{healed:!0,reasons:a.reasons,configDir:r.config_dir||void 0}}async install(e){const t=this.normalizeRootDir(e.rootDir);await this.cleanupConflictingServices(t),await C(o.join(t,"service"),{recursive:!0}),this.prepareServiceRuntime(t);const i=L(t),r=this.captureDaemonBaseline(i),a=this.buildDescriptor(t,e.configDir),s=await this.resolveServiceLogPaths(t),n=S(s.stdoutPath),d=S(s.stderrPath),c=await this.adapter.isServiceLoaded({serviceID:a.service_id,uid:this.uid,homeDir:this.homeDir,runCommand:f}),p=await this.adapter.install({...this.toAdapterPayload(a,t),stdoutPath:s.stdoutPath,stderrPath:s.stderrPath,homeDir:this.homeDir,uid:this.uid,runCommand:f}),h={...a,definition_path:p?.definitionPath??"",installed_at:Date.now(),updated_at:Date.now()};return await this.createStore(t).save(h),c?await this.adapter.restart(this.toRestartParams(h,{reloadDefinition:!0})):await this.adapter.start(this.toStartParams(h)),await this.waitForDaemonStarted(i,{baseline:r,requireNewDaemon:!0,stdoutLogFile:s.stdoutPath,stderrLogFile:s.stderrPath,stdoutLogCursor:n,stderrLogCursor:d}),this.status({rootDir:t})}async start(e){const t=this.normalizeRootDir(e.rootDir);await this.cleanupConflictingServices(t);const i=L(t),r=this.captureDaemonBaseline(i),a=r.status,s=S(i.stdoutLogFile),n=S(i.stderrLogFile),c=await this.createStore(t).load(),p=this.isUpgradeActivationTarget(i);if(!p&&a.running&&c&&this.isDescriptorCurrent(t,c)){if(this.prepareRuntimeIfBehind(t),a.state==="starting"){const m=r.lock;if(m!==null&&m.pid===a.pid&&typeof m.root_dir=="string"&&o.resolve(m.root_dir)===o.resolve(i.rootDir)&&typeof m.instance_id=="string"&&m.instance_id.length>0&&a.instance_id===m.instance_id&&x(m)){const F=await this.resolveServiceLogPaths(t);return await this.waitForDaemonStarted(i,{baseline:r,stdoutLogFile:F.stdoutPath,stderrLogFile:F.stderrPath,stdoutLogCursor:s,stderrLogCursor:n}),this.status({rootDir:t})}}if(this.isDaemonReady(i,r,!1))return this.status({rootDir:t})}let h,g=!1;if(c)!p&&this.isDescriptorCurrent(t,c)&&a.running?h=c:(h=await this.refreshDescriptor(t,c),g=!0);else{const m=await this.resolveActiveDescriptorState(t);h=m.descriptor,g=m.definitionRewritten}const _=await this.resolveServiceLogPaths(t);try{a.running?await this.adapter.restart(this.toRestartParams(h,{reloadDefinition:g})):await this.adapter.start(this.toStartParams(h))}catch(m){if(m instanceof Error&&m.message.includes("plist missing"))await this.adapter.install({...this.toAdapterPayload(h,t),stdoutPath:_.stdoutPath,stderrPath:_.stderrPath,homeDir:this.homeDir,uid:this.uid,runCommand:f}),await this.adapter.start(this.toStartParams(h));else throw m}return await this.waitForDaemonStarted(i,{baseline:r,requireNewDaemon:!0,stdoutLogFile:_.stdoutPath,stderrLogFile:_.stderrPath,stdoutLogCursor:s,stderrLogCursor:n}),this.status({rootDir:t})}async stopServiceAndDaemon(e,t){const i=L(e),r=P(i.daemonStatusFile);await this.adapter.stop(this.toStopParams(t)),!(!r.running||!r.pid||await this.waitForExit(r.pid,{timeoutMs:5e3}))&&this.canForceTerminateDaemon(i,r.pid)&&(await this.terminateTree(r.pid,{platform:this.platform,runCommandImpl:f}),await this.waitForExit(r.pid,{timeoutMs:5e3}))}async stop(e){const t=this.normalizeRootDir(e.rootDir),{descriptor:i}=await this.loadDescriptor(t);return await this.stopServiceAndDaemon(t,i),this.status({rootDir:t})}async restart(e){const t=this.normalizeRootDir(e.rootDir);await this.cleanupConflictingServices(t);const{descriptor:i,definitionRewritten:r}=await this.resolveActiveDescriptorState(t);r||this.prepareRuntimeIfBehind(t);const a=L(t),s=this.captureDaemonBaseline(a),n=await this.resolveServiceLogPaths(t),d=S(n.stdoutPath),c=S(n.stderrPath);try{await this.adapter.restart(this.toRestartParams(i,{reloadDefinition:r}))}catch(p){if(p instanceof Error&&p.message.includes("plist missing"))await this.adapter.install({...this.toAdapterPayload(i,t),stdoutPath:n.stdoutPath,stderrPath:n.stderrPath,homeDir:this.homeDir,uid:this.uid,runCommand:f}),await this.adapter.start(this.toStartParams(i));else throw p}return await this.waitForDaemonStarted(a,{baseline:s,requireNewDaemon:!0,stdoutLogFile:n.stdoutPath,stderrLogFile:n.stderrPath,stdoutLogCursor:d,stderrLogCursor:c}),this.status({rootDir:t})}async uninstall(e){const t=this.normalizeRootDir(e.rootDir),i=this.createStore(t),r=await i.load();return r&&(await this.adapter.uninstall(this.toUninstallParams(r)),await i.clear()),this.status({rootDir:t})}async status(e){const t=this.normalizeRootDir(e.rootDir),r=await this.createStore(t).load(),a=L(t),s=P(a.daemonStatusFile);return r?{installed:!0,install_state:this.isDescriptorCurrent(t,r)?"current":"stale",service_kind:this.adapter.kind,service_id:r.service_id,definition_path:r.definition_path,root_dir:t,daemon_state:s.state,pid:s.pid,connection_state:s.connection_state,updated_at:s.updated_at,reason:s.reason}:{installed:!1,install_state:"missing",service_kind:this.adapter.kind,root_dir:t,daemon_state:s.state,pid:s.pid,reason:s.reason}}}export{Mt as ServiceManager};
@@ -11159,6 +11159,52 @@ var TOOLS = [
11159
11159
  name: { type: "string", maxLength: 100 }
11160
11160
  }
11161
11161
  }
11162
+ },
11163
+ {
11164
+ name: "grix_toolbar_set",
11165
+ description: `Declare this agent's own toolbar controls above the chat input (client_type "acp" only). Two kinds: "select" renders a dropdown, and the user's choice is delivered back to you as a grix://toolbar/select?item=<id>&option=<id> message; "info" renders a button that shows info_text when tapped and never calls back. Call it again with the full desired list to update, or with an empty array to remove everything. At most 6 items.`,
11166
+ inputSchema: {
11167
+ type: "object",
11168
+ properties: {
11169
+ session_id: { type: "string", description: "Target session ID. Defaults to the session this ACP session is bound to." },
11170
+ items: {
11171
+ type: "array",
11172
+ description: "The complete toolbar, replacing whatever was declared before. Empty array clears it.",
11173
+ items: {
11174
+ type: "object",
11175
+ properties: {
11176
+ id: { type: "string", description: "Stable identifier, [a-z0-9_]+, max 32 chars." },
11177
+ kind: { type: "string", enum: ["select", "info"] },
11178
+ label: { type: "string", description: "Control label, max 20 characters." },
11179
+ value: { type: "string", description: "select only: the currently selected option id." },
11180
+ options: {
11181
+ type: "array",
11182
+ description: "select only: 1-20 options.",
11183
+ items: {
11184
+ type: "object",
11185
+ properties: {
11186
+ id: { type: "string" },
11187
+ label: { type: "string", description: "Max 40 characters." }
11188
+ },
11189
+ required: ["id", "label"]
11190
+ }
11191
+ },
11192
+ info_title: { type: "string", description: "info only: dialog title, max 20 characters. Defaults to label." },
11193
+ info_text: { type: "string", description: "info only: dialog body, max 500 characters." }
11194
+ },
11195
+ required: ["id", "kind", "label"]
11196
+ }
11197
+ }
11198
+ },
11199
+ required: ["items"]
11200
+ },
11201
+ validation: {
11202
+ required: ["items"],
11203
+ properties: {
11204
+ session_id: { type: "string" },
11205
+ items: { type: "array" }
11206
+ }
11207
+ }
11162
11208
  }
11163
11209
  ];
11164
11210
  var EVENT_TOOLS = [
@@ -11377,10 +11423,17 @@ function toolCallToInvoke(toolName, args) {
11377
11423
  return buildSkillSetInvoke(args);
11378
11424
  case "grix_skill_get":
11379
11425
  return buildSkillGetInvoke(args);
11426
+ case "grix_toolbar_set":
11427
+ return buildToolbarSetInvoke(args);
11380
11428
  default:
11381
11429
  throw new Error(`Unknown tool: ${toolName}`);
11382
11430
  }
11383
11431
  }
11432
+ function buildToolbarSetInvoke(args) {
11433
+ const params = { items: args.items ?? [] };
11434
+ if (args.session_id != null) params.session_id = args.session_id;
11435
+ return { action: "toolbar_set", params };
11436
+ }
11384
11437
  var QUERY_ACTION_MAP = {
11385
11438
  contact_search: "contact_search",
11386
11439
  session_search: "session_search",
@@ -11620,7 +11673,10 @@ var PHASE1_INVOKE_ACTIONS = /* @__PURE__ */ new Set([
11620
11673
  "skill_set",
11621
11674
  "skill_get",
11622
11675
  "egg_search",
11623
- "egg_get"
11676
+ "egg_get",
11677
+ // 连接器本地处理(bridge.platformInvoke),不打后端 agent_invoke;
11678
+ // 仍要列在白名单里,内部 API 才放行这条 invoke。
11679
+ "toolbar_set"
11624
11680
  ]);
11625
11681
  var ACCESS_CONTROL_ACTION_MAP = {
11626
11682
  pair_approve: "pair_approve",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "grix-connector",
3
- "version": "4.11.13",
3
+ "version": "4.12.0",
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",