grix-connector 4.9.2 → 4.9.3
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/assets/dsh-bridge/{grix-dsh-bridge-4.9.2.tgz → grix-dsh-bridge-4.9.3.tgz} +0 -0
- package/dist/assets/dsh-bridge/manifest.json +4 -4
- package/dist/bridge/adapter-pool.js +1 -1
- package/dist/bridge/bridge.js +2 -2
- package/dist/bridge/connector-ops.js +1 -1
- package/dist/bridge/lifecycle-barrier.js +1 -1
- package/dist/core/upgrade/idle-wait.js +1 -0
- package/dist/core/upgrade/upgrade-checker.js +2 -2
- package/dist/grix.js +8 -8
- package/dist/launcher.mjs +103 -1
- package/dist/manager.js +1 -1
- package/dist/mcp/stream-http/security.js +1 -1
- package/dist/runtime/daemon-lock.js +2 -2
- package/dist/service/platform-adapter.js +29 -29
- package/dist/service/process-control.js +1 -1
- package/package.json +1 -1
- package/scripts/launcher.mjs +103 -1
package/dist/manager.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{join as k}from"node:path";import{homedir as ce}from"node:os";import{AgentInstance as F}from"./bridge/bridge.js";import{buildSharedInstanceConfig as le,diffSharedOwners as de,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 p}from"./core/log/index.js";import{resolveClientVersion as pe}from"./core/util/client-version.js";import{UpgradeChecker as me}from"./core/upgrade/upgrade-checker.js";import{SkillSyncer as fe}from"./core/skill-sync/skill-syncer.js";import{AgentGlobalConfigStore as _e}from"./core/persistence/agent-global-config-store.js";import{resolveSkillScanMode as we}from"./adapter/claude/skill-scanner.js";import{buildWireSkills as ye}from"./core/skill-sync/sync-state.js";import{buildLibrarySkills as Ae}from"./core/skill-sync/library-skills.js";import{AUDIT_LOCAL_ACTION_TYPES as Se}from"./core/aibot/types.js";import{buildReportedSkills as ke,logDefaultSkillsCheck as ve,cleanupProjectedSkills as B}from"./default-skills/index.js";import{buildProviderEnv as be,supportsKiroCwRelay as W,supportsNonMitmRelayConfig as Ie}from"./core/config/provider-env.js";import{resolveHermesHomeDir as Ce}from"./adapter/shared/native-provider-config.js";import{ensureKiroRelay as Q,stopKiroRelay as K}from"./core/proxy/kiro-relay.js";import{resolveCopilotCommand as Ee}from"./core/runtime/copilot-resolve.js";import{resolveCursorCommand as $e}from"./core/runtime/cursor-resolve.js";import{probeInstalledClientCommands as z}from"./core/probe/installed-clients.js";import{AgentInstaller as Pe}from"./core/installer/installer.js";import{reportInstallFailure as Me}from"./core/observability/sentry.js";import{createDefaultAuditProducer as Te}from"./audit/runtime/default-audit-recorder.js";import{normalizeCodexConfigOverrides as xe}from"./adapter/codex/config-overrides.js";import{resolveCodexDirectHome as De}from"./adapter/codex/direct-home.js";import{AgentConfigRepository as V}from"./core/config/agent-config-repository.js";function X(r){if(r)return{...r.provider_id?.trim()?{providerId:r.provider_id.trim()}:{},...r.base_url?.trim()?{baseUrl:r.base_url.trim()}:{},...r.quota_base_url?.trim()?{quotaBaseUrl:r.quota_base_url.trim()}:{},...r.api_key?.trim()?{apiKey:r.api_key.trim()}:{},...r.model?.trim()?{model:r.model.trim()}:{},...r.fast_model?.trim()?{fastModel:r.fast_model.trim()}:{},...r.effort?.trim()?{effort:r.effort.trim()}:{}}}const Re=8e3,Oe=1e4,je=8e3;function Le(r,e){switch(r){case"claude":return{adapterType:"claude",command:"claude"};case"codex":return{adapterType:"codex",command:"codex",options:{sandboxMode:"danger-full-access"}};case"gemini":return{adapterType:"acp",command:"gemini",autoInjectArgs:{acp:!0},enableSessionBinding:!0};case"qwen":return{adapterType:"acp",command:"qwen",adapterHint:"qwen/base",autoInjectArgs:{acp:!0},enableSessionBinding:!0};case"pi":return{adapterType:"pi",command:"pi"};case"cursor":return{adapterType:"cursor",command:$e()};case"reasonix":return{adapterType:"acp",command:"reasonix",args:["acp"],enableSessionBinding:!0};case"codewhale":return{adapterType:"codewhale",command:"codewhale",enableSessionBinding:!0};case"openhuman":return{adapterType:"openhuman",command:"openhuman-core",enableSessionBinding:!0};case"kiro":return{adapterType:"acp",command:"kiro-cli",args:["acp"],enableSessionBinding:!0};case"kimi":return{adapterType:"acp",command:"kimi",args:["acp"],options:{initial_mode:"yolo"},enableSessionBinding:!0};case"opencode":return{adapterType:"opencode",command:"opencode",args:["serve"],enableSessionBinding:!0};case"copilot":{const n=Ee();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"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: ${r}`)}}function v(r,e){return String(r??"").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 Ne(r){return k(A.data,`session-bindings-${v(r,"unknown")}.json`)}function Ge(r,e){return k(A.data,`session-bindings-${v(r,e)}.json`)}function Ue(r,e){return k(A.data,`active-events-${v(r,e)}.json`)}function qe(r){return k(A.data,`pending-events-${v(r,"unknown")}.json`)}function He(r,e){const n=v(r,"agent"),t=v(e,"unknown");return k(A.data,`terminal-outbox-${n}-${t}.json`)}function Fe(...r){const e=[],n=new Set;for(const t of r)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 Be(r,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[r]??n.acp;return{maxConcurrent:e?.max_concurrent??t.maxConcurrent??1,maxQueued:e?.max_queued??t.maxQueued??99,queueTimeoutMs:e?.queue_timeout_ms??t.queueTimeoutMs??0,cancelableQueued:!0,cancelableRunning:!0}}function We(r,e={}){const n=pe(),t=String(r.client_type??"").trim().toLowerCase(),s=Le(t,r),i=t==="deepseek"?r.dsh?.integration_mode??"profile_bridge":void 0,c=i==="embedded_jsonrpc",o=String(r.ws_url??"").trim(),g=typeof r.name=="string"?r.name:"",a=g.trim(),l="get_session_usage",m="get_rate_limits",h="get_agent_global_config",d=Se;if(!a)throw new Error("agent name is required");if(g!==a)throw new Error(`agent name must not contain leading or trailing whitespace: "${g}"`);if(!o)throw new Error(`agent ${r.name}: ws_url is required`);if(!r.agent_id?.trim())throw new Error(`agent ${r.name}: agent_id is required`);if(!r.api_key?.trim())throw new Error(`agent ${r.name}: api_key is required`);const u=s.adapterType,f=u==="acp",_=t==="qwen",y={...s.options??{}};if(u==="deepseek-harness"&&(y.integrationMode=i,y.dshProfile=r.dsh?.profile?.trim()||"web",y.autoInstallBridge=r.dsh?.auto_install_bridge??!c,y.autoStartProfile=r.dsh?.auto_start_profile??!c),u==="deepseek-harness"&&r.provider?.model?.trim()&&(y.defaultModel=r.provider.model.trim()),r.codex_config_overrides!==void 0){if(u!=="codex")throw new Error(`agent ${r.name}: codex_config_overrides is only supported for client_type=codex`);xe(r.codex_config_overrides,`agent ${r.name}: codex_config_overrides`),y.codexConfigOverrides=r.codex_config_overrides}if(u==="codex"&&r.provider?.wire_api!==void 0){const w=r.provider;if(w.wire_api!=="responses"||!w.base_url?.trim()||!w.api_key?.trim()||!w.model?.trim()||!w.catalog_version?.trim()||!w.catalog_sha256?.trim()||!w.catalog_json?.trim())throw Object.assign(new Error(`agent ${r.name}: incomplete Codex direct relay config`),{code:"INVALID_CODEX_DIRECT_CONFIG"});const H=De(A.data,r.name);y.codexHome=H,y.codexDirect={codexHome:H,config:{baseUrl:w.base_url.trim(),model:w.model.trim(),...w.reasoning_effort?.trim()?{reasoningEffort:w.reasoning_effort.trim()}:{},token:w.api_key.trim()},catalog:{version:w.catalog_version.trim(),sha256:w.catalog_sha256.trim(),json:w.catalog_json}}}const S=u==="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,J=u==="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,P=_?{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=u==="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,T=u==="openhuman"?{adapterHint:"openhuman/base",capabilities:["local_action_v1"],localActions:["session_control","set_model","file_list","create_folder"]}:null,x=u==="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,L=u==="codewhale"?{capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","file_list","create_folder",l]}:null,D=u==="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,R=u==="agy"?{adapterHint:"agy/base",capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","file_list","create_folder",m]}:null,O=u==="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,N=new Set(["hermes","reasonix","kimi","copilot","acp"]),Z=f&&!N.has(t),ee=f&&!_?{localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","claude_interaction_reply","file_list","create_folder",...Z?[l]:[]]}:null,te=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,ne=u==="codex"||u==="claude"||u==="opencode"||u==="deepseek-harness"||t==="gemini"?["session_control","set_model","set_mode",...u==="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"]:[],...u==="claude"?["set_reasoning_effort"]:[]]:void 0,se=u==="cursor"||u==="opencode"||u==="openhuman"||u==="agy"||f&&N.has(t)?[m]:[l,m];f&&y.raw_transport===void 0&&(y.raw_transport=t==="gemini");const re=`${t}/base`,I=we(u,t),ae=I==="kiro"?void 0:process.cwd(),j=X(r.provider),b={...process.env,...r.env};if(I==="hermes"&&!b.HERMES_HOME?.trim()){const w=be(t,j);b.HERMES_HOME=Ce({...w,...b},r.name)}const G=ke({mode:I,projectDir:ae,env:b}),U=G.filter(w=>w.source==="connector").map(w=>w.name);U.length>0&&!e.quiet&&p.info("manager",`[${r.name}] injecting connector skills: [${U.join(", ")}]`);const q=ye(G,A.skills);let ie=q.length>0?q:void 0;const oe=Ae({skillsDir:A.skills,mode:I,home:ce(),env:b});return{name:a,adapterType:u,aibot:{url:o,agentId:r.agent_id,apiKey:r.api_key,clientType:t,clientVersion:n,adapterHint:O?.adapterHint??s.adapterHint??P?.adapterHint??M?.adapterHint??T?.adapterHint??x?.adapterHint??D?.adapterHint??R?.adapterHint??re,capabilities:O?.capabilities??S?.capabilities??L?.capabilities??M?.capabilities??T?.capabilities??x?.capabilities??D?.capabilities??R?.capabilities??P?.capabilities??["stream_chunk","local_action_v1","connector_upgrade"],localActions:Fe(O?.localActions??S?.localActions??L?.localActions??J?.localActions??M?.localActions??T?.localActions??x?.localActions??D?.localActions??R?.localActions??P?.localActions??te?.localActions??ee?.localActions??["exec_approve","exec_reject"],ne,se,["connector_rollback","connector_upgrade_push",h,"configure_gateway_provider","skill_upload","skill_delete","skill_enable","skill_disable","skill_refresh",...d]),skills:ie,librarySkills:oe,terminalOutboxPath:He(r.name,r.agent_id)},agent:{command:c?"dsh-jsonrpc-agent":s.command,args:[...s.args??[],...r.args??[]],env:r.env,clientType:t,...j?{provider:j}:{}},adapterOptions:y,acpAuthMethod:y.auth_method,acpInitialMode:y.initial_mode,acpMcpTools:y.acp_mcp_tools,promptTimeoutMs:r.prompt_timeout_ms,bindingsPath:Ne(r.agent_id),legacyBindingsPath:Ge(r.name,r.agent_id),activeEventStorePath:Ue(r.name,r.agent_id),pendingEventStorePath:qe(r.agent_id),...s.enableSessionBinding||f?{enableSessionBinding:!0}:{},...s.autoInjectArgs||t==="reasonix"&&r.provider?.model?.trim()?{autoInjectArgs:{...s.autoInjectArgs??{},...t==="reasonix"&&r.provider?.model?.trim()?{model:r.provider.model.trim()}:{}}}:{},poolMaxSize:r.pool?.maxSize??(u==="deepseek-harness"?4:void 0),poolIdleTimeoutMs:r.pool?.idleTimeoutMs,eventQueue:Be(u,r.event_queue),logDir:A.log,providerBaseUrl:r.provider_base_url?.trim()||void 0,providerApiKey:r.provider_api_key?.trim()||void 0}}function Y(){const r=process.env.GRIX_AGENT_STARTUP_WAIT_MS,e=Number(r);return Number.isFinite(e)&&e>=500?Math.floor(e):Re}function E(){const r=process.env.GRIX_AGENT_STOP_WAIT_MS,e=Number(r);return Number.isFinite(e)&&e>=500?Math.floor(e):Oe}function Qe(){return Math.min(E(),je)}function $(r){if(r===null||typeof r!="object")return JSON.stringify(r)??"null";if(Array.isArray(r))return`[${r.map($).join(",")}]`;const e=r;return`{${Object.keys(e).sort().map(t=>`${JSON.stringify(t)}:${$(e[t])}`).join(",")}}`}function Ke(r,e){return $(r)===$(e)}class vt{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 V(A.config);sharedOwnersCache=new ue(A.data);installer=new Pe;relaySecretResolver;auditProducer=Te();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=E()){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=E()){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 V(n),p.info("manager",`Loading configs from ${n}`),ve(),B(),this.globalConfigStore=new _e(k(A.data,"agent-global-configs.json")),this.globalConfigStore.load();const{entries:t,fileErrors:s,skipped:i}=this.configRepository.scan();for(const a of s)p.error("manager",`Failed to load ${a.file}: ${a.error}`);for(const a of i)p.info("manager",`Skipped ${a}: not an agent config (no "agents" key)`);if(t.length===0&&s.length===0&&i.length===0){p.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:a,file:l}of t)try{const m=this.toRuntimeInstanceConfig(a);c.push({config:m,entry:a,file:l}),p.info("manager",`Loaded ${m.name} (${m.adapterType??"acp"}) from ${l}`)}catch(m){const h=typeof a?.name=="string"?a.name:"<unknown>";p.error("manager",`Invalid agent config in ${l} (name=${h}): ${m}`),o++}let g=0;if(c.length>0){const a=Y();p.info("manager",`Starting ${c.length} agent(s), startup wait=${a}ms`);const l=d=>{this.instances=this.instances.filter(u=>u!==d)},m=c.map(({config:d,entry:u,file:f})=>{const _=this.createConfiguredInstance(d);return this.instances.push(_),this.configMap.set(d.name,d),this.rawConfigMap.set(d.name,{entry:u,file:f}),{config:d,instance:_,startPromise:this.prepareProviderRuntime(d,u).then(()=>{if(this.stopping)throw this.requestStartCancellation(_),Object.assign(new Error("manager is stopping"),{code:"MANAGER_STOPPING"});return this.startTrackedInstance(_)})}}),h=await Promise.all(m.map(async d=>{const u=await new Promise(f=>{let _=!1;const y=setTimeout(()=>{_||(_=!0,f({kind:"timeout"}))},a);d.startPromise.then(()=>{_||(_=!0,clearTimeout(y),f({kind:"started"}))}).catch(S=>{_||(_=!0,clearTimeout(y),f({kind:"failed",error:S}))})});return{task:d,outcome:u}}));for(const{task:d,outcome:u}of h){if(u.kind==="started"){this.restoreCachedSharedInstances(d.config);continue}if(u.kind==="failed"){if(C(u.error)){this.onAgentDeletedByPlatform(d.instance);continue}try{await this.stopInstance(d.instance),l(d.instance),this.failedStartAgents.delete(d.config.name)}catch(f){this.failedStartAgents.add(d.config.name),p.error("manager",`Failed to clean up partially started ${d.config.name}: ${f}`)}p.error("manager",`Failed to start ${d.config.name}: ${u.error}`);continue}g++,p.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)&&(p.info("manager",`Delayed start succeeded: ${d.config.name}`),this.restoreCachedSharedInstances(d.config))}).catch(async f=>{if(C(f)){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(_){this.failedStartAgents.add(d.config.name),p.error("manager",`Failed to clean up delayed start ${d.config.name}: ${_}`)}p.error("manager",`Delayed start failed: ${d.config.name}: ${f}`)}})}if(this.instances.length>0){const d=Math.max(0,this.instances.length-g);p.info("manager",`${d}/${c.length} agent(s) running now`)}g>0&&p.warn("manager",`${g} 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 me(e,()=>this.hasAnyPendingOrBackgroundWork(),()=>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 fe(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){p.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 g=!1;try{g=o.forceRefreshSkills(void 0,{force:!0,ownerLibrarySync:!0})===!0}catch(a){p.warn("manager",`owner library skills reporter threw group=${t} agent=${o.name}: ${a}`)}if(g){i=!0;continue}p.warn("manager",`owner library skills reporter failed group=${t} agent=${o.name}; trying next`)}if(!i){p.warn("manager",`owner library skills: no reporter succeeded group=${t}`);continue}for(const o of c)try{o.adoptSkillsWireHashFromDisk()}catch(g){p.warn("manager",`adoptSkillsWireHashFromDisk failed for ${o.name}: ${g}`)}}}async stop(){p.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(g=>g!==o),this.startingInstances.delete(o)}catch(g){c.push(g)}}),...[...this.sharedInstances.entries()].map(async([o,g])=>{try{await(i.get(g)??this.stopInstance(g,void 0,t())),this.sharedInstances.get(o)===g&&this.sharedInstances.delete(o),this.startingInstances.delete(g)}catch(a){c.push(a)}})]);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),B(),c.length>0)throw new AggregateError(c,"Failed to stop all agents completely");p.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}=de(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 g;try{const a=le(e,c);g=new F(a,this.globalConfigStore,this.auditProducer),g.setLifecycleBusyChecker(()=>this.hasAnyPendingOrBackgroundWork()),g.setLifecycleAdmissionCloser(()=>this.closeAllLifecycleAdmission()),g.setLifecycleAdmissionRestorer(()=>this.reopenAllLifecycleAdmission()),this.options.requestGracefulShutdown&&g.setDaemonShutdownRequester(this.options.requestGracefulShutdown),this.lifecycleAdmissionClosed&&g.closeLifecycleAdmission(),this.sharedInstances.set(o,g),await this.startTrackedInstance(g),g.forceRefreshSkills(void 0,{force:!0}),p.info("manager",`shared instance started: ${o}`)}catch(a){if(g&&!this.stopping&&!this.removingAgents.has(e.name))try{await this.stopInstance(g),this.sharedInstances.get(o)===g&&this.sharedInstances.delete(o)}catch(m){p.error("manager",`cleanup shared instance failed ${o}: ${m}`)}const l=a instanceof Error?a.message:String(a);/Auth failed/i.test(l)?(this.sharedOwnersCache.remove(e.name,c),p.warn("manager",`shared instance auth rejected, removed from cache: ${o}`)):p.error("manager",`start shared instance failed ${o}: ${a}`)}}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),p.info("manager",`shared instance stopped: ${c}`)}catch(g){p.error("manager",`stop shared instance failed ${c}: ${g}`)}}}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)||(p.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(),p.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.hasAnyPendingOrBackgroundWork()),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(()=>X(this.getAgentProvider(e.name))),t}hasAnyPendingOrBackgroundWork(){return this.instances.some(e=>e.hasPendingOrBackgroundWork())||[...this.sharedInstances.values()].some(e=>e.hasPendingOrBackgroundWork())}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);W(n.client_type)&&t?.base_url?.trim()&&t?.api_key?.trim()&&t?.model?.trim()&&await Q(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=Y(),g=await new Promise(h=>{let d=!1;const u=setTimeout(()=>{d||(d=!0,h({kind:"timeout"}))},o);c.then(()=>{d||(d=!0,clearTimeout(u),h({kind:"started"}))},f=>{d||(d=!0,clearTimeout(u),h({kind:"failed",error:f}))})}),a=this.instanceGenerations.get(i);if(a?.platformDeletedObserved&&!a.persisted){const h=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([h,d],`Failed to clean up uncommitted deleted agent "${e.name}"`)}throw h}if(g.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(g.kind==="timeout"){this.configMap.set(e.name,e),this.rawConfigMap.set(e.name,{entry:n,file:t}),p.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(h=>h!==i)}catch(h){this.failedStartAgents.add(e.name),p.error("manager",`Failed to stop late-started ${e.name}: ${h}`)}return}this.failedStartAgents.delete(e.name),this.instanceGenerations.get(i)?.persisted&&this.restoreCachedSharedInstances(e),this.relayEnvSettledHandler?.(),p.info("manager",`Delayed start succeeded: ${e.name}`)}},async h=>{if(C(h)){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),p.error("manager",`Failed to clean up delayed start ${e.name}: ${d}`)}p.error("manager",`Delayed start failed ${e.name}: ${h}`)}}).catch(h=>{p.error("manager",`Delayed start finalizer failed ${e.name}: ${h}`)});return}const l=g.error;C(l)&&this.onAgentDeletedByPlatform(i);let m;try{await this.stopInstance(i),this.instances=this.instances.filter(h=>h!==i),this.failedStartAgents.delete(e.name)}catch(h){m=h,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),p.info("manager",`Removed agent: ${e}`),this.relayEnvSettledHandler?.()}async removeAgentInternal(e,n){this.removingAgents.add(e);try{const t=()=>n?.deadlineMs===void 0?E():Math.max(1,n.deadlineMs-Date.now()),s=this.instances.findIndex(h=>h.name===e),i=s===-1?void 0:this.instances[s],c=`${e}#shared:`,o=n?.keepShareCache?void 0:{dropPendingEvents:!0},g=new Map;i&&(this.requestStartCancellation(i),this.startingInstances.has(i)&&g.set(i,this.stopInstance(i,o,t())));for(const[h,d]of this.sharedInstances)h.startsWith(c)&&(this.requestStartCancellation(d),this.startingInstances.has(d)&&g.set(d,this.stopInstance(d,o,t())));const a=this.shareSyncChains.get(e);this.shareSyncChains.delete(e);const l=[];try{await this.waitForLifecycleDrain([...a?[a]:[],...g.values()],`agent "${e}" shared lifecycle drain`,t())}catch(h){l.push(h)}const m=[];for(const[h,d]of[...this.sharedInstances.entries()])h.startsWith(c)&&m.push((g.get(d)??this.stopInstance(d,o,t())).then(()=>{this.sharedInstances.get(h)===d&&this.sharedInstances.delete(h),this.startingInstances.delete(d)}).catch(u=>{l.push(u),p.error("manager",`stop shared instance failed ${h}: ${u}`)}));if(i)try{await(g.get(i)??this.stopInstance(i,o,t())),this.retireInstance(i);const h=this.instances.indexOf(i);h!==-1&&this.instances.splice(h,1),this.startingInstances.delete(i)}catch(h){l.push(h)}if(await Promise.all(m),!n?.keepShareCache){const h=K(e).catch(()=>{});if(n?.deadlineMs===void 0)await h;else try{await this.waitForLifecycleDrain([h],`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){p.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;p.warn("manager",`Agent "${s}" \u5DF2\u5728\u5E73\u53F0\u5220\u9664\uFF0C\u505C\u6B62\u5B9E\u4F8B\u5E76\u6E05\u7406\u672C\u5730\u914D\u7F6E`);const g=this.rawConfigMap.get(s),a=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 u=m.fileErrors.map(f=>`${f.file}: ${f.error}`).join("; ");throw new Error(`cannot inspect deleted agent generation [${u}]`)}const h=m.entries.filter(u=>String(u.entry?.name??"").trim()===s);if(!h.some(u=>String(u.entry?.agent_id??"").trim()!==i)&&h.length>0&&this.configRepository.remove(s),l.length===0){const u=String(this.rawConfigMap.get(s)?.entry.agent_id??"").trim();(!u||u===i)&&(this.rawConfigMap.delete(s),this.configMap.delete(s),this.failedStartAgents.delete(s))}}catch(m){l.push(m),g&&this.rawConfigMap.set(s,g),a&&this.configMap.set(s,a)}if(l.length>0)throw new AggregateError(l,`Failed to fully clean up deleted agent "${s}"`);p.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 We(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),p.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(([a])=>a!=="api_key")):n,c=this.configRepository.updateExisting(e,a=>{const l={...a};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 g=String(o.client_type??"").trim().toLowerCase();try{W(g)&&(n?.base_url?.trim()&&n?.api_key?.trim()&&n?.model?.trim()?await Q(e,{baseUrl:n.base_url.trim(),apiKey:n.api_key.trim(),model:n.model.trim()}):await K(e))}catch(a){throw this.failedStartAgents.add(e),this.instances.find(l=>l.name===e)?.markRelayEnvStale(),a}if(t?.deferRestart){this.instances.find(a=>a.name===e)?.markRelayEnvStale(),p.info("manager",`Updated provider config for agent (deferred restart): ${e}`);return}try{await this.replaceInstance(e,o,c.file)}catch(a){throw this.failedStartAgents.add(e),this.instances.find(l=>l.name===e)?.markRelayEnvStale(),a}p.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&&(Ie(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){p.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 a of t)p.info("manager",`reload: skipped ${a}: not an agent config (no "agents" key)`);if(n.length>0){const a=n.map(l=>`${l.file}: ${l.error}`).join("; ");throw Object.assign(new Error(`reload aborted: ${n.length} config file(s) failed to parse [${a}]`),{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:a}of e)try{this.toRuntimeInstanceConfig(a,{quiet:!0})}catch(l){const m=a?.name,h=typeof m=="string"&&m.trim()?m:"<unknown>";s.push({name:h,error:l instanceof Error?l.message:String(l)})}if(s.length>0){const a=s.map(l=>`${l.name}: ${l.error}`).join("; ");throw Object.assign(new Error(`reload aborted: ${s.length} agent config(s) invalid [${a}]`),{code:"RELOAD_UNSAFE"})}const i=new Map,c=new Set([...this.pendingPlatformDeletes.values()].map(a=>a.name));for(const a of e){const l=String(a.entry?.name??"").trim();l&&(c.has(l)||(i.has(l)&&p.warn("manager",`reload: duplicate agent name "${l}", last one wins`),i.set(l,a)))}const o={added:[],removed:[],restarted:[],unchanged:[],failed:[]},g=new Set([...this.rawConfigMap.keys()].filter(a=>!c.has(a)));for(const a of g)if(!i.has(a))try{await this.removeAgentInternal(a),o.removed.push(a)}catch(l){o.failed.push({name:a,error:l instanceof Error?l.message:String(l)})}for(const[a,l]of i){const m=this.rawConfigMap.get(a);if(m)if(!Ke(m.entry,l.entry)||!this.instances.some(h=>h.name===a)||this.failedStartAgents.has(a))try{await this.replaceInstance(a,l.entry,l.file),o.restarted.push(a)}catch(h){o.failed.push({name:a,error:h instanceof Error?h.message:String(h)})}else this.rawConfigMap.set(a,l),o.unchanged.push(a);else try{await this.addAgentInternal(l.entry,l.file,{targetPersisted:!0}),o.added.push(a)}catch(h){o.failed.push({name:a,error:h instanceof Error?h.message:String(h)})}}return p.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 ze(this.instances,e)}async probeOne(e,n={}){return Ve(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 ze(r,e){const n=e.concurrency??4,t=Date.now(),s=new Array(r.length);await new Promise(a=>{let l=0,m=0;const h=r.length;if(h===0){a();return}function d(_){const y=r[_];y.probe(e).then(S=>{s[_]=S,u()},S=>{s[_]={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}},u()})}function u(){m++,l<h?d(l++):m===h&&a()}const f=Math.min(n,h);for(let _=0;_<f;_++)d(l++)});const i=s.filter(a=>a.status==="healthy").length,c=s.filter(a=>a.status==="degraded").length,o=s.filter(a=>a.status==="unavailable").length,g=await z();return{ok:i===s.length&&s.length>0,total:s.length,healthy:i,degraded:c,unavailable:o,installed_clients:g,agents:s,probed_at:t,duration_ms:Date.now()-t}}async function Ve(r,e,n){const t=r.find(s=>s.name===e);if(!t)throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});return t.probe(n)}export{vt as Manager,X as normalizeAgentProviderForRuntime,z as probeInstalledClientCommands,ze as probeInstances,Ve as probeOneInstance};
|
|
1
|
+
import{join as k}from"node:path";import{homedir as ce}from"node:os";import{AgentInstance as F}from"./bridge/bridge.js";import{buildSharedInstanceConfig as le,diffSharedOwners as de,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 p}from"./core/log/index.js";import{resolveClientVersion as pe}from"./core/util/client-version.js";import{UpgradeChecker as me}from"./core/upgrade/upgrade-checker.js";import{SkillSyncer as fe}from"./core/skill-sync/skill-syncer.js";import{AgentGlobalConfigStore as _e}from"./core/persistence/agent-global-config-store.js";import{resolveSkillScanMode as we}from"./adapter/claude/skill-scanner.js";import{buildWireSkills as ye}from"./core/skill-sync/sync-state.js";import{buildLibrarySkills as Ae}from"./core/skill-sync/library-skills.js";import{AUDIT_LOCAL_ACTION_TYPES as Se}from"./core/aibot/types.js";import{buildReportedSkills as ke,logDefaultSkillsCheck as ve,cleanupProjectedSkills as B}from"./default-skills/index.js";import{buildProviderEnv as be,supportsKiroCwRelay as W,supportsNonMitmRelayConfig as Ie}from"./core/config/provider-env.js";import{resolveHermesHomeDir as Ce}from"./adapter/shared/native-provider-config.js";import{ensureKiroRelay as Q,stopKiroRelay as K}from"./core/proxy/kiro-relay.js";import{resolveCopilotCommand as Ee}from"./core/runtime/copilot-resolve.js";import{resolveCursorCommand as $e}from"./core/runtime/cursor-resolve.js";import{probeInstalledClientCommands as z}from"./core/probe/installed-clients.js";import{AgentInstaller as Pe}from"./core/installer/installer.js";import{reportInstallFailure as Me}from"./core/observability/sentry.js";import{createDefaultAuditProducer as Te}from"./audit/runtime/default-audit-recorder.js";import{normalizeCodexConfigOverrides as xe}from"./adapter/codex/config-overrides.js";import{resolveCodexDirectHome as De}from"./adapter/codex/direct-home.js";import{AgentConfigRepository as V}from"./core/config/agent-config-repository.js";function X(r){if(r)return{...r.provider_id?.trim()?{providerId:r.provider_id.trim()}:{},...r.base_url?.trim()?{baseUrl:r.base_url.trim()}:{},...r.quota_base_url?.trim()?{quotaBaseUrl:r.quota_base_url.trim()}:{},...r.api_key?.trim()?{apiKey:r.api_key.trim()}:{},...r.model?.trim()?{model:r.model.trim()}:{},...r.fast_model?.trim()?{fastModel:r.fast_model.trim()}:{},...r.effort?.trim()?{effort:r.effort.trim()}:{}}}const Re=8e3,Oe=1e4,je=8e3;function Le(r,e){switch(r){case"claude":return{adapterType:"claude",command:"claude"};case"codex":return{adapterType:"codex",command:"codex",options:{sandboxMode:"danger-full-access"}};case"gemini":return{adapterType:"acp",command:"gemini",autoInjectArgs:{acp:!0},enableSessionBinding:!0};case"qwen":return{adapterType:"acp",command:"qwen",adapterHint:"qwen/base",autoInjectArgs:{acp:!0},enableSessionBinding:!0};case"pi":return{adapterType:"pi",command:"pi"};case"cursor":return{adapterType:"cursor",command:$e()};case"reasonix":return{adapterType:"acp",command:"reasonix",args:["acp"],enableSessionBinding:!0};case"codewhale":return{adapterType:"codewhale",command:"codewhale",enableSessionBinding:!0};case"openhuman":return{adapterType:"openhuman",command:"openhuman-core",enableSessionBinding:!0};case"kiro":return{adapterType:"acp",command:"kiro-cli",args:["acp"],enableSessionBinding:!0};case"kimi":return{adapterType:"acp",command:"kimi",args:["acp"],options:{initial_mode:"yolo"},enableSessionBinding:!0};case"opencode":return{adapterType:"opencode",command:"opencode",args:["serve"],enableSessionBinding:!0};case"copilot":{const n=Ee();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"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: ${r}`)}}function v(r,e){return String(r??"").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 Ne(r){return k(A.data,`session-bindings-${v(r,"unknown")}.json`)}function Ge(r,e){return k(A.data,`session-bindings-${v(r,e)}.json`)}function Ue(r,e){return k(A.data,`active-events-${v(r,e)}.json`)}function qe(r){return k(A.data,`pending-events-${v(r,"unknown")}.json`)}function He(r,e){const n=v(r,"agent"),t=v(e,"unknown");return k(A.data,`terminal-outbox-${n}-${t}.json`)}function Fe(...r){const e=[],n=new Set;for(const t of r)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 Be(r,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[r]??n.acp;return{maxConcurrent:e?.max_concurrent??t.maxConcurrent??1,maxQueued:e?.max_queued??t.maxQueued??99,queueTimeoutMs:e?.queue_timeout_ms??t.queueTimeoutMs??0,cancelableQueued:!0,cancelableRunning:!0}}function We(r,e={}){const n=pe(),t=String(r.client_type??"").trim().toLowerCase(),s=Le(t,r),i=t==="deepseek"?r.dsh?.integration_mode??"profile_bridge":void 0,c=i==="embedded_jsonrpc",o=String(r.ws_url??"").trim(),g=typeof r.name=="string"?r.name:"",a=g.trim(),l="get_session_usage",m="get_rate_limits",h="get_agent_global_config",d=Se;if(!a)throw new Error("agent name is required");if(g!==a)throw new Error(`agent name must not contain leading or trailing whitespace: "${g}"`);if(!o)throw new Error(`agent ${r.name}: ws_url is required`);if(!r.agent_id?.trim())throw new Error(`agent ${r.name}: agent_id is required`);if(!r.api_key?.trim())throw new Error(`agent ${r.name}: api_key is required`);const u=s.adapterType,f=u==="acp",_=t==="qwen",y={...s.options??{}};if(u==="deepseek-harness"&&(y.integrationMode=i,y.dshProfile=r.dsh?.profile?.trim()||"web",y.autoInstallBridge=r.dsh?.auto_install_bridge??!c,y.autoStartProfile=r.dsh?.auto_start_profile??!c),u==="deepseek-harness"&&r.provider?.model?.trim()&&(y.defaultModel=r.provider.model.trim()),r.codex_config_overrides!==void 0){if(u!=="codex")throw new Error(`agent ${r.name}: codex_config_overrides is only supported for client_type=codex`);xe(r.codex_config_overrides,`agent ${r.name}: codex_config_overrides`),y.codexConfigOverrides=r.codex_config_overrides}if(u==="codex"&&r.provider?.wire_api!==void 0){const w=r.provider;if(w.wire_api!=="responses"||!w.base_url?.trim()||!w.api_key?.trim()||!w.model?.trim()||!w.catalog_version?.trim()||!w.catalog_sha256?.trim()||!w.catalog_json?.trim())throw Object.assign(new Error(`agent ${r.name}: incomplete Codex direct relay config`),{code:"INVALID_CODEX_DIRECT_CONFIG"});const H=De(A.data,r.name);y.codexHome=H,y.codexDirect={codexHome:H,config:{baseUrl:w.base_url.trim(),model:w.model.trim(),...w.reasoning_effort?.trim()?{reasoningEffort:w.reasoning_effort.trim()}:{},token:w.api_key.trim()},catalog:{version:w.catalog_version.trim(),sha256:w.catalog_sha256.trim(),json:w.catalog_json}}}const S=u==="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,J=u==="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,P=_?{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=u==="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,T=u==="openhuman"?{adapterHint:"openhuman/base",capabilities:["local_action_v1"],localActions:["session_control","set_model","file_list","create_folder"]}:null,x=u==="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,L=u==="codewhale"?{capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","file_list","create_folder",l]}:null,D=u==="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,R=u==="agy"?{adapterHint:"agy/base",capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","file_list","create_folder",m]}:null,O=u==="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,N=new Set(["hermes","reasonix","kimi","copilot","acp"]),Z=f&&!N.has(t),ee=f&&!_?{localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","claude_interaction_reply","file_list","create_folder",...Z?[l]:[]]}:null,te=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,ne=u==="codex"||u==="claude"||u==="opencode"||u==="deepseek-harness"||t==="gemini"?["session_control","set_model","set_mode",...u==="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"]:[],...u==="claude"?["set_reasoning_effort"]:[]]:void 0,se=u==="cursor"||u==="opencode"||u==="openhuman"||u==="agy"||f&&N.has(t)?[m]:[l,m];f&&y.raw_transport===void 0&&(y.raw_transport=t==="gemini");const re=`${t}/base`,I=we(u,t),ae=I==="kiro"?void 0:process.cwd(),j=X(r.provider),b={...process.env,...r.env};if(I==="hermes"&&!b.HERMES_HOME?.trim()){const w=be(t,j);b.HERMES_HOME=Ce({...w,...b},r.name)}const G=ke({mode:I,projectDir:ae,env:b}),U=G.filter(w=>w.source==="connector").map(w=>w.name);U.length>0&&!e.quiet&&p.info("manager",`[${r.name}] injecting connector skills: [${U.join(", ")}]`);const q=ye(G,A.skills);let ie=q.length>0?q:void 0;const oe=Ae({skillsDir:A.skills,mode:I,home:ce(),env:b});return{name:a,adapterType:u,aibot:{url:o,agentId:r.agent_id,apiKey:r.api_key,clientType:t,clientVersion:n,adapterHint:O?.adapterHint??s.adapterHint??P?.adapterHint??M?.adapterHint??T?.adapterHint??x?.adapterHint??D?.adapterHint??R?.adapterHint??re,capabilities:O?.capabilities??S?.capabilities??L?.capabilities??M?.capabilities??T?.capabilities??x?.capabilities??D?.capabilities??R?.capabilities??P?.capabilities??["stream_chunk","local_action_v1","connector_upgrade"],localActions:Fe(O?.localActions??S?.localActions??L?.localActions??J?.localActions??M?.localActions??T?.localActions??x?.localActions??D?.localActions??R?.localActions??P?.localActions??te?.localActions??ee?.localActions??["exec_approve","exec_reject"],ne,se,["connector_rollback","connector_upgrade_push",h,"configure_gateway_provider","skill_upload","skill_delete","skill_enable","skill_disable","skill_refresh",...d]),skills:ie,librarySkills:oe,terminalOutboxPath:He(r.name,r.agent_id)},agent:{command:c?"dsh-jsonrpc-agent":s.command,args:[...s.args??[],...r.args??[]],env:r.env,clientType:t,...j?{provider:j}:{}},adapterOptions:y,acpAuthMethod:y.auth_method,acpInitialMode:y.initial_mode,acpMcpTools:y.acp_mcp_tools,promptTimeoutMs:r.prompt_timeout_ms,bindingsPath:Ne(r.agent_id),legacyBindingsPath:Ge(r.name,r.agent_id),activeEventStorePath:Ue(r.name,r.agent_id),pendingEventStorePath:qe(r.agent_id),...s.enableSessionBinding||f?{enableSessionBinding:!0}:{},...s.autoInjectArgs||t==="reasonix"&&r.provider?.model?.trim()?{autoInjectArgs:{...s.autoInjectArgs??{},...t==="reasonix"&&r.provider?.model?.trim()?{model:r.provider.model.trim()}:{}}}:{},poolMaxSize:r.pool?.maxSize??(u==="deepseek-harness"?4:void 0),poolIdleTimeoutMs:r.pool?.idleTimeoutMs,eventQueue:Be(u,r.event_queue),logDir:A.log,providerBaseUrl:r.provider_base_url?.trim()||void 0,providerApiKey:r.provider_api_key?.trim()||void 0}}function Y(){const r=process.env.GRIX_AGENT_STARTUP_WAIT_MS,e=Number(r);return Number.isFinite(e)&&e>=500?Math.floor(e):Re}function E(){const r=process.env.GRIX_AGENT_STOP_WAIT_MS,e=Number(r);return Number.isFinite(e)&&e>=500?Math.floor(e):Oe}function Qe(){return Math.min(E(),je)}function $(r){if(r===null||typeof r!="object")return JSON.stringify(r)??"null";if(Array.isArray(r))return`[${r.map($).join(",")}]`;const e=r;return`{${Object.keys(e).sort().map(t=>`${JSON.stringify(t)}:${$(e[t])}`).join(",")}}`}function Ke(r,e){return $(r)===$(e)}class vt{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 V(A.config);sharedOwnersCache=new ue(A.data);installer=new Pe;relaySecretResolver;auditProducer=Te();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=E()){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=E()){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 V(n),p.info("manager",`Loading configs from ${n}`),ve(),B(),this.globalConfigStore=new _e(k(A.data,"agent-global-configs.json")),this.globalConfigStore.load();const{entries:t,fileErrors:s,skipped:i}=this.configRepository.scan();for(const a of s)p.error("manager",`Failed to load ${a.file}: ${a.error}`);for(const a of i)p.info("manager",`Skipped ${a}: not an agent config (no "agents" key)`);if(t.length===0&&s.length===0&&i.length===0){p.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:a,file:l}of t)try{const m=this.toRuntimeInstanceConfig(a);c.push({config:m,entry:a,file:l}),p.info("manager",`Loaded ${m.name} (${m.adapterType??"acp"}) from ${l}`)}catch(m){const h=typeof a?.name=="string"?a.name:"<unknown>";p.error("manager",`Invalid agent config in ${l} (name=${h}): ${m}`),o++}let g=0;if(c.length>0){const a=Y();p.info("manager",`Starting ${c.length} agent(s), startup wait=${a}ms`);const l=d=>{this.instances=this.instances.filter(u=>u!==d)},m=c.map(({config:d,entry:u,file:f})=>{const _=this.createConfiguredInstance(d);return this.instances.push(_),this.configMap.set(d.name,d),this.rawConfigMap.set(d.name,{entry:u,file:f}),{config:d,instance:_,startPromise:this.prepareProviderRuntime(d,u).then(()=>{if(this.stopping)throw this.requestStartCancellation(_),Object.assign(new Error("manager is stopping"),{code:"MANAGER_STOPPING"});return this.startTrackedInstance(_)})}}),h=await Promise.all(m.map(async d=>{const u=await new Promise(f=>{let _=!1;const y=setTimeout(()=>{_||(_=!0,f({kind:"timeout"}))},a);d.startPromise.then(()=>{_||(_=!0,clearTimeout(y),f({kind:"started"}))}).catch(S=>{_||(_=!0,clearTimeout(y),f({kind:"failed",error:S}))})});return{task:d,outcome:u}}));for(const{task:d,outcome:u}of h){if(u.kind==="started"){this.restoreCachedSharedInstances(d.config);continue}if(u.kind==="failed"){if(C(u.error)){this.onAgentDeletedByPlatform(d.instance);continue}try{await this.stopInstance(d.instance),l(d.instance),this.failedStartAgents.delete(d.config.name)}catch(f){this.failedStartAgents.add(d.config.name),p.error("manager",`Failed to clean up partially started ${d.config.name}: ${f}`)}p.error("manager",`Failed to start ${d.config.name}: ${u.error}`);continue}g++,p.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)&&(p.info("manager",`Delayed start succeeded: ${d.config.name}`),this.restoreCachedSharedInstances(d.config))}).catch(async f=>{if(C(f)){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(_){this.failedStartAgents.add(d.config.name),p.error("manager",`Failed to clean up delayed start ${d.config.name}: ${_}`)}p.error("manager",`Delayed start failed: ${d.config.name}: ${f}`)}})}if(this.instances.length>0){const d=Math.max(0,this.instances.length-g);p.info("manager",`${d}/${c.length} agent(s) running now`)}g>0&&p.warn("manager",`${g} 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 me(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 fe(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){p.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 g=!1;try{g=o.forceRefreshSkills(void 0,{force:!0,ownerLibrarySync:!0})===!0}catch(a){p.warn("manager",`owner library skills reporter threw group=${t} agent=${o.name}: ${a}`)}if(g){i=!0;continue}p.warn("manager",`owner library skills reporter failed group=${t} agent=${o.name}; trying next`)}if(!i){p.warn("manager",`owner library skills: no reporter succeeded group=${t}`);continue}for(const o of c)try{o.adoptSkillsWireHashFromDisk()}catch(g){p.warn("manager",`adoptSkillsWireHashFromDisk failed for ${o.name}: ${g}`)}}}async stop(){p.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(g=>g!==o),this.startingInstances.delete(o)}catch(g){c.push(g)}}),...[...this.sharedInstances.entries()].map(async([o,g])=>{try{await(i.get(g)??this.stopInstance(g,void 0,t())),this.sharedInstances.get(o)===g&&this.sharedInstances.delete(o),this.startingInstances.delete(g)}catch(a){c.push(a)}})]);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),B(),c.length>0)throw new AggregateError(c,"Failed to stop all agents completely");p.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}=de(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 g;try{const a=le(e,c);g=new F(a,this.globalConfigStore,this.auditProducer),g.setLifecycleBusyChecker(()=>this.describeAnyPendingOrBackgroundWork()),g.setLifecycleAdmissionCloser(()=>this.closeAllLifecycleAdmission()),g.setLifecycleAdmissionRestorer(()=>this.reopenAllLifecycleAdmission()),this.options.requestGracefulShutdown&&g.setDaemonShutdownRequester(this.options.requestGracefulShutdown),this.lifecycleAdmissionClosed&&g.closeLifecycleAdmission(),this.sharedInstances.set(o,g),await this.startTrackedInstance(g),g.forceRefreshSkills(void 0,{force:!0}),p.info("manager",`shared instance started: ${o}`)}catch(a){if(g&&!this.stopping&&!this.removingAgents.has(e.name))try{await this.stopInstance(g),this.sharedInstances.get(o)===g&&this.sharedInstances.delete(o)}catch(m){p.error("manager",`cleanup shared instance failed ${o}: ${m}`)}const l=a instanceof Error?a.message:String(a);/Auth failed/i.test(l)?(this.sharedOwnersCache.remove(e.name,c),p.warn("manager",`shared instance auth rejected, removed from cache: ${o}`)):p.error("manager",`start shared instance failed ${o}: ${a}`)}}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),p.info("manager",`shared instance stopped: ${c}`)}catch(g){p.error("manager",`stop shared instance failed ${c}: ${g}`)}}}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)||(p.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(),p.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(()=>X(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);W(n.client_type)&&t?.base_url?.trim()&&t?.api_key?.trim()&&t?.model?.trim()&&await Q(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=Y(),g=await new Promise(h=>{let d=!1;const u=setTimeout(()=>{d||(d=!0,h({kind:"timeout"}))},o);c.then(()=>{d||(d=!0,clearTimeout(u),h({kind:"started"}))},f=>{d||(d=!0,clearTimeout(u),h({kind:"failed",error:f}))})}),a=this.instanceGenerations.get(i);if(a?.platformDeletedObserved&&!a.persisted){const h=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([h,d],`Failed to clean up uncommitted deleted agent "${e.name}"`)}throw h}if(g.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(g.kind==="timeout"){this.configMap.set(e.name,e),this.rawConfigMap.set(e.name,{entry:n,file:t}),p.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(h=>h!==i)}catch(h){this.failedStartAgents.add(e.name),p.error("manager",`Failed to stop late-started ${e.name}: ${h}`)}return}this.failedStartAgents.delete(e.name),this.instanceGenerations.get(i)?.persisted&&this.restoreCachedSharedInstances(e),this.relayEnvSettledHandler?.(),p.info("manager",`Delayed start succeeded: ${e.name}`)}},async h=>{if(C(h)){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),p.error("manager",`Failed to clean up delayed start ${e.name}: ${d}`)}p.error("manager",`Delayed start failed ${e.name}: ${h}`)}}).catch(h=>{p.error("manager",`Delayed start finalizer failed ${e.name}: ${h}`)});return}const l=g.error;C(l)&&this.onAgentDeletedByPlatform(i);let m;try{await this.stopInstance(i),this.instances=this.instances.filter(h=>h!==i),this.failedStartAgents.delete(e.name)}catch(h){m=h,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),p.info("manager",`Removed agent: ${e}`),this.relayEnvSettledHandler?.()}async removeAgentInternal(e,n){this.removingAgents.add(e);try{const t=()=>n?.deadlineMs===void 0?E():Math.max(1,n.deadlineMs-Date.now()),s=this.instances.findIndex(h=>h.name===e),i=s===-1?void 0:this.instances[s],c=`${e}#shared:`,o=n?.keepShareCache?void 0:{dropPendingEvents:!0},g=new Map;i&&(this.requestStartCancellation(i),this.startingInstances.has(i)&&g.set(i,this.stopInstance(i,o,t())));for(const[h,d]of this.sharedInstances)h.startsWith(c)&&(this.requestStartCancellation(d),this.startingInstances.has(d)&&g.set(d,this.stopInstance(d,o,t())));const a=this.shareSyncChains.get(e);this.shareSyncChains.delete(e);const l=[];try{await this.waitForLifecycleDrain([...a?[a]:[],...g.values()],`agent "${e}" shared lifecycle drain`,t())}catch(h){l.push(h)}const m=[];for(const[h,d]of[...this.sharedInstances.entries()])h.startsWith(c)&&m.push((g.get(d)??this.stopInstance(d,o,t())).then(()=>{this.sharedInstances.get(h)===d&&this.sharedInstances.delete(h),this.startingInstances.delete(d)}).catch(u=>{l.push(u),p.error("manager",`stop shared instance failed ${h}: ${u}`)}));if(i)try{await(g.get(i)??this.stopInstance(i,o,t())),this.retireInstance(i);const h=this.instances.indexOf(i);h!==-1&&this.instances.splice(h,1),this.startingInstances.delete(i)}catch(h){l.push(h)}if(await Promise.all(m),!n?.keepShareCache){const h=K(e).catch(()=>{});if(n?.deadlineMs===void 0)await h;else try{await this.waitForLifecycleDrain([h],`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){p.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;p.warn("manager",`Agent "${s}" \u5DF2\u5728\u5E73\u53F0\u5220\u9664\uFF0C\u505C\u6B62\u5B9E\u4F8B\u5E76\u6E05\u7406\u672C\u5730\u914D\u7F6E`);const g=this.rawConfigMap.get(s),a=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 u=m.fileErrors.map(f=>`${f.file}: ${f.error}`).join("; ");throw new Error(`cannot inspect deleted agent generation [${u}]`)}const h=m.entries.filter(u=>String(u.entry?.name??"").trim()===s);if(!h.some(u=>String(u.entry?.agent_id??"").trim()!==i)&&h.length>0&&this.configRepository.remove(s),l.length===0){const u=String(this.rawConfigMap.get(s)?.entry.agent_id??"").trim();(!u||u===i)&&(this.rawConfigMap.delete(s),this.configMap.delete(s),this.failedStartAgents.delete(s))}}catch(m){l.push(m),g&&this.rawConfigMap.set(s,g),a&&this.configMap.set(s,a)}if(l.length>0)throw new AggregateError(l,`Failed to fully clean up deleted agent "${s}"`);p.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 We(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),p.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(([a])=>a!=="api_key")):n,c=this.configRepository.updateExisting(e,a=>{const l={...a};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 g=String(o.client_type??"").trim().toLowerCase();try{W(g)&&(n?.base_url?.trim()&&n?.api_key?.trim()&&n?.model?.trim()?await Q(e,{baseUrl:n.base_url.trim(),apiKey:n.api_key.trim(),model:n.model.trim()}):await K(e))}catch(a){throw this.failedStartAgents.add(e),this.instances.find(l=>l.name===e)?.markRelayEnvStale(),a}if(t?.deferRestart){this.instances.find(a=>a.name===e)?.markRelayEnvStale(),p.info("manager",`Updated provider config for agent (deferred restart): ${e}`);return}try{await this.replaceInstance(e,o,c.file)}catch(a){throw this.failedStartAgents.add(e),this.instances.find(l=>l.name===e)?.markRelayEnvStale(),a}p.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&&(Ie(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){p.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 a of t)p.info("manager",`reload: skipped ${a}: not an agent config (no "agents" key)`);if(n.length>0){const a=n.map(l=>`${l.file}: ${l.error}`).join("; ");throw Object.assign(new Error(`reload aborted: ${n.length} config file(s) failed to parse [${a}]`),{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:a}of e)try{this.toRuntimeInstanceConfig(a,{quiet:!0})}catch(l){const m=a?.name,h=typeof m=="string"&&m.trim()?m:"<unknown>";s.push({name:h,error:l instanceof Error?l.message:String(l)})}if(s.length>0){const a=s.map(l=>`${l.name}: ${l.error}`).join("; ");throw Object.assign(new Error(`reload aborted: ${s.length} agent config(s) invalid [${a}]`),{code:"RELOAD_UNSAFE"})}const i=new Map,c=new Set([...this.pendingPlatformDeletes.values()].map(a=>a.name));for(const a of e){const l=String(a.entry?.name??"").trim();l&&(c.has(l)||(i.has(l)&&p.warn("manager",`reload: duplicate agent name "${l}", last one wins`),i.set(l,a)))}const o={added:[],removed:[],restarted:[],unchanged:[],failed:[]},g=new Set([...this.rawConfigMap.keys()].filter(a=>!c.has(a)));for(const a of g)if(!i.has(a))try{await this.removeAgentInternal(a),o.removed.push(a)}catch(l){o.failed.push({name:a,error:l instanceof Error?l.message:String(l)})}for(const[a,l]of i){const m=this.rawConfigMap.get(a);if(m)if(!Ke(m.entry,l.entry)||!this.instances.some(h=>h.name===a)||this.failedStartAgents.has(a))try{await this.replaceInstance(a,l.entry,l.file),o.restarted.push(a)}catch(h){o.failed.push({name:a,error:h instanceof Error?h.message:String(h)})}else this.rawConfigMap.set(a,l),o.unchanged.push(a);else try{await this.addAgentInternal(l.entry,l.file,{targetPersisted:!0}),o.added.push(a)}catch(h){o.failed.push({name:a,error:h instanceof Error?h.message:String(h)})}}return p.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 ze(this.instances,e)}async probeOne(e,n={}){return Ve(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 ze(r,e){const n=e.concurrency??4,t=Date.now(),s=new Array(r.length);await new Promise(a=>{let l=0,m=0;const h=r.length;if(h===0){a();return}function d(_){const y=r[_];y.probe(e).then(S=>{s[_]=S,u()},S=>{s[_]={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}},u()})}function u(){m++,l<h?d(l++):m===h&&a()}const f=Math.min(n,h);for(let _=0;_<f;_++)d(l++)});const i=s.filter(a=>a.status==="healthy").length,c=s.filter(a=>a.status==="degraded").length,o=s.filter(a=>a.status==="unavailable").length,g=await z();return{ok:i===s.length&&s.length>0,total:s.length,healthy:i,degraded:c,unavailable:o,installed_clients:g,agents:s,probed_at:t,duration_ms:Date.now()-t}}async function Ve(r,e,n){const t=r.find(s=>s.name===e);if(!t)throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});return t.probe(n)}export{vt as Manager,X as normalizeAgentProviderForRuntime,z as probeInstalledClientCommands,ze as probeInstances,Ve as probeOneInstance};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
function a(
|
|
1
|
+
function a(e){const t=new Set([`http://127.0.0.1:${e.serverPort}`,`http://localhost:${e.serverPort}`,...e.allowedOrigins]),o=new Set([`127.0.0.1:${e.serverPort}`,`localhost:${e.serverPort}`,...e.allowedHosts]);return{validateRequest(s){const r=i(s,t);if(!r.ok)return r;const n=l(s,o);return n.ok?{ok:!0}:n}}}function i(e,t){const o=e.headers.origin;return o?t.has(o)?{ok:!0}:{ok:!1,statusCode:403,message:`Origin not allowed: ${o}`}:{ok:!0}}function l(e,t){const o=e.headers.host;return o?t.has(o)?{ok:!0}:{ok:!1,statusCode:403,message:`Host not allowed: ${o}`}:{ok:!1,statusCode:403,message:"Missing Host header"}}export{a as createSecurityPolicy};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{openSync as
|
|
2
|
-
`;for(let d=0;d<=n;d+=1)try{const
|
|
1
|
+
import{openSync as x,closeSync as D,existsSync as y,readFileSync as s,unlinkSync as h,writeSync as E}from"node:fs";import{mkdir as L}from"node:fs/promises";import{randomUUID as _}from"node:crypto";import f from"node:path";import c from"node:process";import{getProcessCommandLine as m,isProcessRunning as l}from"../service/process-control.js";function w(r){try{const e=JSON.parse(r);if(typeof e?.pid=="number"&&typeof e.acquired_at=="number")return e}catch{}return null}function p(r){if(!y(r))return null;try{return w(s(r,"utf8"))}catch{return null}}function M(r){const e=p(r);return e!==null&&l(e.pid)}class k extends Error{holder;constructor(e){super(`another grix-connector daemon (pid ${e.pid}) is already serving this root; yielding`),this.holder=e,this.name="DaemonAlreadyServingError"}}function O(r,e=2e3,n=2){return async t=>{let u="19579";try{const a=f.join(r,"health-port");y(a)&&(u=s(a,"utf8").trim()||u)}catch{}for(let a=0;a<n;a+=1)try{const d=await fetch(`http://127.0.0.1:${u}/healthz`,{signal:AbortSignal.timeout(e)});if(!d.ok)return!1;const i=await d.json();return i.status==="ok"&&i.pid===t.pid}catch{}return!1}}function S(r){const e=new Int32Array(new SharedArrayBuffer(4));Atomics.wait(e,0,0,r)}function g(r){const e=typeof r.command_line=="string"&&r.command_line?r.command_line:null;if(e===null)return!0;const n=m(r.pid);return n===null?!0:n===e}function j(r){const e=typeof r.command_line=="string"&&r.command_line?r.command_line:null;if(e===null)return!1;const n=m(r.pid);return n!==null&&n===e}function z(r,e={}){const n=e.selfPid??c.pid,t=(e.readLock??p)(r);return!t||t.pid===n||!(e.isRunning??l)(t.pid)?null:(e.isSameProcess??g)(t)?t:null}function v(r){try{return c.kill(r,0),!0}catch(e){return e?.code!=="EPERM"}}function I(r){if(r<=0||r===c.pid)return!0;try{c.kill(r,"SIGTERM")}catch(t){return t?.code!=="EPERM"}const e=Date.now()+3e3;for(;Date.now()<e;){if(!l(r))return!0;S(100)}try{c.kill(r,"SIGKILL")}catch{}const n=Date.now()+1e3;for(;Date.now()<n;){if(!l(r))return!0;S(50)}return!l(r)}async function G(r,e,n=2,t={}){await L(f.dirname(r),{recursive:!0});const u={pid:c.pid,acquired_at:Date.now(),root_dir:f.resolve(e),instance_id:_(),command_line:m(c.pid)},a=`${JSON.stringify(u,null,2)}
|
|
2
|
+
`;for(let d=0;d<=n;d+=1)try{const i=x(r,"wx");try{E(i,a)}finally{D(i)}return u}catch(i){if(!(i instanceof Error)||i.code!=="EEXIST")throw i;let o=null;try{o=w(s(r,"utf8"))}catch{}if(o&&l(o.pid)){if(o.pid===c.pid)throw new Error(`daemon already running (pid ${o.pid}, acquired at ${new Date(o.acquired_at).toISOString()})`);if(v(o.pid)&&g(o)){if(t.isHolderServing&&await t.isHolderServing(o))throw new k(o);if(!I(o.pid))throw new Error(`\u65E0\u6CD5\u7EC8\u6B62\u8001\u8FDB\u7A0B (pid ${o.pid})\uFF0C\u8BF7\u624B\u52A8\u5904\u7406`)}}try{h(r)}catch{}}throw new Error("failed to acquire daemon lock after retries")}async function J(r){const e=p(r);if(e&&e.pid===c.pid)try{h(r)}catch{}}export{k as DaemonAlreadyServingError,G as acquireDaemonLock,O as createHealthzHolderProbe,z as findLiveLockHolder,M as isDaemonLocked,j as isLockHolderIdentityVerified,g as isLockHolderSameProcess,p as readDaemonLock,J as releaseDaemonLock};
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import{existsSync as
|
|
1
|
+
import{existsSync as C,readFileSync as V}from"node:fs";import{mkdir as v,readdir as I,readFile as P,rm as y,stat as it,writeFile as $}from"node:fs/promises";import p from"node:os";import c from"node:path";import{fileURLToPath as ot,pathToFileURL as at}from"node:url";import{GRIX_HOME_ENV as T}from"../core/config/paths.js";import{isProcessRunning as R,runCommand as f,spawnDetached as _,isWindowsElevated as st,killProcessesByCommandLine as X,findProcessesByCommandLine as ct}from"./process-control.js";import{getServicePrefix as L,parseConfigDirFromPlistXML as lt,parseConfigDirFromSystemdUnit as ut,resolveBareDaemonMarkerPath as S,resolveLinuxUserUnitPath as dt,resolveMacOSLaunchAgentPath as mt}from"./service-paths.js";import{LAUNCHER_FILE as pt,readPointer as ft,versionDir as ht}from"../core/upgrade/version-store.js";function w(t){return String(t??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}function D(t){return`'${String(t??"").replace(/'/g,"'\\''")}'`}function Q(t){return String(t??"").replace(/\\/g,"\\\\").replace(/"/g,'\\"').replace(/\n/g,"\\n").replace(/\r/g,"\\r")}function z(t){return`"${Q(t).replace(/%/g,"%%")}"`}function O(t){return String(t??"").replace(/%/g,"%%")}function gt(t){return`"${Q(t).replace(/\$/g,()=>"$$").replace(/%/g,"%%")}"`}function j(t=process.env){const n=[];for(const e of["GRIX_HEALTH_PORT","GRIX_ADMIN_PORT"]){const r=String(t[e]??"").trim();/^\d{2,5}$/.test(r)&&n.push([e,r])}return n}function J(t){return[t.nodePath,t.cliPath,...t.configDir?["--config-dir",t.configDir]:[]]}function wt(t){const n=J(t);return`<?xml version="1.0" encoding="UTF-8"?>
|
|
2
2
|
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3
3
|
<plist version="1.0">
|
|
4
4
|
<dict>
|
|
5
5
|
<key>Label</key>
|
|
6
|
-
<string>${
|
|
6
|
+
<string>${w(t.serviceID)}</string>
|
|
7
7
|
<key>ProgramArguments</key>
|
|
8
8
|
<array>
|
|
9
|
-
${
|
|
9
|
+
${n.map(e=>` <string>${w(e)}</string>`).join(`
|
|
10
10
|
`)}
|
|
11
11
|
</array>
|
|
12
12
|
<key>RunAtLoad</key>
|
|
@@ -17,33 +17,33 @@ ${e.map(n=>` <string>${g(n)}</string>`).join(`
|
|
|
17
17
|
<key>ThrottleInterval</key>
|
|
18
18
|
<integer>30</integer>
|
|
19
19
|
<key>WorkingDirectory</key>
|
|
20
|
-
<string>${
|
|
20
|
+
<string>${w(c.dirname(t.cliPath))}</string>
|
|
21
21
|
<key>EnvironmentVariables</key>
|
|
22
22
|
<dict>
|
|
23
|
-
<key>${
|
|
24
|
-
<string>${
|
|
25
|
-
${j().map(([
|
|
23
|
+
<key>${T}</key>
|
|
24
|
+
<string>${w(t.rootDir)}</string>
|
|
25
|
+
${j().map(([e,r])=>` <key>${e}</key>
|
|
26
26
|
<string>${r}</string>
|
|
27
27
|
`).join("")}${t.environmentPath?` <key>PATH</key>
|
|
28
|
-
<string>${
|
|
28
|
+
<string>${w(t.environmentPath)}</string>
|
|
29
29
|
`:""} </dict>
|
|
30
30
|
<key>StandardOutPath</key>
|
|
31
|
-
<string>${
|
|
31
|
+
<string>${w(t.stdoutPath)}</string>
|
|
32
32
|
<key>StandardErrorPath</key>
|
|
33
|
-
<string>${
|
|
33
|
+
<string>${w(t.stderrPath)}</string>
|
|
34
34
|
</dict>
|
|
35
35
|
</plist>
|
|
36
|
-
`}function
|
|
36
|
+
`}function yt(t){const n=J(t).map(e=>gt(e)).join(" ");return`[Unit]
|
|
37
37
|
Description=grix-connector daemon (${t.serviceID})
|
|
38
38
|
After=network.target
|
|
39
39
|
|
|
40
40
|
[Service]
|
|
41
41
|
Type=simple
|
|
42
|
-
ExecStart=${
|
|
43
|
-
WorkingDirectory=${O(
|
|
44
|
-
Environment=${
|
|
45
|
-
${j().map(([
|
|
46
|
-
`).join("")}${t.environmentPath?`Environment=${
|
|
42
|
+
ExecStart=${n}
|
|
43
|
+
WorkingDirectory=${O(c.dirname(t.cliPath))}
|
|
44
|
+
Environment=${z(`${T}=${t.rootDir}`)}
|
|
45
|
+
${j().map(([e,r])=>`Environment=${e}=${r}
|
|
46
|
+
`).join("")}${t.environmentPath?`Environment=${z(`PATH=${t.environmentPath}`)}
|
|
47
47
|
`:""}
|
|
48
48
|
Restart=always
|
|
49
49
|
RestartSec=30
|
|
@@ -52,8 +52,8 @@ StandardError=append:${O(t.stderrPath)}
|
|
|
52
52
|
|
|
53
53
|
[Install]
|
|
54
54
|
WantedBy=default.target
|
|
55
|
-
`}function
|
|
56
|
-
`);return{path:
|
|
55
|
+
`}function x(t,n){const e=t.replace(/[^a-zA-Z0-9._-]/g,"_");return c.join(n,".grix",`${e}-wrapper.vbs`)}function M(t,n){const e=t.replace(/[^a-zA-Z0-9._-]/g,"_"),r=c.join(n,"AppData","Roaming","Microsoft","Windows","Start Menu","Programs","Startup");return c.join(r,`${e}.lnk`)}function m(t){return String(t??"").replace(/"/g,'""')}function A(t){return`'${String(t??"").replace(/'/g,"''")}'`}function $t(t,n){const e=c.join(c.dirname(t),c.basename(n).replace(/\.lnk$/,"-create.vbs")),r=["Option Explicit","Dim shell, shortcut",'Set shell = CreateObject("WScript.Shell")',`Set shortcut = shell.CreateShortcut("${m(n)}")`,'shortcut.TargetPath = "wscript.exe"',`shortcut.Arguments = "//B //NoLogo \\"${m(t)}\\""`,"shortcut.WindowStyle = 7","shortcut.Save",""].join(`\r
|
|
56
|
+
`);return{path:e,content:r}}async function Et(t){const{wrapperPath:n,shortcutPath:e,runCommand:r}=t,i=$t(n,e);for(let o=1;o<=3;o+=1){try{await $(i.path,i.content,"utf8"),await r("wscript.exe",[i.path,"//B","//NoLogo"])}catch{}finally{await y(i.path,{force:!0}).catch(()=>{})}if(C(e))return!0}return console.warn(`Warning: failed to create Windows logon-autostart shortcut at ${e}. The daemon will NOT start automatically after the next reboot/logon. Check Controlled Folder Access / antivirus blocking writes to the Startup folder, or create the shortcut manually.`),!1}async function Pt(t){try{const n=await t("whoami",["/user","/fo","csv","/nh"],{allowFailure:!0}),e=String(n?.stdout??"").match(/"(S-1-[0-9-]+)"/);if(e)return e[1]}catch{}return null}function St(t){const n=`//B //NoLogo "${t.wrapperPath}"`;return`<?xml version="1.0" encoding="UTF-16"?>
|
|
57
57
|
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
|
|
58
58
|
<RegistrationInfo>
|
|
59
59
|
<Description>grix-connector daemon autostart</Description>
|
|
@@ -61,12 +61,12 @@ WantedBy=default.target
|
|
|
61
61
|
<Triggers>
|
|
62
62
|
<LogonTrigger>
|
|
63
63
|
<Enabled>true</Enabled>
|
|
64
|
-
<UserId>${
|
|
64
|
+
<UserId>${w(t.userId)}</UserId>
|
|
65
65
|
</LogonTrigger>
|
|
66
66
|
</Triggers>
|
|
67
67
|
<Principals>
|
|
68
68
|
<Principal id="Author">
|
|
69
|
-
<UserId>${
|
|
69
|
+
<UserId>${w(t.userId)}</UserId>
|
|
70
70
|
<LogonType>InteractiveToken</LogonType>
|
|
71
71
|
<RunLevel>LeastPrivilege</RunLevel>
|
|
72
72
|
</Principal>
|
|
@@ -92,17 +92,17 @@ WantedBy=default.target
|
|
|
92
92
|
<Actions Context="Author">
|
|
93
93
|
<Exec>
|
|
94
94
|
<Command>wscript.exe</Command>
|
|
95
|
-
<Arguments>${
|
|
95
|
+
<Arguments>${w(n)}</Arguments>
|
|
96
96
|
</Exec>
|
|
97
97
|
</Actions>
|
|
98
98
|
</Task>
|
|
99
|
-
`}async function
|
|
100
|
-
`),i=Buffer.from(r,"utf16le").toString("base64"),
|
|
101
|
-
`)}function
|
|
102
|
-
${
|
|
99
|
+
`}async function xt(t){const{serviceID:n,wrapperPath:e,runCommand:r}=t,i=await Pt(r)??p.userInfo().username,o=c.join(c.dirname(e),`${n}-task.xml`);try{const a=St({userId:i,wrapperPath:e});return await $(o,"\uFEFF"+a,"utf16le"),await r("schtasks",["/Create","/TN",n,"/XML",o,"/F"]),!0}catch{return!1}finally{await y(o,{force:!0}).catch(()=>{})}}const K=4,kt="grix-wrapper-template:";function Ct(t){const n=c.join(t.rootDir,"data","upgrade-wrapper-child.json"),e=[`"${t.cliPath}"`,...t.configDir?["--config-dir",`"${t.configDir}"`]:[]],r=["$ErrorActionPreference = 'Stop'",`$recordPath = ${A(n)}`,"$exitCode = 1","try {"," [IO.Directory]::CreateDirectory([IO.Path]::GetDirectoryName($recordPath)) | Out-Null",` $p = Start-Process -FilePath ${A(t.nodePath)} -ArgumentList @(${e.map(A).join(", ")}) -WindowStyle Hidden -PassThru`,` $record = @{ pid = $p.Id; launched_at = [DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds(); boot_time = [DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds() - [Environment]::TickCount64; cli_path = ${A(t.cliPath)} }`," $utf8NoBom = New-Object System.Text.UTF8Encoding($false)"," try {"," [IO.File]::WriteAllText($recordPath, ($record | ConvertTo-Json -Compress), $utf8NoBom)"," } catch {",' & "$env:SystemRoot\\System32\\taskkill.exe" /PID $p.Id /T /F 2>$null | Out-Null'," $p.WaitForExit()"," throw"," }"," $p.WaitForExit()"," $exitCode = $p.ExitCode","} finally {"," Remove-Item -LiteralPath $recordPath -Force -ErrorAction SilentlyContinue","}","exit $exitCode"].join(`
|
|
100
|
+
`),i=Buffer.from(r,"utf16le").toString("base64"),l=`"${c.join(process.env.SystemRoot||"C:\\Windows","System32","WindowsPowerShell","v1.0","powershell.exe")}" -NoProfile -NonInteractive -ExecutionPolicy Bypass -EncodedCommand ${i}`,a=c.join(t.rootDir,"bin","upgrade-guardian.mjs"),s=c.join(t.rootDir,"bin","upgrade-guardian.fallback.mjs"),u=c.join(t.rootDir,"data","upgrade-wrapper.pause.json"),d=c.join(t.rootDir,"data","upgrade-pending.json"),h=c.join(t.rootDir,"data"),g=c.join(h,"wrapper.lock"),E=[`"${m(t.nodePath)}"`,`"${m(a)}"`,"activate","--root",`"${m(t.rootDir)}"`].join(" "),b=`${E} --wrapper-handoff`,H=[`"${m(t.nodePath)}"`,`"${m(s)}"`,"activate","--root",`"${m(t.rootDir)}"`].join(" "),et=`${H} --wrapper-handoff`;return[`' ${kt} ${K}`,"Option Explicit","Dim shell, processEnv, fso, command, guardianCommand, handoffGuardianCommand, guardianFallbackCommand, handoffGuardianFallbackCommand, guardianPath, guardianFallbackPath, maintenanceFile, pendingFile, delayMs, rapidCount, guardianExit, wrapperLockDir, wrapperLockFile, wrapperLock",`command = "${m(l)}"`,`guardianCommand = "${m(E)}"`,`handoffGuardianCommand = "${m(b)}"`,`guardianFallbackCommand = "${m(H)}"`,`handoffGuardianFallbackCommand = "${m(et)}"`,`guardianPath = "${m(a)}"`,`guardianFallbackPath = "${m(s)}"`,`maintenanceFile = "${m(u)}"`,`pendingFile = "${m(d)}"`,`wrapperLockDir = "${m(h)}"`,`wrapperLockFile = "${m(g)}"`,'Set shell = CreateObject("WScript.Shell")','Set processEnv = shell.Environment("Process")','Set fso = CreateObject("Scripting.FileSystemObject")',"On Error Resume Next","If Not fso.FolderExists(wrapperLockDir) Then fso.CreateFolder wrapperLockDir","Err.Clear","Set wrapperLock = fso.OpenTextFile(wrapperLockFile, 2, True)","If Err.Number <> 0 Then"," Err.Clear"," If fso.FileExists(wrapperLockFile) Then"," On Error GoTo 0"," WScript.Quit 0"," End If","Else"," wrapperLock.Write CStr(Now())"," Err.Clear","End If","On Error GoTo 0",`processEnv("${T}") = "${m(t.rootDir)}"`,'processEnv("GRIX_WRAPPER_SUPERVISED") = "1"',...j().map(([nt,rt])=>`processEnv("${nt}") = "${rt}"`),"delayMs = 5000","rapidCount = 0","Do"," Do While fso.FileExists(maintenanceFile)"," If fso.FileExists(guardianPath) Then"," guardianExit = shell.Run(guardianCommand, 0, True)"," ElseIf fso.FileExists(guardianFallbackPath) Then"," guardianExit = shell.Run(guardianFallbackCommand, 0, True)"," Else"," On Error Resume Next"," fso.DeleteFile maintenanceFile, True"," On Error GoTo 0"," WScript.Sleep 1000"," End If"," If fso.FileExists(maintenanceFile) Then WScript.Sleep 3000"," Loop"," If fso.FileExists(pendingFile) Then"," If fso.FileExists(guardianPath) Then"," guardianExit = shell.Run(guardianCommand, 0, True)"," ElseIf fso.FileExists(guardianFallbackPath) Then"," guardianExit = shell.Run(guardianFallbackCommand, 0, True)"," End If"," End If"," shell.Run command, 0, True"," If fso.FileExists(pendingFile) Then"," If fso.FileExists(guardianPath) Then"," guardianExit = shell.Run(handoffGuardianCommand, 0, True)"," ElseIf fso.FileExists(guardianFallbackPath) Then"," guardianExit = shell.Run(handoffGuardianFallbackCommand, 0, True)"," End If"," End If"," If fso.FileExists(pendingFile) And Not PendingIsTerminal() Then"," rapidCount = 0"," WScript.Sleep delayMs"," ElseIf fso.FileExists(maintenanceFile) Then"," rapidCount = 0"," Else"," rapidCount = rapidCount + 1"," If rapidCount >= 10 Then"," WScript.Sleep 300000"," rapidCount = 0"," Else"," WScript.Sleep delayMs"," End If"," End If","Loop","","Function PendingIsTerminal()"," Dim stream, text"," PendingIsTerminal = False"," On Error Resume Next"," Set stream = fso.OpenTextFile(pendingFile, 1)"," If Err.Number <> 0 Then"," Err.Clear"," On Error GoTo 0"," Exit Function"," End If"," text = stream.ReadAll"," stream.Close"," If Err.Number <> 0 Then"," Err.Clear"," On Error GoTo 0"," Exit Function"," End If"," On Error GoTo 0",' text = Replace(Replace(Replace(text, " ", ""), vbCr, ""), vbLf, "")',' If InStr(text, """phase"":""failed""") > 0 Then PendingIsTerminal = True',' If InStr(text, """phase"":""finalizing""") > 0 Then PendingIsTerminal = True',"End Function",""].join(`\r
|
|
101
|
+
`)}function vt(t,n){return c.join(ht(t,n),"dist","service","platform-adapter.js")}async function Tt(t,n={}){const e={version:K,build:Ct};try{if(c.basename(t)!==pt)return e;const r=c.dirname(t),i=ft(r,"current");if(!i)return e;const o=vt(r,i),l=n.selfModulePath??ot(import.meta.url);if(c.resolve(o)===c.resolve(l)||!C(o))return e;const a=await import(at(o).href),s=Number(a.WINDOWS_WRAPPER_TEMPLATE_VERSION),u=a.buildWindowsWrapperScript;return typeof u!="function"||!Number.isFinite(s)||s<=e.version?e:{version:s,build:u}}catch{return e}}function F(t){return`gui/${t??0}`}function Ft(t=process.env){return t.GRIX_LAUNCHER_MANAGED==="1"||!!t.GRIX_CLAUDE_INTERNAL_API_URL}function U(t){const n=String(t?.stdout??"").trim();return String(t?.stderr??"").trim()||n||`exit=${Number(t?.exitCode??-1)}`}function N(t,n,e,r){if(t?.trim())return c.resolve(t);const i=n||e;return i?c.resolve(c.dirname(c.dirname(i))):c.join(r,".grix")}async function q(t){try{await it(t)}catch{throw new Error(`launchd plist missing: ${t}`)}}async function Y(t){const{serviceID:n,domain:e,runCommand:r}=t,i=`${e}/${n}`,o=await r("launchctl",["bootout",i],{allowFailure:!0});if(Number(o?.exitCode??-1)!==0){const l=await r("launchctl",["print",i],{allowFailure:!0});if(Number(l?.exitCode??1)===0){await new Promise(s=>setTimeout(s,250));const a=await r("launchctl",["print",i],{allowFailure:!0});if(Number(a?.exitCode??1)===0)throw new Error(`launchctl bootout ${i}: ${U(o)}`)}return}for(let l=0;l<20;l+=1){const a=await r("launchctl",["print",i],{allowFailure:!0});if(Number(a?.exitCode??1)!==0)return;await new Promise(s=>setTimeout(s,250))}throw new Error(`launchctl bootout ${i}: service remains loaded`)}async function B(t){const{serviceID:n,domain:e,definitionPath:r,runCommand:i}=t,o=`${e}/${n}`,l=await i("launchctl",["bootstrap",e,r],{allowFailure:!0});if(Number(l?.exitCode??-1)===0)return;const a=await i("launchctl",["print",o],{allowFailure:!0});Number(a?.exitCode??1)===0&&await Y(t);const s=await i("launchctl",["bootstrap",e,r],{allowFailure:!0});if(Number(s?.exitCode??-1)!==0)throw new Error(`launchctl bootstrap ${e} failed: initial=${U(l)}; retry=${U(s)}`)}function bt(){return{platform:"darwin",kind:"launchd",async install({serviceID:t,nodePath:n,cliPath:e,configDir:r,rootDir:i,stdoutPath:o,stderrPath:l,environmentPath:a="",homeDir:s=p.homedir()}){const u=mt(t,s),d=N(i,o,l,s);return await v(c.dirname(u),{recursive:!0}),await $(u,wt({serviceID:t,nodePath:n,cliPath:e,configDir:r,rootDir:d,stdoutPath:o,stderrPath:l,environmentPath:a}),{encoding:"utf8",mode:384}),{definitionPath:u}},async start({serviceID:t,definitionPath:n,runCommand:e=f,uid:r=process.getuid?.()??0}){const i=F(r);await q(n),await B({serviceID:t,domain:i,definitionPath:n,runCommand:e})},async stop({serviceID:t,runCommand:n=f,uid:e=process.getuid?.()??0}){const r=F(e);await n("launchctl",["bootout",`${r}/${t}`],{allowFailure:!0})},async restart({serviceID:t,definitionPath:n,runCommand:e=f,uid:r=process.getuid?.()??0,env:i=process.env,reloadDefinition:o=!1}){const l=F(r),a={serviceID:t,domain:l,definitionPath:n,runCommand:e};if(await q(n),!o&&Ft(i)){const s=await e("launchctl",["kickstart","-k",`${l}/${t}`],{allowFailure:!0});if(Number(s?.exitCode??-1)===0)return;await B(a);return}await Y(a),await B(a)},async uninstall({serviceID:t,definitionPath:n,runCommand:e=f,uid:r=process.getuid?.()??0}){const i=F(r);await e("launchctl",["bootout",`${i}/${t}`],{allowFailure:!0}),await y(n,{force:!0})},async discoverServices({homeDir:t=p.homedir()}={}){const n=c.join(t,"Library","LaunchAgents"),e=await I(n).catch(()=>[]),r=L("darwin"),i=[];for(const o of e){if(!o.startsWith(r)||!o.endsWith(".plist"))continue;const l=o.slice(0,-6),a=c.join(n,o);let s=null;try{const u=await P(a,"utf8");s=lt(u)}catch{}i.push({serviceID:l,definitionPath:a,configDir:s})}return i},async isServiceLoaded({serviceID:t,runCommand:n=f,uid:e=process.getuid?.()??0}){const r=F(e),i=await n("launchctl",["print",`${r}/${t}`],{allowFailure:!0});return Number(i?.exitCode??1)===0}}}function It(t){return`${t}-wrapper.vbs`}const Rt=5e3;async function G(t,n){const e=await ct(It(t),{runCommandImpl:n,platform:"win32"}).catch(()=>[]),r=new Set([process.pid,process.ppid]);return e.filter(i=>!r.has(i))}async function Lt(t,n,e=Rt){const r=Date.now()+e;let i=await G(t,n);for(;i.length>0&&Date.now()<r;)await new Promise(o=>setTimeout(o,250)),i=await G(t,n);return i}function Dt(){return{platform:"win32",kind:"task-scheduler",async install({serviceID:t,nodePath:n,cliPath:e,configDir:r,rootDir:i,stdoutPath:o,stderrPath:l,runCommand:a=f,homeDir:s=p.homedir()}){const u=x(t,s),d=N(i,o,l,s);await v(c.dirname(u),{recursive:!0});const h=await Tt(e);await $(u,h.build({nodePath:n,cliPath:e,configDir:r,rootDir:d}),"utf8");let g=!1;st()&&(g=await xt({serviceID:t,wrapperPath:u,runCommand:a}));const E=M(t,s);return g?await y(E,{force:!0}).catch(()=>{}):await Et({wrapperPath:u,shortcutPath:E,runCommand:a}),{definitionPath:g?`task:${t}`:`startup:${t}`}},async start({serviceID:t,definitionPath:n,runCommand:e=f,homeDir:r=p.homedir()}){const i=x(t,r);if(n.startsWith("task:"))try{await e("schtasks",["/Run","/TN",t]);return}catch{}(await G(t,e)).length>0||_("wscript.exe",["//B","//NoLogo",i])},async stop({serviceID:t,runCommand:n=f,homeDir:e=p.homedir()}){await n("schtasks",["/End","/TN",t],{allowFailure:!0});const r=`${t}-wrapper.vbs`;await X(r,{platform:"win32"})},async restart({serviceID:t,definitionPath:n,runCommand:e=f,homeDir:r=p.homedir()}){await e("schtasks",["/End","/TN",t],{allowFailure:!0});const i=`${t}-wrapper.vbs`;await X(i,{platform:"win32"});const o=x(t,r);if(n?.startsWith("task:"))try{await e("schtasks",["/Run","/TN",t]);return}catch{}(await Lt(t,e)).length>0||_("wscript.exe",["//B","//NoLogo",o])},async uninstall({serviceID:t,runCommand:n=f,homeDir:e=p.homedir()}){await n("schtasks",["/Delete","/TN",t,"/F"],{allowFailure:!0});const r=x(t,e);await y(r,{force:!0});const i=M(t,e);await y(i,{force:!0})},async discoverServices({homeDir:t=p.homedir(),runCommand:n=f}={}){const e=L("win32"),r=await n("schtasks",["/Query","/FO","CSV","/NH"],{allowFailure:!0}),i=[];if(Number(r?.exitCode??-1)===0){const a=String(r.stdout??"").split(/\r?\n/);for(const s of a){const u=s.match(/^"([^"]+)"/);if(!u)continue;const d=u[1];if(!d.startsWith(e))continue;let h=null;try{const g=x(d,t),b=(await P(g,"utf8")).match(/--config-dir\s+""([^""]+)""/);b&&(h=b[1])}catch{}i.push({serviceID:d,definitionPath:`task:${d}`,configDir:h})}}const o=c.join(t,"AppData","Roaming","Microsoft","Windows","Start Menu","Programs","Startup"),l=await I(o).catch(()=>[]);for(const a of l){if(!a.startsWith(e)||!a.endsWith(".lnk"))continue;const s=a.slice(0,-4);if(i.some(d=>d.serviceID===s))continue;let u=null;try{const d=x(s,t),g=(await P(d,"utf8")).match(/--config-dir\s+""([^""]+)""/);g&&(u=g[1])}catch{}i.push({serviceID:s,definitionPath:`startup:${s}`,configDir:u})}return i},async isServiceLoaded({serviceID:t,runCommand:n=f,homeDir:e=p.homedir()}){const r=await n("schtasks",["/Query","/TN",t,"/NH"],{allowFailure:!0});return Number(r?.exitCode??1)===0?!0:C(M(t,e))}}}function At(){return{platform:"linux",kind:"systemd-user",async install({serviceID:t,nodePath:n,cliPath:e,configDir:r,rootDir:i,stdoutPath:o,stderrPath:l,environmentPath:a="",homeDir:s=p.homedir(),runCommand:u=f}){const d=dt(t,s),h=N(i,o,l,s);return await v(c.dirname(d),{recursive:!0}),await $(d,yt({serviceID:t,nodePath:n,cliPath:e,configDir:r,rootDir:h,stdoutPath:o,stderrPath:l,environmentPath:a}),{encoding:"utf8",mode:384}),await u("systemctl",["--user","daemon-reload"]),await u("systemctl",["--user","enable",`${t}.service`]),{definitionPath:d}},async start({serviceID:t,runCommand:n=f}){await n("systemctl",["--user","start",`${t}.service`])},async stop({serviceID:t,runCommand:n=f}){await n("systemctl",["--user","stop",`${t}.service`],{allowFailure:!0})},async restart({serviceID:t,runCommand:n=f}){await n("systemctl",["--user","restart",`${t}.service`])},async uninstall({serviceID:t,definitionPath:n,runCommand:e=f}){await e("systemctl",["--user","stop",`${t}.service`],{allowFailure:!0}),await e("systemctl",["--user","disable",`${t}.service`],{allowFailure:!0}),await y(n,{force:!0}),await e("systemctl",["--user","daemon-reload"])},async discoverServices({homeDir:t=p.homedir()}={}){const n=c.join(t,".config","systemd","user"),e=await I(n).catch(()=>[]),r=L("linux"),i=[];for(const o of e){if(!o.startsWith(r)||!o.endsWith(".service"))continue;const l=o.slice(0,-8),a=c.join(n,o);let s=null;try{const u=await P(a,"utf8");s=ut(u)}catch{}i.push({serviceID:l,definitionPath:a,configDir:s})}return i},async isServiceLoaded({serviceID:t,runCommand:n=f}){const e=await n("systemctl",["--user","is-active",`${t}.service`],{allowFailure:!0});return String(e?.stdout??"").trim()==="active"}}}function k(t){try{const n=V(t,"utf8"),e=JSON.parse(n);if(e&&typeof e.service_id=="string"&&typeof e.node_path=="string"&&typeof e.cli_path=="string"&&typeof e.root_dir=="string")return e}catch{}return null}function Nt(t){return c.join(t,"daemon.lock.json")}function W(t){const n=Nt(t);try{const e=V(n,"utf8"),r=JSON.parse(e);if(typeof r?.pid=="number")return r.pid}catch{}return 0}function Z(t){return c.join(t,".profile")}function Wt(t){return t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function _t(t){const n=[D(t.nodePath),D(t.cliPath)];t.configDir&&n.push("--config-dir",D(t.configDir));const e=`env ${T}=${D(t.rootDir)} ${n.join(" ")}`;return`${e} status >/dev/null 2>&1 || (nohup ${e} start >/dev/null 2>&1 &)`}function Ot(t,n){return`# >>> grix-connector autostart [${t}] >>>
|
|
102
|
+
${n}
|
|
103
103
|
# <<< grix-connector autostart [${t}] <<<
|
|
104
|
-
`}function
|
|
105
|
-
${i}`).replace(/^\n+/,"");else{const
|
|
104
|
+
`}function tt(t){const n=Wt(t);return new RegExp(`(?:^|\\n)# >>> grix-connector autostart \\[${n}\\] >>>\\n[\\s\\S]*?\\n# <<< grix-connector autostart \\[${n}\\] <<<\\n?`,"m")}async function jt(t,n,e){let r="";try{r=await P(t,"utf8")}catch{}const i=Ot(n,e),o=tt(n);let l;if(o.test(r))l=r.replace(o,`
|
|
105
|
+
${i}`).replace(/^\n+/,"");else{const a=r.length===0||r.endsWith(`
|
|
106
106
|
`)?"":`
|
|
107
|
-
`;
|
|
108
|
-
`,{encoding:"utf8",mode:384});const h=
|
|
107
|
+
`;l=`${r}${a}${i}`}l!==r&&(await v(c.dirname(t),{recursive:!0}),await $(t,l,{encoding:"utf8"}))}async function Mt(t,n){let e="";try{e=await P(t,"utf8")}catch{return}const r=tt(n);if(!r.test(e))return;const i=e.replace(r,"").replace(/^\n+/,"");await $(t,i,{encoding:"utf8"})}function Ut(){return{platform:"linux",kind:"bare-daemon",async install({serviceID:t,nodePath:n,cliPath:e,configDir:r,rootDir:i,stdoutPath:o,stderrPath:l,homeDir:a=p.homedir()}){const s=S(t,a);await v(c.dirname(s),{recursive:!0});const u=N(i,o,l,a),d={schema_version:1,service_id:t,node_path:n,cli_path:e,config_dir:r??"",root_dir:u,stdout_path:o,stderr_path:l,installed_at:Date.now()};await $(s,`${JSON.stringify(d,null,2)}
|
|
108
|
+
`,{encoding:"utf8",mode:384});const h=_t({nodePath:n,cliPath:e,configDir:r??"",rootDir:u});return await jt(Z(a),t,h),{definitionPath:s}},async start({serviceID:t,homeDir:n=p.homedir()}){const e=S(t,n),r=k(e);if(!r)throw new Error(`bare-daemon marker missing: ${e}`);const i=[r.cli_path];r.config_dir&&i.push("--config-dir",r.config_dir),_(r.node_path,i,{env:{...process.env,[T]:r.root_dir}})},async stop({serviceID:t,homeDir:n=p.homedir()}){const e=S(t,n),r=k(e);if(!r)return;const i=W(r.root_dir);if(i>0&&R(i))try{process.kill(i,"SIGTERM")}catch{}},async restart(t){await this.stop(t);const n=Date.now()+5e3,e=S(t.serviceID,t.homeDir??p.homedir()),r=k(e);for(;r&&Date.now()<n;){const i=W(r.root_dir);if(i<=0||!R(i))break;await new Promise(o=>setTimeout(o,100))}await this.start(t)},async uninstall({serviceID:t,homeDir:n=p.homedir()}){const e=S(t,n),r=k(e);if(r){const i=W(r.root_dir);if(i>0&&R(i))try{process.kill(i,"SIGTERM")}catch{}}await y(e,{force:!0}),await Mt(Z(n),t)},async discoverServices({homeDir:t=p.homedir()}={}){const n=c.join(t,".grix","service"),e=await I(n).catch(()=>[]),r=L("linux"),i=[];for(const o of e){if(!o.startsWith(r)||!o.endsWith(".bare.json"))continue;const l=o.slice(0,-10),a=c.join(n,o),s=k(a);i.push({serviceID:l,definitionPath:a,configDir:s?.config_dir||null})}return i},async isServiceLoaded({serviceID:t,homeDir:n=p.homedir()}){const e=S(t,n),r=k(e);if(!r)return!1;const i=W(r.root_dir);return i>0&&R(i)}}}function Bt(t=process.getuid?.()??0){if(process.env.GRIX_FORCE_BARE_DAEMON==="1")return!1;const n=process.env.XDG_RUNTIME_DIR;return!!(n&&C(c.join(n,"bus"))||C(`/run/user/${t}/bus`))}function Yt(t=process.platform,n={}){return t==="darwin"?bt():t==="win32"?Dt():n.systemdUserAvailable??Bt()?At():Ut()}export{kt as WINDOWS_WRAPPER_TEMPLATE_MARKER,K as WINDOWS_WRAPPER_TEMPLATE_VERSION,Ct as buildWindowsWrapperScript,j as collectServicePortEnv,Yt as getPlatformServiceAdapter,Ft as isInsideManagedDaemonTree,Bt as isSystemdUserBusAvailable,Mt as removeProfileAutostart,M as resolveWindowsStartupShortcutPath,x as resolveWindowsWrapperPath,Tt as resolveWindowsWrapperTemplateSource,jt as upsertProfileAutostart};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{execFileSync as v,execSync as m,spawn as y}from"node:child_process";import
|
|
1
|
+
import{execFileSync as v,execSync as m,spawn as y}from"node:child_process";import r from"node:process";import{setTimeout as I}from"node:timers/promises";let d;function F(e=r.platform,t=r.env){return e==="win32"&&t.GRIX_WRAPPER_SUPERVISED!=="1"}function _(){if(r.platform!=="win32")return!1;if(d!==void 0)return d;try{m("net session",{stdio:"ignore",windowsHide:!0}),d=!0}catch{d=!1}return d}function H(e,t,n={}){y(e,t,{detached:!0,stdio:"ignore",windowsHide:!0,env:n.env??r.env}).unref()}function w(e,t,n={}){const{cwd:o,env:u=r.env,allowFailure:a=!1}=n;return new Promise((i,s)=>{const l=y(e,t,{cwd:o||void 0,env:u,stdio:["ignore","pipe","pipe"],windowsHide:!0}),P=[],g=[];l.stdout&&l.stdout.on("data",c=>P.push(c)),l.stderr&&l.stderr.on("data",c=>g.push(c)),l.on("error",c=>{if(a){i({exitCode:-1,stdout:"",stderr:c instanceof Error?c.message:String(c)});return}s(c)}),l.on("close",c=>{const f=c??0,S=Buffer.concat(P).toString("utf8"),x=Buffer.concat(g).toString("utf8");if(f!==0&&!a){const E=x.trim()||S.trim()||`${e} exited with code ${f}`;s(new Error(E));return}i({exitCode:f,stdout:S,stderr:x})})})}function p(e){if(e<=0)return!1;try{return r.kill(e,0),!0}catch(t){if(t instanceof Error&&"code"in t){if(t.code==="ESRCH")return!1;if(t.code==="EPERM")return!0}return!1}}const h=3e3;function L(e){try{const o=m(`wmic process where ProcessId=${e} get CommandLine /format:list`,{encoding:"utf8",stdio:["ignore","pipe","ignore"],timeout:h,windowsHide:!0}).match(/CommandLine=(.*)/)?.[1]?.trim();if(o)return o}catch{}try{const t=`${r.env.SystemRoot||"C:\\Windows"}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`,o=v(t,["-NoProfile","-NonInteractive","-WindowStyle","Hidden","-Command",`(Get-CimInstance Win32_Process -Filter 'ProcessId=${e}').CommandLine`],{encoding:"utf8",stdio:["ignore","pipe","ignore"],timeout:h,windowsHide:!0}).trim();return o||null}catch{return null}}function T(e){if(e<=0||!Number.isInteger(e))return null;if(r.platform==="win32")return L(e);try{const n=m(`ps -ww -o command= -p ${e}`,{encoding:"utf8",stdio:["ignore","pipe","ignore"],timeout:h}).trim();return n||null}catch{return null}}async function W(e,t={}){const{platform:n=r.platform,runCommandImpl:o=w}=t;if(e<=0)return!1;if(n==="win32")return await o("taskkill",["/PID",String(e),"/T","/F"],{allowFailure:!0}),!0;try{return r.kill(-e,"SIGTERM"),!0}catch{}try{return r.kill(e,"SIGTERM"),!0}catch{return!1}}async function G(e,t={}){const{timeoutMs:n=5e3,intervalMs:o=100}=t;if(e<=0)return!0;const u=Date.now()+n;for(;Date.now()<=u;){if(!p(e))return!0;await I(o)}return!p(e)}function R(){return`${r.env.SystemRoot||"C:\\Windows"}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`}function C(e){return e.split(/\r?\n/).map(t=>parseInt(t.trim(),10)).filter(t=>t>0&&!Number.isNaN(t))}async function k(e,t={}){const n=t.runCommandImpl??w;if((t.platform??r.platform)==="win32"){const u=await n("wmic",["process","where",`CommandLine Like '%${e}%'`,"get","ProcessId"],{allowFailure:!0});if(u.exitCode===0)return C(u.stdout);const a=e.replace(/'/g,"''"),i=await n(R(),["-NoProfile","-NonInteractive","-WindowStyle","Hidden","-Command",`Get-CimInstance Win32_Process | Where-Object { $_.CommandLine -like '*${a}*' } | Select-Object -ExpandProperty ProcessId`],{allowFailure:!0});return i.exitCode!==0?[]:C(i.stdout)}const o=await n("pgrep",["-f",e],{allowFailure:!0});return o.exitCode!==0?[]:C(o.stdout)}async function D(e,t={}){const{exceptPid:n=0,platform:o=r.platform}=t,u=await k(e),a=new Set([r.pid,r.ppid,n]),i=u.filter(s=>!a.has(s));if(i.length===0)return 0;if(o==="win32")for(const s of i)await W(s,{platform:"win32",runCommandImpl:w});else{for(const s of i)try{r.kill(s,"SIGTERM")}catch{}await I(500);for(const s of i)if(p(s))try{r.kill(s,"SIGKILL")}catch{}}return i.length}export{k as findProcessesByCommandLine,T as getProcessCommandLine,p as isProcessRunning,_ as isWindowsElevated,D as killProcessesByCommandLine,w as runCommand,F as shouldSweepWindowsWrappers,H as spawnDetached,W as terminateProcessTree,G as waitForProcessExit};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "grix-connector",
|
|
3
|
-
"version": "4.9.
|
|
3
|
+
"version": "4.9.3",
|
|
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",
|