grix-connector 4.0.2 → 4.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/dist/adapter/acp/acp-adapter.js +3 -3
  2. package/dist/adapter/acp/session-activity.js +1 -1
  3. package/dist/adapter/claude/claude-bridge-server.js +1 -0
  4. package/dist/adapter/claude/claude-tools.js +1 -0
  5. package/dist/adapter/claude/claude-worker-client.js +1 -0
  6. package/dist/adapter/claude/mcp-http-launcher.js +2 -0
  7. package/dist/adapter/claude/result-timeout.js +1 -0
  8. package/dist/adapter/deepseek/deepseek-adapter.js +6 -0
  9. package/dist/adapter/deepseek/index.js +1 -0
  10. package/dist/adapter/qwen/index.js +1 -0
  11. package/dist/adapter/qwen/qwen-adapter.js +4 -0
  12. package/dist/aibot/client.js +1 -0
  13. package/dist/aibot/index.js +1 -0
  14. package/dist/aibot/types.js +0 -0
  15. package/dist/assets/dsh-bridge/grix-dsh-bridge-4.2.0.tgz +0 -0
  16. package/dist/assets/dsh-bridge/manifest.json +5 -5
  17. package/dist/audit/adapters/agy/agy-audit-adapter.js +1 -1
  18. package/dist/audit/adapters/agy/agy-gen-metadata.js +1 -0
  19. package/dist/bridge/bridge.js +7 -7
  20. package/dist/bridge/lifecycle-barrier.js +1 -1
  21. package/dist/core/access/allowlist-gate.js +1 -0
  22. package/dist/core/access/allowlist-store.js +1 -0
  23. package/dist/core/access/index.js +1 -0
  24. package/dist/core/aibot/connection-manager.js +1 -1
  25. package/dist/core/config/agent-config-repository.js +2 -0
  26. package/dist/core/file-ops/handler.js +1 -0
  27. package/dist/core/file-ops/list-files.js +1 -0
  28. package/dist/core/file-ops/types.js +0 -0
  29. package/dist/core/installer/npm-registry.js +4 -4
  30. package/dist/core/runtime/health.js +1 -1
  31. package/dist/core/skill-sync/skill-syncer.js +1 -1
  32. package/dist/core/upgrade/npm-upgrader.js +2 -2
  33. package/dist/core/upgrade/upgrade-checker.js +1 -1
  34. package/dist/default-skills/grix-agent-dispatch/SKILL.md +9 -9
  35. package/dist/grix.js +7 -7
  36. package/dist/log.js +3 -0
  37. package/dist/main.js +31 -0
  38. package/dist/manager.js +1 -2
  39. package/dist/mcp/stream-http/config.js +1 -0
  40. package/dist/mcp/stream-http/connection-binding.js +1 -0
  41. package/dist/mcp/stream-http/event-tool-executor.js +1 -0
  42. package/dist/mcp/stream-http/gateway.js +1 -0
  43. package/dist/mcp/stream-http/index.js +1 -0
  44. package/dist/mcp/stream-http/security.js +1 -0
  45. package/dist/mcp/stream-http/session-manager.js +1 -0
  46. package/dist/mcp/stream-http/tool-executor.js +1 -0
  47. package/dist/mcp/stream-http/tool-registry.js +1 -0
  48. package/dist/mcp/stream-http/tool-schemas.js +1 -0
  49. package/dist/service/platform-adapter.js +25 -24
  50. package/dist/service/process-control.js +1 -1
  51. package/dist/service/service-manager.js +3 -3
  52. package/dist/service/service-paths.js +2 -2
  53. package/dist/service/windows-wrapper-migration.js +1 -0
  54. package/dist/session/index.js +1 -0
  55. package/dist/session/manager.js +1 -0
  56. package/dist/transport/index.js +1 -0
  57. package/dist/transport/json-rpc.js +3 -0
  58. package/openclaw-plugin/audit-recorder-worker.js +496 -54
  59. package/package.json +2 -1
  60. package/scripts/install-guardian.mjs +35 -19
  61. package/scripts/upgrade-guardian.mjs +1345 -0
  62. package/scripts/upgrade-guardian.sh +100 -75
  63. package/dist/assets/dsh-bridge/grix-dsh-bridge-4.0.2.tgz +0 -0
package/dist/manager.js CHANGED
@@ -1,2 +1 @@
1
- import{readFileSync as se,readdirSync as ie,writeFileSync as oe}from"node:fs";import{join as A}from"node:path";import{homedir as ce}from"node:os";import{AgentInstance as N}from"./bridge/bridge.js";import{buildSharedInstanceConfig as le,diffSharedOwners as de,sharedInstanceKey as pe,SharedOwnersCache as me}from"./manager-share-config.js";import{isAgentDeletedError as q}from"./core/aibot/errors.js";import{GRIX_PATHS as w,log as l}from"./core/log/index.js";import{resolveClientVersion as ue}from"./core/util/client-version.js";import{UpgradeChecker as ge}from"./core/upgrade/upgrade-checker.js";import{SkillSyncer as fe}from"./core/skill-sync/skill-syncer.js";import{AgentGlobalConfigStore as he}from"./core/persistence/agent-global-config-store.js";import{resolveSkillScanMode as _e}from"./adapter/claude/skill-scanner.js";import{buildWireSkills as ye}from"./core/skill-sync/sync-state.js";import{buildLibrarySkills as we}from"./core/skill-sync/library-skills.js";import{AUDIT_LOCAL_ACTION_TYPES as ke}from"./core/aibot/types.js";import{buildReportedSkills as Ae,logDefaultSkillsCheck as Se,cleanupProjectedSkills as U}from"./default-skills/index.js";import{buildProviderEnv as be,supportsKiroCwRelay as F,supportsNonMitmRelayConfig as ve}from"./core/config/provider-env.js";import{resolveHermesHomeDir as Ce}from"./adapter/shared/native-provider-config.js";import{ensureKiroRelay as W,stopKiroRelay as K}from"./core/proxy/kiro-relay.js";import{resolveCopilotCommand as Q}from"./core/runtime/copilot-resolve.js";import{resolveCursorCommand as G}from"./core/runtime/cursor-resolve.js";import{getCliVersion as xe,resolveCliPath as $e}from"./core/util/cli-probe.js";import{AgentInstaller as Ie}from"./core/installer/installer.js";import{reportInstallFailure as Ee}from"./core/observability/sentry.js";import{createDefaultAuditProducer as Te}from"./audit/runtime/default-audit-recorder.js";import{normalizeCodexConfigOverrides as Pe}from"./adapter/codex/config-overrides.js";import{resolveCodexDirectHome as Me}from"./adapter/codex/direct-home.js";function z(n){if(n)return{...n.provider_id?.trim()?{providerId:n.provider_id.trim()}:{},...n.base_url?.trim()?{baseUrl:n.base_url.trim()}:{},...n.quota_base_url?.trim()?{quotaBaseUrl:n.quota_base_url.trim()}:{},...n.api_key?.trim()?{apiKey:n.api_key.trim()}:{},...n.model?.trim()?{model:n.model.trim()}:{},...n.fast_model?.trim()?{fastModel:n.fast_model.trim()}:{},...n.effort?.trim()?{effort:n.effort.trim()}:{}}}const je=8e3;function Oe(){const n=Q();return[{clientType:"openclaw",command:"openclaw"},{clientType:"claude",command:"claude"},{clientType:"codex",command:"codex"},{clientType:"qwen",command:"qwen"},{clientType:"hermes",command:"hermes"},{clientType:"reasonix",command:"reasonix"},{clientType:"codewhale",command:"codewhale"},{clientType:"opencode",command:"opencode"},{clientType:"cursor",command:G()},{clientType:"pi",command:"pi"},{clientType:"openhuman",command:"openhuman-core"},{clientType:"kiro",command:"kiro-cli"},{clientType:"kimi",command:"kimi"},{clientType:"copilot",command:n.command},{clientType:"agy",command:"agy"},{clientType:"deepseek",command:"dsh"}]}async function Re(){return Promise.all(Oe().map(async n=>{const e=await $e(n.command);if(!e)return{client_type:n.clientType,command:n.command,installed:!1,path:null,version:null};const t=await xe(e,n.versionArgs??["--version"]);return{client_type:n.clientType,command:n.command,installed:!0,path:e,version:t.version,error:t.error}}))}function De(n){switch(n){case"claude":return{adapterType:"claude",command:"claude"};case"codex":return{adapterType:"codex",command:"codex",options:{sandboxMode:"danger-full-access"}};case"gemini":return{adapterType:"acp",command:"gemini",autoInjectArgs:{acp:!0},enableSessionBinding:!0};case"qwen":return{adapterType:"acp",command:"qwen",adapterHint:"qwen/base",autoInjectArgs:{acp:!0},enableSessionBinding:!0};case"pi":return{adapterType:"pi",command:"pi"};case"cursor":return{adapterType:"cursor",command:G()};case"reasonix":return{adapterType:"acp",command:"reasonix",args:["acp"],enableSessionBinding:!0};case"codewhale":return{adapterType:"codewhale",command:"codewhale",enableSessionBinding:!0};case"openhuman":return{adapterType:"openhuman",command:"openhuman-core",enableSessionBinding:!0};case"kiro":return{adapterType:"acp",command:"kiro-cli",args:["acp"],enableSessionBinding:!0};case"kimi":return{adapterType:"acp",command:"kimi",args:["acp"],options:{initial_mode:"yolo"},enableSessionBinding:!0};case"opencode":return{adapterType:"opencode",command:"opencode",args:["serve"],enableSessionBinding:!0};case"copilot":{const e=Q();return{adapterType:"acp",command:e.command,args:[...e.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};default:throw new Error(`Unsupported client_type: ${n}`)}}function b(n,e){return String(n??"").trim().toLowerCase().replace(/[^a-z0-9._-]+/g,"-").replace(/-+/g,"-").replace(/^-|-$/g,"")||String(e??"").trim().toLowerCase().replace(/[^a-z0-9._-]+/g,"-").replace(/-+/g,"-").replace(/^-|-$/g,"")||"default"}function Le(n){return A(w.data,`session-bindings-${b(n,"unknown")}.json`)}function He(n,e){return A(w.data,`session-bindings-${b(n,e)}.json`)}function Be(n,e){return A(w.data,`active-events-${b(n,e)}.json`)}function Ne(n){return A(w.data,`pending-events-${b(n,"unknown")}.json`)}function qe(n,e){const t=b(n,"agent"),r=b(e,"unknown");return A(w.data,`terminal-outbox-${t}-${r}.json`)}function Ue(...n){const e=[],t=new Set;for(const r of n)for(const a of r??[]){const c=String(a??"").trim(),i=c.toLowerCase();!c||t.has(i)||(t.add(i),e.push(c))}return e.length>0?e:void 0}function Fe(n,e){const t={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}},r=t[n]??t.acp;return{maxConcurrent:e?.max_concurrent??r.maxConcurrent??1,maxQueued:e?.max_queued??r.maxQueued??99,queueTimeoutMs:e?.queue_timeout_ms??r.queueTimeoutMs??0,cancelableQueued:!0,cancelableRunning:!0}}function We(n,e={}){const t=ue(),r=String(n.client_type??"").trim().toLowerCase(),a=De(r),c=r==="deepseek"?n.dsh?.integration_mode??"profile_bridge":void 0,i=c==="embedded_jsonrpc",m=String(n.ws_url??"").trim(),o="get_session_usage",s="get_rate_limits",f="get_agent_global_config",g=ke;if(!n.name?.trim())throw new Error("agent name is required");if(!m)throw new Error(`agent ${n.name}: ws_url is required`);if(!n.agent_id?.trim())throw new Error(`agent ${n.name}: agent_id is required`);if(!n.api_key?.trim())throw new Error(`agent ${n.name}: api_key is required`);const d=a.adapterType,p=d==="acp",y=r==="qwen",u={...a.options??{}};if(d==="deepseek-harness"&&(u.integrationMode=c,u.dshProfile=n.dsh?.profile?.trim()||"web",u.autoInstallBridge=n.dsh?.auto_install_bridge??!i,u.autoStartProfile=n.dsh?.auto_start_profile??!i),d==="deepseek-harness"&&n.provider?.model?.trim()&&(u.defaultModel=n.provider.model.trim()),n.codex_config_overrides!==void 0){if(d!=="codex")throw new Error(`agent ${n.name}: codex_config_overrides is only supported for client_type=codex`);Pe(n.codex_config_overrides,`agent ${n.name}: codex_config_overrides`),u.codexConfigOverrides=n.codex_config_overrides}if(d==="codex"&&n.provider?.wire_api!==void 0){const h=n.provider;if(h.wire_api!=="responses"||!h.base_url?.trim()||!h.api_key?.trim()||!h.model?.trim()||!h.catalog_version?.trim()||!h.catalog_sha256?.trim()||!h.catalog_json?.trim())throw Object.assign(new Error(`agent ${n.name}: incomplete Codex direct relay config`),{code:"INVALID_CODEX_DIRECT_CONFIG"});const B=Me(w.data,n.name);u.codexHome=B,u.codexDirect={codexHome:B,config:{baseUrl:h.base_url.trim(),model:h.model.trim(),...h.reasoning_effort?.trim()?{reasoningEffort:h.reasoning_effort.trim()}:{},token:h.api_key.trim()},catalog:{version:h.catalog_version.trim(),sha256:h.catalog_sha256.trim(),json:h.catalog_json}}}const _=d==="codex"?{capabilities:["local_action_v1","agent_invoke"],localActions:["session_control","get_context","set_model","set_mode","set_reasoning_effort","set_service_tier","set_sandbox_mode","exec_approve","exec_reject","file_list","create_folder","turn_interrupt","permission_approve","permission_reject","thread_compact",o,s]}:null,S=d==="claude"?{localActions:["session_control","set_mode","set_model","set_reasoning_effort","claude_interaction_reply","exec_approve","exec_reject","file_list","create_folder","thread_compact",o,s]}:null,k=y?{capabilities:["stream_chunk","local_action_v1"],localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","file_list","create_folder",o],adapterHint:"qwen/base"}:null,$=d==="pi"?{adapterHint:"pi/base",capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","get_context","file_list","create_folder",o,s]}:null,I=d==="openhuman"?{adapterHint:"openhuman/base",capabilities:["local_action_v1"],localActions:["session_control","set_model","file_list","create_folder"]}:null,E=d==="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",s]}:null,O=d==="codewhale"?{capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","file_list","create_folder",o]}:null,T=d==="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,P=d==="agy"?{adapterHint:"agy/base",capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","file_list","create_folder",s]}:null,M=d==="deepseek-harness"?{adapterHint:i?"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",o,s,"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,R=new Set(["hermes","reasonix","kimi","copilot"]),J=p&&!R.has(r),X=p&&!y?{localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","claude_interaction_reply","file_list","create_folder",...J?[o]:[]]}:null,Y=r==="kiro"?{localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","file_list","create_folder","thread_compact",o,s]}:null,Z=d==="codex"||d==="claude"||d==="opencode"||d==="deepseek-harness"||r==="gemini"?["session_control","set_model","set_mode",...d==="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"]:[],...d==="claude"?["set_reasoning_effort"]:[]]:void 0,ee=d==="cursor"||d==="opencode"||d==="openhuman"||d==="agy"||p&&R.has(r)?[s]:[o,s];p&&u.raw_transport===void 0&&(u.raw_transport=r==="gemini");const te=`${r}/base`,C=_e(d,r),ne=C==="kiro"?void 0:process.cwd(),j=z(n.provider),v={...process.env,...n.env};if(C==="hermes"&&!v.HERMES_HOME?.trim()){const h=be(r,j);v.HERMES_HOME=Ce({...h,...v},n.name)}const D=Ae({mode:C,projectDir:ne,env:v}),L=D.filter(h=>h.source==="connector").map(h=>h.name);L.length>0&&!e.quiet&&l.info("manager",`[${n.name}] injecting connector skills: [${L.join(", ")}]`);const H=ye(D,w.skills);let re=H.length>0?H:void 0;const ae=we({skillsDir:w.skills,mode:C,home:ce(),env:v});return{name:n.name,adapterType:d,aibot:{url:m,agentId:n.agent_id,apiKey:n.api_key,clientType:r,clientVersion:t,adapterHint:M?.adapterHint??a.adapterHint??k?.adapterHint??$?.adapterHint??I?.adapterHint??E?.adapterHint??T?.adapterHint??P?.adapterHint??te,capabilities:M?.capabilities??_?.capabilities??O?.capabilities??$?.capabilities??I?.capabilities??E?.capabilities??T?.capabilities??P?.capabilities??k?.capabilities??["stream_chunk","local_action_v1","connector_upgrade"],localActions:Ue(M?.localActions??_?.localActions??O?.localActions??S?.localActions??$?.localActions??I?.localActions??E?.localActions??T?.localActions??P?.localActions??k?.localActions??Y?.localActions??X?.localActions??["exec_approve","exec_reject"],Z,ee,["connector_rollback","connector_upgrade_push",f,"configure_gateway_provider","skill_upload","skill_delete","skill_enable","skill_disable","skill_refresh",...g]),skills:re,librarySkills:ae,terminalOutboxPath:qe(n.name,n.agent_id)},agent:{command:i?"dsh-jsonrpc-agent":a.command,args:[...a.args??[],...n.args??[]],env:n.env,clientType:r,...j?{provider:j}:{}},adapterOptions:u,acpAuthMethod:u.auth_method,acpInitialMode:u.initial_mode,acpMcpTools:u.acp_mcp_tools,promptTimeoutMs:n.prompt_timeout_ms,bindingsPath:Le(n.agent_id),legacyBindingsPath:He(n.name,n.agent_id),activeEventStorePath:Be(n.name,n.agent_id),pendingEventStorePath:Ne(n.agent_id),...a.enableSessionBinding||p?{enableSessionBinding:!0}:{},...a.autoInjectArgs||r==="reasonix"&&n.provider?.model?.trim()?{autoInjectArgs:{...a.autoInjectArgs??{},...r==="reasonix"&&n.provider?.model?.trim()?{model:n.provider.model.trim()}:{}}}:{},poolMaxSize:n.pool?.maxSize??(d==="deepseek-harness"?4:void 0),poolIdleTimeoutMs:n.pool?.idleTimeoutMs,eventQueue:Fe(d,n.event_queue),logDir:w.log,providerBaseUrl:n.provider_base_url?.trim()||void 0,providerApiKey:n.provider_api_key?.trim()||void 0}}function Ke(){const n=process.env.GRIX_AGENT_STARTUP_WAIT_MS,e=Number(n);return Number.isFinite(e)&&e>=500?Math.floor(e):je}function V(n){const e=[],t=[],r=[],a=ie(n).filter(c=>c.endsWith(".json")).sort();for(const c of a)try{const i=JSON.parse(se(A(n,c),"utf-8"));if(i===null||typeof i!="object"||Array.isArray(i)||!("agents"in i)){r.push(c);continue}if(!Array.isArray(i.agents)){t.push({file:c,error:"agents is not an array"});continue}if(i.agents.length===0){t.push({file:c,error:"agents array is empty"});continue}for(const m of i.agents)e.push({entry:m,file:c})}catch(i){t.push({file:c,error:i instanceof Error?i.message:String(i)})}return{entries:e,fileErrors:t,skipped:r}}function x(n){if(n===null||typeof n!="object")return JSON.stringify(n)??"null";if(Array.isArray(n))return`[${n.map(x).join(",")}]`;const e=n;return`{${Object.keys(e).sort().map(r=>`${JSON.stringify(r)}:${x(e[r])}`).join(",")}}`}function Qe(n,e){return x(n)===x(e)}class St{instances=[];configMap=new Map;rawConfigMap=new Map;reloadChain=Promise.resolve();sharedInstances=new Map;shareSyncChains=new Map;stopping=!1;lifecycleAdmissionClosed=!1;deletedAgentCleanups=new Set;upgradeChecker=null;skillSyncer=null;globalConfigStore;configDir=w.config;sharedOwnersCache=new me(w.data);installer=new Ie;relaySecretResolver;auditProducer=Te();async start(e){const t=e??w.config;this.configDir=t,l.info("manager",`Loading configs from ${t}`),Se(),U(),this.globalConfigStore=new he(A(w.data,"agent-global-configs.json")),this.globalConfigStore.load();const{entries:r,fileErrors:a,skipped:c}=V(t);for(const s of a)l.error("manager",`Failed to load ${s.file}: ${s.error}`);for(const s of c)l.info("manager",`Skipped ${s}: not an agent config (no "agents" key)`);if(r.length===0&&a.length===0&&c.length===0){l.warn("manager",`No config files found in ${t}; daemon idle (waiting for config). Add agents under this directory then run: grix-connector reload`);return}const i=[];let m=a.length;for(const{entry:s,file:f}of r)try{const g=this.toRuntimeInstanceConfig(s);i.push({config:g,entry:s,file:f}),l.info("manager",`Loaded ${g.name} (${g.adapterType??"acp"}) from ${f}`)}catch(g){const d=typeof s?.name=="string"?s.name:"<unknown>";l.error("manager",`Invalid agent config in ${f} (name=${d}): ${g}`),m++}let o=0;if(i.length>0){const s=Ke();l.info("manager",`Starting ${i.length} agent(s), startup wait=${s}ms`);const f=p=>{this.instances=this.instances.filter(y=>y!==p)},g=i.map(({config:p,entry:y,file:u})=>{const _=this.createConfiguredInstance(p);return this.instances.push(_),this.configMap.set(p.name,p),this.rawConfigMap.set(p.name,{entry:y,file:u}),{config:p,instance:_,startPromise:this.prepareProviderRuntime(p,y).then(()=>_.start())}}),d=await Promise.all(g.map(async p=>{const y=await new Promise(u=>{let _=!1;const S=setTimeout(()=>{_||(_=!0,u({kind:"timeout"}))},s);p.startPromise.then(()=>{_||(_=!0,clearTimeout(S),u({kind:"started"}))}).catch(k=>{_||(_=!0,clearTimeout(S),u({kind:"failed",error:k}))})});return{task:p,outcome:y}}));for(const{task:p,outcome:y}of d){if(y.kind==="started"){this.restoreCachedSharedInstances(p.config);continue}if(y.kind==="failed"){if(q(y.error)){this.onAgentDeletedByPlatform(p.config.name);continue}f(p.instance),l.error("manager",`Failed to start ${p.config.name}: ${y.error}`);continue}o++,l.warn("manager",`Startup pending for ${p.config.name}, continue retrying in background`),p.startPromise.then(()=>{l.info("manager",`Delayed start succeeded: ${p.config.name}`),this.restoreCachedSharedInstances(p.config)}).catch(u=>{if(q(u)){this.onAgentDeletedByPlatform(p.config.name);return}f(p.instance),l.error("manager",`Delayed start failed: ${p.config.name}: ${u}`)})}if(this.instances.length>0){const p=Math.max(0,this.instances.length-o);l.info("manager",`${p}/${i.length} agent(s) running now`)}o>0&&l.warn("manager",`${o} agent(s) still connecting in background`)}if(this.instances.length===0&&i.length>0)throw new Error("All agent configurations failed to start");await this.ensureAuxServices()}async ensureAuxServices(){if(this.configMap.size===0)return;const e=[...this.configMap.values()].map(t=>({apiKey:t.aibot.apiKey,wsUrl:t.aibot.url}));this.upgradeChecker||(this.upgradeChecker=new ge(e,()=>this.hasAnyPendingOrBackgroundWork(),()=>this.closeAllLifecycleAdmission()),await this.upgradeChecker.start()),this.skillSyncer||(this.skillSyncer=new fe(e,w.skills),this.skillSyncer.setOnSyncSuccess(()=>this.refreshOwnerLibrarySkills()),await this.skillSyncer.start())}refreshOwnerLibrarySkills(){const e=new Map,t=r=>{let a;try{a=r.skillsSyncGroupKey()}catch(i){l.warn("manager",`skillsSyncGroupKey failed for ${r.name}: ${i}`);return}const c=e.get(a)??[];c.push(r),e.set(a,c)};for(const r of this.instances)t(r);for(const r of this.sharedInstances.values())t(r);for(const[r,a]of e){let c=!1;const i=[];for(const m of a){if(c){i.push(m);continue}let o=!1;try{o=m.forceRefreshSkills(void 0,{force:!0,ownerLibrarySync:!0})===!0}catch(s){l.warn("manager",`owner library skills reporter threw group=${r} agent=${m.name}: ${s}`)}if(o){c=!0;continue}l.warn("manager",`owner library skills reporter failed group=${r} agent=${m.name}; trying next`)}if(!c){l.warn("manager",`owner library skills: no reporter succeeded group=${r}`);continue}for(const m of i)try{m.adoptSkillsWireHashFromDisk()}catch(o){l.warn("manager",`adoptSkillsWireHashFromDisk failed for ${m.name}: ${o}`)}}}async stop(){l.info("manager","Stopping all agents..."),this.stopping=!0,this.upgradeChecker?.stop(),this.skillSyncer?.stop(),await Promise.allSettled([...this.shareSyncChains.values()]),this.shareSyncChains.clear();const e=[...this.sharedInstances.values()];this.sharedInstances.clear(),await Promise.allSettled([...this.instances.map(t=>t.stop()),...e.map(t=>t.stop())]),await this.globalConfigStore?.flush(),await this.auditProducer.stop({timeoutMs:5e3}).catch(()=>!1),this.instances=[],U(),l.info("manager","All stopped")}onShareSet(e,t){this.sharedOwnersCache.save(e.name,t);const a=(this.shareSyncChains.get(e.name)??Promise.resolve()).catch(()=>{}).then(()=>this.syncSharedInstances(e,t));this.shareSyncChains.set(e.name,a)}async syncSharedInstances(e,t){if(this.stopping)return;const{toAdd:r,toRemove:a}=de(e.name,this.sharedInstances.keys(),t);for(const c of r){if(this.stopping)break;const i=pe(e.name,c);try{const m=le(e,c),o=new N(m,this.globalConfigStore,this.auditProducer);o.setLifecycleBusyChecker(()=>this.hasAnyPendingOrBackgroundWork()),o.setLifecycleAdmissionCloser(()=>this.closeAllLifecycleAdmission()),this.lifecycleAdmissionClosed&&o.closeLifecycleAdmission(),this.sharedInstances.set(i,o),await o.start(),o.forceRefreshSkills(void 0,{force:!0}),l.info("manager",`shared instance started: ${i}`)}catch(m){this.sharedInstances.delete(i);const o=m instanceof Error?m.message:String(m);/Auth failed/i.test(o)?(this.sharedOwnersCache.remove(e.name,c),l.warn("manager",`shared instance auth rejected, removed from cache: ${i}`)):l.error("manager",`start shared instance failed ${i}: ${m}`)}}for(const{key:c}of a){const i=this.sharedInstances.get(c);i&&(await i.stop().catch(m=>l.error("manager",`stop shared instance failed ${c}: ${m}`)),this.sharedInstances.get(c)===i&&this.sharedInstances.delete(c),l.info("manager",`shared instance stopped: ${c}`))}}restoreCachedSharedInstances(e){const t=this.sharedOwnersCache.load(e.name);t.length!==0&&(l.info("manager",`restoring ${t.length} cached shared instance(s) for ${e.name}`),this.onShareSet(e,t))}getAgentsStatus(){return this.instances.map(e=>e.getStatus())}async addAgent(e){await this.addAgentInternal(e,"agents.json"),this.persistAgentsConfig(),await this.ensureAuxServices(),l.info("manager",`Added agent: ${e.name}`)}async addAgentInternal(e,t){const r=this.toRuntimeInstanceConfig(e);if(this.instances.some(a=>a.name===r.name))throw new Error(`Agent "${r.name}" already exists`);await this.startInstanceFromConfig(r,e,t)}createConfiguredInstance(e){const t=new N(e,this.globalConfigStore,this.auditProducer);return t.setUpgradeTrigger(()=>this.upgradeChecker?.triggerCheck()),t.setLifecycleBusyChecker(()=>this.hasAnyPendingOrBackgroundWork()),t.setLifecycleAdmissionCloser(()=>this.closeAllLifecycleAdmission()),this.lifecycleAdmissionClosed&&t.closeLifecycleAdmission(),t.setInstallAgentHandler(r=>this.installAgent(r)),t.setShareSetHandler(r=>this.onShareSet(e,r)),t.setAgentDeletedHandler(()=>this.onAgentDeletedByPlatform(e.name)),t.setSkillSyncHandler(()=>this.skillSyncer?.triggerSync()),t.setProviderConfigHandler(r=>this.setAgentProvider(e.name,r)),t.setRelayStateApplyPorts({getLocalState:()=>this.relayStateApplier?.getLocalState(e.name)??{enabled:!1},applyEnable:(r,a)=>{const c=this.relayStateApplier;return c?c.applyEnable(e.name,r,a):Promise.reject(new Error("relay state applier is not configured"))},applyDisable:()=>{const r=this.relayStateApplier;return r?r.applyDisable(e.name):Promise.reject(new Error("relay state applier is not configured"))}}),t.setRelayEnvSettledHandler(()=>this.relayEnvSettledHandler?.()),t.setProviderResolver(()=>z(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()}async prepareProviderRuntime(e,t){const r=this.resolveAgentProvider(t.provider);F(t.client_type)&&r?.base_url?.trim()&&r?.api_key?.trim()&&r?.model?.trim()&&await W(e.name,{baseUrl:r.base_url.trim(),apiKey:r.api_key.trim(),model:r.model.trim()})}async startInstanceFromConfig(e,t,r){await this.prepareProviderRuntime(e,t);const a=this.createConfiguredInstance(e);this.instances.push(a);try{await a.start(),this.configMap.set(e.name,e),this.rawConfigMap.set(e.name,{entry:t,file:r})}catch(c){throw this.instances=this.instances.filter(i=>i!==a),c}this.relayEnvSettledHandler?.()}async removeAgent(e){if(!this.instances.some(t=>t.name===e))throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});await this.removeAgentInternal(e),this.persistAgentsConfig(),l.info("manager",`Removed agent: ${e}`),this.relayEnvSettledHandler?.()}async removeAgentInternal(e,t){const r=this.instances.findIndex(s=>s.name===e),a=r===-1?void 0:this.instances[r];this.configMap.delete(e),this.rawConfigMap.delete(e);const c=this.shareSyncChains.get(e);this.shareSyncChains.delete(e),c&&await c.catch(()=>{});const i=t?.keepShareCache?void 0:{dropPendingEvents:!0},m=`${e}#shared:`,o=[];for(const[s,f]of[...this.sharedInstances.entries()])s.startsWith(m)&&o.push(f.stop(i).catch(g=>l.error("manager",`stop shared instance failed ${s}: ${g}`)).then(()=>{this.sharedInstances.get(s)===f&&this.sharedInstances.delete(s)}));if(t?.keepShareCache||this.sharedOwnersCache.delete(e),a)try{await a.stop(i)}finally{const s=this.instances.indexOf(a);s!==-1&&this.instances.splice(s,1)}await Promise.all(o),t?.keepShareCache||await K(e).catch(()=>{})}onAgentDeletedByPlatform(e){this.stopping||this.deletedAgentCleanups.has(e)||(this.deletedAgentCleanups.add(e),(async()=>{try{if(!(this.rawConfigMap.has(e)||this.instances.some(r=>r.name===e)))return;l.warn("manager",`Agent "${e}" \u5DF2\u5728\u5E73\u53F0\u5220\u9664\uFF0C\u505C\u6B62\u5B9E\u4F8B\u5E76\u6E05\u7406\u672C\u5730\u914D\u7F6E`),await this.removeAgentInternal(e),this.persistAgentsConfig(),l.info("manager",`Agent "${e}" \u5DF2\u5728\u5E73\u53F0\u5220\u9664\uFF0C\u672C\u5730\u914D\u7F6E\u5DF2\u6E05\u7406\uFF08agents.json \u5DF2\u79FB\u9664\u8BE5\u6761\u76EE\uFF09`)}catch(t){l.error("manager",`cleanup for deleted agent "${e}" failed: ${t}`)}finally{this.deletedAgentCleanups.delete(e)}})())}persistAgentsConfig(){const e=A(this.configDir,"agents.json");try{const t=[];for(const[,a]of this.configMap){const c=this.rawConfigMap.get(a.name)?.entry.provider;t.push({name:a.name,ws_url:a.aibot.url,agent_id:a.aibot.agentId,api_key:a.aibot.apiKey,client_type:a.aibot.clientType,...c?{provider:c}:{}})}oe(e,JSON.stringify({agents:t},null,4)+`
2
- `,"utf-8")}catch(t){l.error("manager",`Failed to persist agents config: ${t}`)}}isAgentBusy(e){return this.instances.find(r=>r.name===e)?.hasPendingWork()??!1}getAgentsWithStaleRelayEnv(){return this.instances.filter(e=>e.hasStaleRelayEnv()).map(e=>e.name)}markAgentRelayEnvStale(e){const t=this.instances.find(r=>r.name===e);return t?(t.markRelayEnvStale(),!0):!1}relayEnvSettledHandler;setRelayEnvSettledHandler(e){this.relayEnvSettledHandler=e}relayStateApplier;setRelayStateApplier(e){this.relayStateApplier=e}reportRelayStateLocalChange(e){this.instances.find(r=>r.name===e)?.reportRelayStateLocalChange()}setRelaySecretResolver(e){this.relaySecretResolver=e}resolveAgentProvider(e){if(!e?.api_key_ref?.trim())return e;const t=e.api_key_ref.trim(),r=this.relaySecretResolver?.(t);if(!r)throw Object.assign(new Error(`relay credential "${t}" is unavailable`),{code:"RELAY_SECRET_UNAVAILABLE"});return{...e,api_key:r}}toRuntimeInstanceConfig(e,t={}){const r=this.resolveAgentProvider(e.provider);return We(r===e.provider?e:{...e,provider:r},t)}getAgentProvider(e){return this.resolveAgentProvider(this.rawConfigMap.get(e)?.entry.provider)}async restartAgent(e){const t=this.rawConfigMap.get(e);if(!t)throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});await this.replaceInstance(e,t.entry,t.file),l.info("manager",`Restarted agent: ${e}`)}async setAgentProvider(e,t,r){const a=this.rawConfigMap.get(e);if(!a)throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});const c=String(a.entry.client_type??"").trim().toLowerCase();F(c)&&(t?.base_url?.trim()&&t?.api_key?.trim()&&t?.model?.trim()?await W(e,{baseUrl:t.base_url.trim(),apiKey:t.api_key.trim(),model:t.model.trim()}):await K(e));const i=t?.api_key_ref?.trim()?Object.fromEntries(Object.entries(t).filter(([o])=>o!=="api_key")):t,m={...a.entry,provider:i};if(r?.deferRestart){this.rawConfigMap.set(e,{...a,entry:m}),this.instances.find(o=>o.name===e)?.markRelayEnvStale(),this.persistAgentsConfig(),l.info("manager",`Updated provider config for agent (deferred restart): ${e}`);return}await this.replaceInstance(e,m,a.file),this.persistAgentsConfig(),l.info("manager",`Updated provider config for agent: ${e}`)}hasDirectRelayConfig(e){const t=this.rawConfigMap.get(e)?.entry.provider;return!!(t?.api_key_ref?.trim()||t?.api_key?.trim())}getDirectRelayConfigAgentNames(){return[...this.rawConfigMap.entries()].filter(([,e])=>!!e.entry.provider&&(ve(e.entry.client_type)||["claude","codex"].includes(String(e.entry.client_type??"").trim().toLowerCase()))).map(([e])=>e)}async fetchRelayCredential(e,t){const r=this.instances.find(a=>a.name===e);if(!r)throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});return r.fetchRelayCredential(t)}async clearDirectProviderConfigs(){const e=this.getDirectRelayConfigAgentNames(),t=[];for(const r of e)try{await this.setAgentProvider(r,void 0,{deferRestart:this.isAgentBusy(r)}),t.push(r)}catch(a){l.warn("manager",`Failed to clear provider config for "${r}": ${a instanceof Error?a.message:String(a)}`)}return t}async replaceInstance(e,t,r){const a=this.toRuntimeInstanceConfig(t);await this.removeAgentInternal(e,{keepShareCache:!0}),await this.startInstanceFromConfig(a,t,r),this.restoreCachedSharedInstances(a)}async reload(){const e=this.reloadChain.catch(()=>{}).then(()=>this.doReload());return this.reloadChain=e.catch(()=>{}),e}async doReload(){if(this.stopping)throw Object.assign(new Error("manager is stopping"),{code:"RELOAD_UNSAFE"});const{entries:e,fileErrors:t,skipped:r}=V(this.configDir);for(const o of r)l.info("manager",`reload: skipped ${o}: not an agent config (no "agents" key)`);if(t.length>0){const o=t.map(s=>`${s.file}: ${s.error}`).join("; ");throw Object.assign(new Error(`reload aborted: ${t.length} config file(s) failed to parse [${o}]`),{code:"RELOAD_UNSAFE"})}if(e.length===0)throw Object.assign(new Error("reload aborted: no valid agent config found"),{code:"RELOAD_UNSAFE"});const a=[];for(const{entry:o}of e)try{this.toRuntimeInstanceConfig(o,{quiet:!0})}catch(s){const f=o?.name,g=typeof f=="string"&&f.trim()?f:"<unknown>";a.push({name:g,error:s instanceof Error?s.message:String(s)})}if(a.length>0){const o=a.map(s=>`${s.name}: ${s.error}`).join("; ");throw Object.assign(new Error(`reload aborted: ${a.length} agent config(s) invalid [${o}]`),{code:"RELOAD_UNSAFE"})}const c=new Map;for(const o of e){const s=String(o.entry?.name??"").trim();s&&(c.has(s)&&l.warn("manager",`reload: duplicate agent name "${s}", last one wins`),c.set(s,o))}const i={added:[],removed:[],restarted:[],unchanged:[],failed:[]},m=new Set(this.rawConfigMap.keys());for(const o of m)if(!c.has(o))try{await this.removeAgentInternal(o),i.removed.push(o)}catch(s){i.failed.push({name:o,error:s instanceof Error?s.message:String(s)})}for(const[o,s]of c){const f=this.rawConfigMap.get(o);if(f)if(Qe(f.entry,s.entry))this.rawConfigMap.set(o,s),i.unchanged.push(o);else try{await this.replaceInstance(o,s.entry,s.file),i.restarted.push(o)}catch(g){i.failed.push({name:o,error:g instanceof Error?g.message:String(g)})}else try{await this.addAgentInternal(s.entry,s.file),i.added.push(o)}catch(g){i.failed.push({name:o,error:g instanceof Error?g.message:String(g)})}}return l.info("manager",`reload done: +${i.added.length} -${i.removed.length} ~${i.restarted.length} =${i.unchanged.length} !${i.failed.length}`),await this.ensureAuxServices(),i}async checkUpgrade(){return this.upgradeChecker?this.upgradeChecker.checkForUpdate():{available:!1}}triggerUpgrade(){this.upgradeChecker?.triggerCheck()}async probeAll(e={}){return Ge(this.instances,e)}async probeOne(e,t={}){return ze(this.instances,e,t)}listInstallable(){return this.installer.listInstallable()}async installAgent(e){const t=await this.installer.install(e);return Ee(t),t}getInstallProgress(e){return this.installer.getProgress(e)??null}}async function Ge(n,e){const t=e.concurrency??4,r=Date.now(),a=new Array(n.length);await new Promise(s=>{let f=0,g=0;const d=n.length;if(d===0){s();return}function p(_){const S=n[_];S.probe(e).then(k=>{a[_]=k,y()},k=>{a[_]={agent_name:S.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:k?.message??String(k)}},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}},y()})}function y(){g++,f<d?p(f++):g===d&&s()}const u=Math.min(t,d);for(let _=0;_<u;_++)p(f++)});const c=a.filter(s=>s.status==="healthy").length,i=a.filter(s=>s.status==="degraded").length,m=a.filter(s=>s.status==="unavailable").length,o=await Re();return{ok:c===a.length&&a.length>0,total:a.length,healthy:c,degraded:i,unavailable:m,installed_clients:o,agents:a,probed_at:r,duration_ms:Date.now()-r}}async function ze(n,e,t){const r=n.find(a=>a.name===e);if(!r)throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});return r.probe(t)}export{St as Manager,z as normalizeAgentProviderForRuntime,Re as probeInstalledClientCommands,Ge as probeInstances,ze as probeOneInstance};
1
+ import{join as k}from"node:path";import{homedir as le}from"node:os";import{AgentInstance as F}from"./bridge/bridge.js";import{buildSharedInstanceConfig as de,diffSharedOwners as he,sharedInstanceKey as ue,SharedOwnersCache as me}from"./manager-share-config.js";import{AgentDeletedError as pe,isAgentDeletedError as C}from"./core/aibot/errors.js";import{GRIX_PATHS as A,log as p}from"./core/log/index.js";import{resolveClientVersion as ge}from"./core/util/client-version.js";import{UpgradeChecker as fe}from"./core/upgrade/upgrade-checker.js";import{SkillSyncer as _e}from"./core/skill-sync/skill-syncer.js";import{AgentGlobalConfigStore as ye}from"./core/persistence/agent-global-config-store.js";import{resolveSkillScanMode as we}from"./adapter/claude/skill-scanner.js";import{buildWireSkills as Ae}from"./core/skill-sync/sync-state.js";import{buildLibrarySkills as Se}from"./core/skill-sync/library-skills.js";import{AUDIT_LOCAL_ACTION_TYPES as ke}from"./core/aibot/types.js";import{buildReportedSkills as ve,logDefaultSkillsCheck as be,cleanupProjectedSkills as B}from"./default-skills/index.js";import{buildProviderEnv as Ie,supportsKiroCwRelay as W,supportsNonMitmRelayConfig as Ce}from"./core/config/provider-env.js";import{resolveHermesHomeDir as Ee}from"./adapter/shared/native-provider-config.js";import{ensureKiroRelay as Q,stopKiroRelay as K}from"./core/proxy/kiro-relay.js";import{resolveCopilotCommand as z}from"./core/runtime/copilot-resolve.js";import{resolveCursorCommand as V}from"./core/runtime/cursor-resolve.js";import{getCliVersion as $e,resolveCliPath as Te}from"./core/util/cli-probe.js";import{AgentInstaller as Pe}from"./core/installer/installer.js";import{reportInstallFailure as Me}from"./core/observability/sentry.js";import{createDefaultAuditProducer as xe}from"./audit/runtime/default-audit-recorder.js";import{normalizeCodexConfigOverrides as De}from"./adapter/codex/config-overrides.js";import{resolveCodexDirectHome as Oe}from"./adapter/codex/direct-home.js";import{AgentConfigRepository as X}from"./core/config/agent-config-repository.js";function Y(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,je=1e4,Le=8e3;function Ne(){const r=z();return[{clientType:"openclaw",command:"openclaw"},{clientType:"claude",command:"claude"},{clientType:"codex",command:"codex"},{clientType:"qwen",command:"qwen"},{clientType:"hermes",command:"hermes"},{clientType:"reasonix",command:"reasonix"},{clientType:"codewhale",command:"codewhale"},{clientType:"opencode",command:"opencode"},{clientType:"cursor",command:V()},{clientType:"pi",command:"pi"},{clientType:"openhuman",command:"openhuman-core"},{clientType:"kiro",command:"kiro-cli"},{clientType:"kimi",command:"kimi"},{clientType:"copilot",command:r.command},{clientType:"agy",command:"agy"},{clientType:"deepseek",command:"dsh"}]}async function Ue(){return Promise.all(Ne().map(async r=>{const e=await Te(r.command);if(!e)return{client_type:r.clientType,command:r.command,installed:!1,path:null,version:null};const n=await $e(e,r.versionArgs??["--version"]);return{client_type:r.clientType,command:r.command,installed:!0,path:e,version:n.version,error:n.error}}))}function Ge(r){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:V()};case"reasonix":return{adapterType:"acp",command:"reasonix",args:["acp"],enableSessionBinding:!0};case"codewhale":return{adapterType:"codewhale",command:"codewhale",enableSessionBinding:!0};case"openhuman":return{adapterType:"openhuman",command:"openhuman-core",enableSessionBinding:!0};case"kiro":return{adapterType:"acp",command:"kiro-cli",args:["acp"],enableSessionBinding:!0};case"kimi":return{adapterType:"acp",command:"kimi",args:["acp"],options:{initial_mode:"yolo"},enableSessionBinding:!0};case"opencode":return{adapterType:"opencode",command:"opencode",args:["serve"],enableSessionBinding:!0};case"copilot":{const e=z();return{adapterType:"acp",command:e.command,args:[...e.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};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 qe(r){return k(A.data,`session-bindings-${v(r,"unknown")}.json`)}function He(r,e){return k(A.data,`session-bindings-${v(r,e)}.json`)}function Fe(r,e){return k(A.data,`active-events-${v(r,e)}.json`)}function Be(r){return k(A.data,`pending-events-${v(r,"unknown")}.json`)}function We(r,e){const n=v(r,"agent"),t=v(e,"unknown");return k(A.data,`terminal-outbox-${n}-${t}.json`)}function Qe(...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 Ke(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 ze(r,e={}){const n=ge(),t=String(r.client_type??"").trim().toLowerCase(),s=Ge(t),i=t==="deepseek"?r.dsh?.integration_mode??"profile_bridge":void 0,c=i==="embedded_jsonrpc",o=String(r.ws_url??"").trim(),m=typeof r.name=="string"?r.name:"",a=m.trim(),l="get_session_usage",g="get_rate_limits",h="get_agent_global_config",d=ke;if(!a)throw new Error("agent name is required");if(m!==a)throw new Error(`agent name must not contain leading or trailing whitespace: "${m}"`);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",w={...s.options??{}};if(u==="deepseek-harness"&&(w.integrationMode=i,w.dshProfile=r.dsh?.profile?.trim()||"web",w.autoInstallBridge=r.dsh?.auto_install_bridge??!c,w.autoStartProfile=r.dsh?.auto_start_profile??!c),u==="deepseek-harness"&&r.provider?.model?.trim()&&(w.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`);De(r.codex_config_overrides,`agent ${r.name}: codex_config_overrides`),w.codexConfigOverrides=r.codex_config_overrides}if(u==="codex"&&r.provider?.wire_api!==void 0){const y=r.provider;if(y.wire_api!=="responses"||!y.base_url?.trim()||!y.api_key?.trim()||!y.model?.trim()||!y.catalog_version?.trim()||!y.catalog_sha256?.trim()||!y.catalog_json?.trim())throw Object.assign(new Error(`agent ${r.name}: incomplete Codex direct relay config`),{code:"INVALID_CODEX_DIRECT_CONFIG"});const H=Oe(A.data,r.name);w.codexHome=H,w.codexDirect={codexHome:H,config:{baseUrl:y.base_url.trim(),model:y.model.trim(),...y.reasoning_effort?.trim()?{reasoningEffort:y.reasoning_effort.trim()}:{},token:y.api_key.trim()},catalog:{version:y.catalog_version.trim(),sha256:y.catalog_sha256.trim(),json:y.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,g]}:null,Z=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,g]}:null,T=_?{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,P=u==="pi"?{adapterHint:"pi/base",capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","get_context","file_list","create_folder",l,g]}:null,M=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",g]}: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,O=u==="agy"?{adapterHint:"agy/base",capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","file_list","create_folder",g]}:null,R=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,g,"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"]),ee=f&&!N.has(t),te=f&&!_?{localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","claude_interaction_reply","file_list","create_folder",...ee?[l]:[]]}:null,ne=t==="kiro"?{localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","file_list","create_folder","thread_compact",l,g]}:null,se=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,re=u==="cursor"||u==="opencode"||u==="openhuman"||u==="agy"||f&&N.has(t)?[g]:[l,g];f&&w.raw_transport===void 0&&(w.raw_transport=t==="gemini");const ae=`${t}/base`,I=we(u,t),ie=I==="kiro"?void 0:process.cwd(),j=Y(r.provider),b={...process.env,...r.env};if(I==="hermes"&&!b.HERMES_HOME?.trim()){const y=Ie(t,j);b.HERMES_HOME=Ee({...y,...b},r.name)}const U=ve({mode:I,projectDir:ie,env:b}),G=U.filter(y=>y.source==="connector").map(y=>y.name);G.length>0&&!e.quiet&&p.info("manager",`[${r.name}] injecting connector skills: [${G.join(", ")}]`);const q=Ae(U,A.skills);let oe=q.length>0?q:void 0;const ce=Se({skillsDir:A.skills,mode:I,home:le(),env:b});return{name:a,adapterType:u,aibot:{url:o,agentId:r.agent_id,apiKey:r.api_key,clientType:t,clientVersion:n,adapterHint:R?.adapterHint??s.adapterHint??T?.adapterHint??P?.adapterHint??M?.adapterHint??x?.adapterHint??D?.adapterHint??O?.adapterHint??ae,capabilities:R?.capabilities??S?.capabilities??L?.capabilities??P?.capabilities??M?.capabilities??x?.capabilities??D?.capabilities??O?.capabilities??T?.capabilities??["stream_chunk","local_action_v1","connector_upgrade"],localActions:Qe(R?.localActions??S?.localActions??L?.localActions??Z?.localActions??P?.localActions??M?.localActions??x?.localActions??D?.localActions??O?.localActions??T?.localActions??ne?.localActions??te?.localActions??["exec_approve","exec_reject"],se,re,["connector_rollback","connector_upgrade_push",h,"configure_gateway_provider","skill_upload","skill_delete","skill_enable","skill_disable","skill_refresh",...d]),skills:oe,librarySkills:ce,terminalOutboxPath:We(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:w,acpAuthMethod:w.auth_method,acpInitialMode:w.initial_mode,acpMcpTools:w.acp_mcp_tools,promptTimeoutMs:r.prompt_timeout_ms,bindingsPath:qe(r.agent_id),legacyBindingsPath:He(r.name,r.agent_id),activeEventStorePath:Fe(r.name,r.agent_id),pendingEventStorePath:Be(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:Ke(u,r.event_queue),logDir:A.log,providerBaseUrl:r.provider_base_url?.trim()||void 0,providerApiKey:r.provider_api_key?.trim()||void 0}}function J(){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):je}function Ve(){return Math.min(E(),Le)}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 Xe(r,e){return $(r)===$(e)}class Ct{options;instances=[];configMap=new Map;rawConfigMap=new Map;mutationTail=Promise.resolve();startingInstances=new Set;failedStartAgents=new Set;removingAgents=new Set;instanceGenerations=new WeakMap;sharedInstances=new Map;shareSyncChains=new Map;stopping=!1;lifecycleAdmissionClosed=!1;deletedAgentCleanups=new Set;pendingPlatformDeletes=new Map;upgradeChecker=null;skillSyncer=null;globalConfigStore;configRepository=new X(A.config);sharedOwnersCache=new me(A.data);installer=new Pe;relaySecretResolver;auditProducer=xe();constructor(e={}){this.options=e}runMutation(e,n=[]){const t=this.mutationTail.catch(()=>{}).then(async()=>{if(this.stopping)throw Object.assign(new Error("manager is stopping"),{code:"MANAGER_STOPPING"});await this.retryPendingPlatformDeletesUnlocked();const s=new Set([...this.pendingPlatformDeletes.values()].map(c=>c.name)),i=n.find(c=>s.has(c.trim()));if(i)throw Object.assign(new Error(`Agent "${i}" is pending permanent platform-deletion cleanup`),{code:"AGENT_DELETE_PENDING"});return e()});return this.mutationTail=t.then(()=>{},()=>{}),t}async startTrackedInstance(e){this.startingInstances.add(e);try{await e.start()}finally{this.startingInstances.delete(e)}}requestStartCancellation(e){const n=e.requestStop;typeof n=="function"&&n.call(e)}async stopInstance(e,n,t=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 X(n),p.info("manager",`Loading configs from ${n}`),be(),B(),this.globalConfigStore=new ye(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 g=this.toRuntimeInstanceConfig(a);c.push({config:g,entry:a,file:l}),p.info("manager",`Loaded ${g.name} (${g.adapterType??"acp"}) from ${l}`)}catch(g){const h=typeof a?.name=="string"?a.name:"<unknown>";p.error("manager",`Invalid agent config in ${l} (name=${h}): ${g}`),o++}let m=0;if(c.length>0){const a=J();p.info("manager",`Starting ${c.length} agent(s), startup wait=${a}ms`);const l=d=>{this.instances=this.instances.filter(u=>u!==d)},g=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(g.map(async d=>{const u=await new Promise(f=>{let _=!1;const w=setTimeout(()=>{_||(_=!0,f({kind:"timeout"}))},a);d.startPromise.then(()=>{_||(_=!0,clearTimeout(w),f({kind:"started"}))}).catch(S=>{_||(_=!0,clearTimeout(w),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}m++,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-m);p.info("manager",`${d}/${c.length} agent(s) running now`)}m>0&&p.warn("manager",`${m} agent(s) still connecting in background`)}if(this.instances.length===0&&c.length>0)throw new Error("All agent configurations failed to start");await this.ensureAuxServices()}async ensureAuxServices(){if(this.stopping||this.configMap.size===0)return;const e=[...this.configMap.values()].map(n=>({apiKey:n.aibot.apiKey,wsUrl:n.aibot.url}));if(!this.upgradeChecker&&(this.upgradeChecker=new fe(e,()=>this.hasAnyPendingOrBackgroundWork(),()=>this.closeAllLifecycleAdmission(),{requestGracefulShutdown:this.options.requestGracefulShutdown,reopenLifecycleAdmission:()=>this.reopenAllLifecycleAdmission()}),await this.upgradeChecker.start(),this.stopping)){this.upgradeChecker.stop();return}this.skillSyncer||(this.skillSyncer=new _e(e,A.skills),this.skillSyncer.setOnSyncSuccess(()=>this.refreshOwnerLibrarySkills()),await this.skillSyncer.start(),this.stopping&&this.skillSyncer.stop())}refreshOwnerLibrarySkills(){const e=new Map,n=t=>{let s;try{s=t.skillsSyncGroupKey()}catch(c){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 m=!1;try{m=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(m){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(m){p.warn("manager",`adoptSkillsWireHashFromDisk failed for ${o.name}: ${m}`)}}}async stop(){p.info("manager","Stopping all agents..."),this.stopping=!0;const e=Ve(),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(m=>m!==o),this.startingInstances.delete(o)}catch(m){c.push(m)}}),...[...this.sharedInstances.entries()].map(async([o,m])=>{try{await(i.get(m)??this.stopInstance(m,void 0,t())),this.sharedInstances.get(o)===m&&this.sharedInstances.delete(o),this.startingInstances.delete(m)}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}=he(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=ue(e.name,c);let m;try{const a=de(e,c);m=new F(a,this.globalConfigStore,this.auditProducer),m.setLifecycleBusyChecker(()=>this.hasAnyPendingOrBackgroundWork()),m.setLifecycleAdmissionCloser(()=>this.closeAllLifecycleAdmission()),m.setLifecycleAdmissionRestorer(()=>this.reopenAllLifecycleAdmission()),this.options.requestGracefulShutdown&&m.setDaemonShutdownRequester(this.options.requestGracefulShutdown),this.lifecycleAdmissionClosed&&m.closeLifecycleAdmission(),this.sharedInstances.set(o,m),await this.startTrackedInstance(m),m.forceRefreshSkills(void 0,{force:!0}),p.info("manager",`shared instance started: ${o}`)}catch(a){if(m&&!this.stopping&&!this.removingAgents.has(e.name))try{await this.stopInstance(m),this.sharedInstances.get(o)===m&&this.sharedInstances.delete(o)}catch(g){p.error("manager",`cleanup shared instance failed ${o}: ${g}`)}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(m){p.error("manager",`stop shared instance failed ${c}: ${m}`)}}}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.setShareSetHandler(s=>this.onShareSet(t,e,s)),t.setAgentDeletedHandler(()=>this.onAgentDeletedByPlatform(t)),t.setSkillSyncHandler(()=>this.skillSyncer?.triggerSync()),t.setProviderConfigHandler(s=>this.setAgentProvider(e.name,s)),t.setRelayStateApplyPorts({getLocalState:()=>this.relayStateApplier?.getLocalState(e.name)??{enabled:!1},applyEnable:(s,i)=>{const c=this.relayStateApplier;return c?c.applyEnable(e.name,s,i):Promise.reject(new Error("relay state applier is not configured"))},applyDisable:()=>{const s=this.relayStateApplier;return s?s.applyDisable(e.name):Promise.reject(new Error("relay state applier is not configured"))}}),t.setRelayEnvSettledHandler(()=>this.relayEnvSettledHandler?.()),t.setProviderResolver(()=>Y(this.getAgentProvider(e.name))),t}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=J(),m=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 pe(`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(m.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(m.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=m.error;C(l)&&this.onAgentDeletedByPlatform(i);let g;try{await this.stopInstance(i),this.instances=this.instances.filter(h=>h!==i),this.failedStartAgents.delete(e.name)}catch(h){g=h,this.failedStartAgents.add(e.name)}throw s.targetPersisted&&(this.configMap.set(e.name,e),this.rawConfigMap.set(e.name,{entry:n,file:t})),g!==void 0?new AggregateError([l,g],`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},m=new Map;i&&(this.requestStartCancellation(i),this.startingInstances.has(i)&&m.set(i,this.stopInstance(i,o,t())));for(const[h,d]of this.sharedInstances)h.startsWith(c)&&(this.requestStartCancellation(d),this.startingInstances.has(d)&&m.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]:[],...m.values()],`agent "${e}" shared lifecycle drain`,t())}catch(h){l.push(h)}const g=[];for(const[h,d]of[...this.sharedInstances.entries()])h.startsWith(c)&&g.push((m.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(m.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(g),!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(g=>g.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 m=this.rawConfigMap.get(s),a=this.configMap.get(s),l=[];if(c&&o===i)try{await this.removeAgentInternal(s,{deadlineMs:n})}catch(g){l.push(g)}try{const g=this.configRepository.scan();if(g.fileErrors.length>0){const u=g.fileErrors.map(f=>`${f.file}: ${f.error}`).join("; ");throw new Error(`cannot inspect deleted agent generation [${u}]`)}const h=g.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(g){l.push(g),m&&this.rawConfigMap.set(s,m),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}relayStateApplier;setRelayStateApplier(e){this.relayStateApplier=e}reportRelayStateLocalChange(e){this.instances.find(t=>t.name===e)?.reportRelayStateLocalChange()}setRelaySecretResolver(e){this.relaySecretResolver=e}resolveAgentProvider(e){if(!e?.api_key_ref?.trim())return e;const n=e.api_key_ref.trim(),t=this.relaySecretResolver?.(n);if(!t)throw Object.assign(new Error(`relay credential "${n}" is unavailable`),{code:"RELAY_SECRET_UNAVAILABLE"});return{...e,api_key:t}}toRuntimeInstanceConfig(e,n={}){const t=this.resolveAgentProvider(e.provider);return ze(t===e.provider?e:{...e,provider:t},n)}getAgentProvider(e){return this.resolveAgentProvider(this.rawConfigMap.get(e)?.entry.provider)}async restartAgent(e){return this.runMutation(()=>this.restartAgentUnlocked(e),[e])}async restartAgentUnlocked(e){const n=this.rawConfigMap.get(e);if(!n)throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});await this.replaceInstance(e,n.entry,n.file),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 m=String(o.client_type??"").trim().toLowerCase();try{W(m)&&(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&&(Ce(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 g=a?.name,h=typeof g=="string"&&g.trim()?g:"<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:[]},m=new Set([...this.rawConfigMap.keys()].filter(a=>!c.has(a)));for(const a of m)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 g=this.rawConfigMap.get(a);if(g)if(!Xe(g.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 Ye(this.instances,e)}async probeOne(e,n={}){return Je(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 Ye(r,e){const n=e.concurrency??4,t=Date.now(),s=new Array(r.length);await new Promise(a=>{let l=0,g=0;const h=r.length;if(h===0){a();return}function d(_){const w=r[_];w.probe(e).then(S=>{s[_]=S,u()},S=>{s[_]={agent_name:w.name,client_type:"unknown",adapter_type:"acp",ok:!1,status:"error",probed_at:Date.now(),duration_ms:0,cached:!1,cli:{command:"",installed:!1,path:null,version:null,error:{code:"internal",message: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(){g++,l<h?d(l++):g===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,m=await Ue();return{ok:i===s.length&&s.length>0,total:s.length,healthy:i,degraded:c,unavailable:o,installed_clients:m,agents:s,probed_at:t,duration_ms:Date.now()-t}}async function Je(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{Ct as Manager,Y as normalizeAgentProviderForRuntime,Ue as probeInstalledClientCommands,Ye as probeInstances,Je as probeOneInstance};
@@ -0,0 +1 @@
1
+ import*as n from"node:net";const i={bind:"127.0.0.1",port:0,endpoint:"/mcp",sessionTimeoutMs:18e5,invokeTimeoutMs:3e4};function s(u){const e={bind:u?.bind??i.bind,port:u?.port??i.port,endpoint:u?.endpoint??i.endpoint,sessionTimeoutMs:u?.sessionTimeoutMs??i.sessionTimeoutMs,invokeTimeoutMs:u?.invokeTimeoutMs??i.invokeTimeoutMs,allowedOrigins:u?.allowedOrigins,allowedHosts:u?.allowedHosts};return t(e.bind),e.port!==0&&o(e.port),r(e.sessionTimeoutMs),e}function t(u){if(!u||!n.isIPv4(u)&&!n.isIPv6(u))throw new Error(`\u914D\u7F6E\u6821\u9A8C\u5931\u8D25: bind \u5730\u5740 "${u}" \u4E0D\u662F\u5408\u6CD5\u7684 IPv4 \u6216 IPv6 \u5730\u5740`)}function o(u){if(!Number.isInteger(u)||u<1||u>65535)throw new Error(`\u914D\u7F6E\u6821\u9A8C\u5931\u8D25: port \u503C ${u} \u4E0D\u5728\u5408\u6CD5\u8303\u56F4 1-65535 \u5185\u6216\u4E0D\u662F\u6574\u6570`)}function r(u){if(!Number.isInteger(u)||u<1e3||u>864e5)throw new Error(`\u914D\u7F6E\u6821\u9A8C\u5931\u8D25: session_timeout_ms \u503C ${u} \u4E0D\u5728\u5408\u6CD5\u8303\u56F4 1000-86400000 \u5185`)}export{s as createDefaultGatewayConfig};
@@ -0,0 +1 @@
1
+ const a=3e4;class c{connectionManager;onDisconnected;bindings=new Map;constructor(n,i){this.connectionManager=n,this.onDisconnected=i}async bind(n,i){if(this.bindings.has(n))throw new Error(`Session ${n} is already bound to a connection`);const e=await this.connectWithTimeout(i),t=[],s=e.onDisconnected(()=>{this.removeBinding(n),this.onDisconnected(n)});return t.push(s),this.bindings.set(n,{sessionId:n,handle:e,subscriptions:t}),e}getHandle(n){return this.bindings.get(n)?.handle}unbind(n){const i=this.bindings.get(n);if(i){this.bindings.delete(n);for(const e of i.subscriptions)e();i.handle.disconnect()}}unbindAll(){const n=[...this.bindings.keys()];for(const i of n)this.unbind(i)}connectWithTimeout(n){return new Promise((i,e)=>{let t=!1;const s=setTimeout(()=>{t||(t=!0,e(new Error("Connection bind timeout after 30000ms")))},3e4);this.connectionManager.connect({agentId:n.agentId,apiKey:n.apiKey,url:n.wsUrl,clientType:n.clientType,capabilities:["agent_invoke"],adapterHint:`${n.clientType}/base`},{maxRetries:0}).then(o=>{t?o.disconnect():(t=!0,clearTimeout(s),i(o))}).catch(o=>{t||(t=!0,clearTimeout(s),e(o))})})}removeBinding(n){const i=this.bindings.get(n);if(i){this.bindings.delete(n);for(const e of i.subscriptions)e()}}}export{c as ConnectionBindingImpl};
@@ -0,0 +1 @@
1
+ import{splitTextForAibotProtocol as v}from"../../core/protocol/protocol-text.js";const d=new Set(["grix_reply","grix_complete","grix_event_ack","grix_composing","grix_access_control","grix_status"]);function g(n){return d.has(n)}function p(n,e,t){switch(e){case"grix_reply":return f(n,t);case"grix_complete":return x(n,t);case"grix_event_ack":return C(n,t);case"grix_composing":return S(n,t);case"grix_access_control":return m(n,t);case"grix_status":return y(n);default:return r(`\u672A\u77E5\u4E8B\u4EF6\u5DE5\u5177: ${e}`)}}function f(n,e){const t=String(e.event_id??""),s=String(e.session_id??""),i=String(e.text??""),a=e.quoted_message_id,l=e.is_final===!0;if(!s)return r("\u7F3A\u5C11\u5FC5\u586B\u53C2\u6570: session_id");if(!i)return r("\u7F3A\u5C11\u5FC5\u586B\u53C2\u6570: text");const _=v(i),c=`reply_${t||"proactive"}_${Date.now()}`;for(let o=0;o<_.length;o++)n.sendStreamChunk({event_id:t||void 0,session_id:s,delta_content:_[o],chunk_seq:o+1,is_finish:!1,client_msg_id:c,quoted_message_id:o===0?a:void 0});return n.sendStreamChunk({event_id:t||void 0,session_id:s,delta_content:"",chunk_seq:_.length+1,is_finish:!0,client_msg_id:c}),l&&t&&n.sendEventResult({event_id:t,status:"responded"}),u({ok:!0,chunks:_.length,client_msg_id:c})}function x(n,e){const t=String(e.event_id??""),s=String(e.status??""),i=e.msg;return t?["responded","canceled","failed"].includes(s)?(n.sendEventResult({event_id:t,status:s,msg:i}),u({ok:!0,event_id:t,status:s})):r("status \u5FC5\u987B\u4E3A responded\u3001canceled \u6216 failed"):r("\u7F3A\u5C11\u5FC5\u586B\u53C2\u6570: event_id")}function C(n,e){const t=String(e.event_id??""),s=e.session_id;return t?(n.sendEventAck({event_id:t,session_id:s,received_at:Date.now()}),u({ok:!0,event_id:t})):r("\u7F3A\u5C11\u5FC5\u586B\u53C2\u6570: event_id")}function S(n,e){const t=String(e.session_id??""),s=e.active===!0,i=e.event_id;return t?(n.sendSessionActivitySet({session_id:t,kind:"composing",active:s,ref_event_id:i,ttl_ms:s?3e4:void 0}),u({ok:!0,session_id:t,active:s})):r("\u7F3A\u5C11\u5FC5\u586B\u53C2\u6570: session_id")}function m(n,e){const t=String(e.action??""),s={pair_approve:"pair_approve",pair_deny:"pair_deny",allow_sender:"sender_allow",remove_sender:"sender_remove",set_policy:"policy_set"}[t];if(!s)return r(`\u672A\u77E5 access_control action: ${t}`);const i={};return e.code!=null&&(i.code=e.code),e.sender_id!=null&&(i.sender_id=e.sender_id),e.policy!=null&&(i.policy=e.policy),{content:[{type:"text",text:"__ASYNC_ACCESS_CONTROL__"}],_async:{verb:s,payload:i}}}function y(n){const e=n.status,t=n.getStatusSnapshot();return u({connected:e==="ready",status:e,connected_at:t.connectedAt})}function u(n){return{content:[{type:"text",text:JSON.stringify(n)}],isError:!1}}function r(n){return{content:[{type:"text",text:n}],isError:!0}}export{d as EVENT_TOOL_NAMES,p as executeEventTool,g as isEventTool};
@@ -0,0 +1 @@
1
+ import{createServer as N}from"node:http";import{Server as O}from"@modelcontextprotocol/sdk/server/index.js";import{StreamableHTTPServerTransport as j}from"@modelcontextprotocol/sdk/server/streamableHttp.js";import{ListToolsRequestSchema as R,CallToolRequestSchema as E}from"@modelcontextprotocol/sdk/types.js";import{createSecurityPolicy as h}from"./security.js";import{SessionManagerImpl as q}from"./session-manager.js";import{ToolRegistryImpl as A}from"./tool-registry.js";import{ToolExecutorImpl as C}from"./tool-executor.js";function J(n,r){let a=null,p=!1,m,c;const S=new A,v=new C;let s=null;return{async start(){m=h({serverPort:n.port,allowedOrigins:n.allowedOrigins??[],allowedHosts:n.allowedHosts??[]}),c=new q({maxSessions:1,sessionTimeoutMs:n.sessionTimeoutMs,onSessionExpired:e=>{w(e)}}),a=N((e,t)=>{g(e,t)}),await new Promise((e,t)=>{a.on("error",t),a.listen(n.port,n.bind,()=>{a.removeListener("error",t),p=!0;const i=a.address();i&&typeof i=="object"&&(n.port=i.port,m=h({serverPort:n.port,allowedOrigins:n.allowedOrigins??[],allowedHosts:n.allowedHosts??[]})),e()})})},async stop(){if(p=!1,a&&(await new Promise(e=>{a.close(()=>e())}),a=null),s){try{await s.transport.close()}catch{}try{await s.mcpServer.close()}catch{}s=null}c&&(await c.closeAll(),c.dispose())},getStatus(){return{listening:p,url:`http://${n.bind}:${n.port}${n.endpoint}`,activeSessions:s?1:0}},pushEvent(e){s&&y(s.mcpServer,"notifications/message",{event_id:e.event_id,session_id:e.session_id,sender_id:e.sender_id??"",content:e.content,msg_type:e.msg_type??1,msg_id:e.msg_id??"",session_type:e.session_type??1,quoted_message_id:e.quoted_message_id??"",attachments:e.attachments??[],context_messages:e.context_messages??[]},s.transport)},pushStop(e){s&&y(s.mcpServer,"notifications/event_stop",{event_id:e.event_id,stop_id:e.stop_id,session_id:e.session_id,reason:e.reason??""},s.transport)},pushRevoke(e){s&&y(s.mcpServer,"notifications/event_revoke",{event_id:e.event_id,session_id:e.session_id,reason:e.reason??""},s.transport)},pushLocalAction(e){s&&y(s.mcpServer,"notifications/local_action",e,s.transport)}};async function g(e,t){if(new URL(e.url??"/",`http://${e.headers.host??"localhost"}`).pathname!==n.endpoint){t.writeHead(404,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Not Found"}));return}const i=m.validateRequest(e);if(!i.ok){t.writeHead(i.statusCode??403,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:i.message}));return}const o=e.method?.toUpperCase();if(o==="GET"){const d=f(e);if(!d||!s||s.sessionId!==d){t.writeHead(400,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Bad Request: missing or invalid session"}));return}c.touchActivity(d),await s.transport.handleRequest(e,t);return}if(o==="DELETE"){await x(e,t);return}if(o==="POST"){await _(e,t);return}t.writeHead(405,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Method Not Allowed"}))}async function _(e,t){const i=await b(e);let o;try{o=JSON.parse(i)}catch{t.writeHead(400,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Invalid JSON body"}));return}const d=I(o),l=e.headers.accept??"";if(!l.includes("application/json")||!l.includes("text/event-stream")){t.writeHead(406,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Not Acceptable: Accept header must include both application/json and text/event-stream"}));return}if(d)await T(e,t,o);else{const u=f(e);if(!u){t.writeHead(400,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Bad Request: missing Mcp-Session-Id header"}));return}if(!s||s.sessionId!==u){t.writeHead(404,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Not Found: session does not exist or has expired"}));return}c.touchActivity(u),await s.transport.handleRequest(e,t,o)}}async function T(e,t,i){if(s){t.writeHead(503,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Service Unavailable: gateway already has an active session"}));return}const o=c.createSession().sessionId,d=new j({sessionIdGenerator:()=>o,enableJsonResponse:!1,onsessioninitialized:()=>{},onsessionclosed:()=>{w(o)}}),l=new O({name:"grix-mcp-server",version:"1.0.0"},{capabilities:{tools:{}}});H(l,o),await l.connect(d),s={transport:d,mcpServer:l,sessionId:o},await d.handleRequest(e,t,i);try{c.markReady(o)}catch{}}async function x(e,t){const i=f(e);if(!i){t.writeHead(400,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Bad Request: missing Mcp-Session-Id header"}));return}if(!s||s.sessionId!==i){t.writeHead(404,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Not Found: session does not exist or has expired"}));return}await s.transport.handleRequest(e,t)}function H(e,t){const i=S.getTools();e.setRequestHandler(R,async()=>({tools:i.map(o=>({name:o.name,description:o.description,inputSchema:o.inputSchema}))})),e.setRequestHandler(E,async o=>{const{name:d,arguments:l}=o.params,u=c.getSession(t);return!u||u.state!=="ready"?{content:[{type:"text",text:`Session \u72B6\u6001\u4E0D\u53EF\u7528: ${u?.state??"unknown"}`}],isError:!0}:r.status!=="ready"?{content:[{type:"text",text:`\u8FDE\u63A5\u4E0D\u53EF\u7528: \u5F53\u524D\u72B6\u6001\u4E3A ${r.status}`}],isError:!0}:(c.touchActivity(t),v.execute(r,d,l??{},n.invokeTimeoutMs))})}async function w(e){if(s?.sessionId===e){try{await s.mcpServer.close()}catch{}s=null}try{await c.closeSession(e)}catch{}}}function y(n,r,a,p){return p?p.send({jsonrpc:"2.0",method:r,params:a}):Promise.resolve()}function f(n){const r=n.headers["mcp-session-id"];return Array.isArray(r)?r[0]:r||void 0}function I(n){return n&&typeof n=="object"&&"method"in n?n.method==="initialize":Array.isArray(n)?n.some(r=>r&&typeof r=="object"&&r.method==="initialize"):!1}function b(n){return new Promise((r,a)=>{const p=[];n.on("data",m=>p.push(m)),n.on("end",()=>r(Buffer.concat(p).toString("utf-8"))),n.on("error",a)})}export{J as createMcpGateway};
@@ -0,0 +1 @@
1
+ import{createMcpGateway as a}from"./gateway.js";import{createSecurityPolicy as e}from"./security.js";import{SessionManagerImpl as r}from"./session-manager.js";import{createDefaultGatewayConfig as t}from"./config.js";export{r as SessionManagerImpl,t as createDefaultGatewayConfig,a as createMcpGateway,e as createSecurityPolicy};
@@ -0,0 +1 @@
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};
@@ -0,0 +1 @@
1
+ import{randomUUID as n}from"node:crypto";const i={initializing:0,ready:1,closing:2,closed:3},o=6e4;class a{sessions=new Map;config;scanTimer=null;constructor(s){this.config=s,this.startScanTimer()}createSession(){if(this.getActiveCount()>=this.config.maxSessions)throw new Error(`Session limit exceeded: max ${this.config.maxSessions} active sessions`);const s=Date.now(),t={sessionId:n(),createdAt:s,lastActivityAt:s,state:"initializing"};return this.sessions.set(t.sessionId,t),t}getSession(s){return this.sessions.get(s)}markReady(s){const t=this.sessions.get(s);if(!t)throw new Error(`Session not found: ${s}`);this.transitionState(t,"ready")}async closeSession(s){const t=this.sessions.get(s);if(!t)throw new Error(`Session not found: ${s}`);t.state!=="closed"&&(t.state!=="closing"&&this.transitionState(t,"closing"),this.transitionState(t,"closed"))}touchActivity(s){const t=this.sessions.get(s);t&&(t.lastActivityAt=Date.now())}getActiveCount(){let s=0;for(const t of this.sessions.values())t.state!=="closed"&&s++;return s}async closeAll(){const s=[];for(const t of this.sessions.values())t.state!=="closed"&&s.push(this.closeSession(t.sessionId));await Promise.all(s)}dispose(){this.scanTimer!==null&&(clearInterval(this.scanTimer),this.scanTimer=null)}startScanTimer(){this.scanTimer=setInterval(()=>{this.scanExpiredSessions()},o)}scanExpiredSessions(){const s=Date.now();for(const t of this.sessions.values())t.state==="closed"||t.state==="closing"||s-t.lastActivityAt>this.config.sessionTimeoutMs&&this.expireSession(t.sessionId)}async expireSession(s){await this.closeSession(s),await this.config.onSessionExpired?.(s)}transitionState(s,t){const e=i[s.state];if(i[t]<=e)throw new Error(`Invalid state transition: cannot transition from '${s.state}' to '${t}'`);s.state=t}}export{a as SessionManagerImpl};
@@ -0,0 +1 @@
1
+ import{toolCallToInvoke as i}from"../../core/mcp/tools.js";import{ToolRegistryImpl as l}from"./tool-registry.js";import{validateToolArgs as a}from"./tool-schemas.js";import{isEventTool as p,executeEventTool as d}from"./event-tool-executor.js";class y{registry;constructor(){this.registry=new l}async execute(r,e,t,n){if(!this.registry.hasTool(e))return this.errorResult(`\u672A\u77E5\u5DE5\u5177: ${e}`);const s=a(e,t);if(!s.valid)return this.errorResult(`\u53C2\u6570\u6821\u9A8C\u5931\u8D25: ${s.error}`);if(r.status!=="ready")return this.errorResult(`\u8FDE\u63A5\u4E0D\u53EF\u7528: \u5F53\u524D\u72B6\u6001\u4E3A ${r.status}`);if(p(e))return this.executeEventTool(r,e,t);const o=i(e,t);try{const u=await r.agentInvoke(o.action,o.params,n);return this.normalizeResult(u)}catch(u){const c=u instanceof Error?u.message:String(u);return c.toLowerCase().includes("timeout")?this.errorResult(`\u8C03\u7528\u8D85\u65F6: ${c}`):this.errorResult(`\u8C03\u7528\u5931\u8D25: ${c}`)}}normalizeResult(r){if(r==null||typeof r!="object")return this.successResult(r??null);const e=r,t=typeof e.code=="number"?e.code:0;if(t===0){const s="data"in e?e.data:null;return this.successResult(s??null)}const n=typeof e.msg=="string"?e.msg:"\u672A\u77E5\u9519\u8BEF";return this.errorResult(`\u4E0A\u6E38\u9519\u8BEF [code=${t}]: ${n}`)}successResult(r){return{content:[{type:"text",text:JSON.stringify(r)}],isError:!1}}errorResult(r){return{content:[{type:"text",text:r}],isError:!0}}async executeEventTool(r,e,t){return e==="grix_access_control"?this.executeAccessControl(r,t):d(r,e,t)}async executeAccessControl(r,e){const t=String(e.action??""),n={pair_approve:"pair_approve",pair_deny:"pair_deny",allow_sender:"sender_allow",remove_sender:"sender_remove",set_policy:"policy_set"}[t];if(!n)return this.errorResult(`\u672A\u77E5 access_control action: ${t}`);const s={};e.code!=null&&(s.code=e.code),e.sender_id!=null&&(s.sender_id=e.sender_id),e.policy!=null&&(s.policy=e.policy);try{const o=await r.agentInvoke("claude_access_control",{verb:n,payload:s},3e4);return this.successResult(o)}catch(o){const u=o instanceof Error?o.message:String(o);return this.errorResult(`access_control \u8C03\u7528\u5931\u8D25: ${u}`)}}}export{y as ToolExecutorImpl};
@@ -0,0 +1 @@
1
+ import{TOOLS as o,EVENT_TOOLS as s}from"../../core/mcp/tools.js";const e=new Set(["grix_query","grix_group","grix_message_send","grix_message_unsend","grix_admin"]),r=new Set(["grix_reply","grix_complete","grix_event_ack","grix_composing","grix_access_control","grix_status"]);class a{tools;toolMap;constructor(){this.tools=[...o.filter(t=>e.has(t.name)),...s.filter(t=>r.has(t.name))],this.toolMap=new Map(this.tools.map(t=>[t.name,t]))}getTools(){return this.tools}getTool(t){return this.toolMap.get(t)}hasTool(t){return this.toolMap.has(t)}}export{a as ToolRegistryImpl};
@@ -0,0 +1 @@
1
+ const o={required:["action"],properties:{action:{type:"string",enum:["contact_search","session_search","message_history","message_search"]},id:{type:"string"},keyword:{type:"string",maxLength:200},limit:{type:"integer",minimum:1,maximum:100},offset:{type:"integer",minimum:0},sessionId:{type:"string"},beforeId:{type:"string"}}},a={required:["action"],properties:{action:{type:"string",enum:["create","detail","leave","add_members","remove_members","update_member_role","update_all_members_muted","update_member_speaking","dissolve"]},sessionId:{type:"string"},name:{type:"string",maxLength:128},memberIds:{type:"array",items:{type:"string"},maxItems:100},memberTypes:{type:"array",items:{type:"integer",enum:[1,2]}},memberId:{type:"string"},role:{type:"integer",enum:[1,2]},memberType:{type:"integer"},allMembersMuted:{type:"boolean"},isSpeakMuted:{type:"boolean"},canSpeakWhenAllMuted:{type:"boolean"}}},p={required:["sessionId","content"],properties:{sessionId:{type:"string"},content:{type:"string",maxLength:1e4},msgType:{type:"integer"},quotedMessageId:{type:"string"},threadId:{type:"string"}}},m={required:["sessionId","msgId"],properties:{sessionId:{type:"string"},msgId:{type:"string"}}},g={required:["action"],properties:{action:{type:"string",enum:["create_agent","list_categories","create_category","update_category","assign_category","rotate_api_key"]},agentName:{type:"string"},introduction:{type:"string"},isMain:{type:"boolean"},agentId:{type:"string"},categoryId:{type:"string"},name:{type:"string"},parentId:{type:"string"},sortOrder:{type:"integer"}}},y={required:["session_id","text"],properties:{event_id:{type:"string"},session_id:{type:"string"},text:{type:"string",maxLength:5e4},quoted_message_id:{type:"string"},is_final:{type:"boolean"}}},d={required:["event_id","status"],properties:{event_id:{type:"string"},status:{type:"string",enum:["responded","canceled","failed"]},msg:{type:"string",maxLength:500}}},c={required:["event_id"],properties:{event_id:{type:"string"},session_id:{type:"string"}}},l={required:["session_id","active"],properties:{session_id:{type:"string"},active:{type:"boolean"},event_id:{type:"string"}}},_={required:["action"],properties:{action:{type:"string",enum:["pair_approve","pair_deny","allow_sender","remove_sender","set_policy"]},code:{type:"string"},sender_id:{type:"string"},policy:{type:"string",enum:["allowlist","open","disabled"]}}},f={required:[],properties:{}},C={grix_query:o,grix_group:a,grix_message_send:p,grix_message_unsend:m,grix_admin:g,grix_reply:y,grix_complete:d,grix_event_ack:c,grix_composing:l,grix_access_control:_,grix_status:f};function B(r,t){const e=C[r];if(!e)return{valid:!1,error:`\u672A\u77E5\u5DE5\u5177: ${r}`};for(const i of e.required)if(t[i]===void 0||t[i]===null)return{valid:!1,error:`\u7F3A\u5C11\u5FC5\u586B\u53C2\u6570: ${i}`};for(const[i,u]of Object.entries(t)){if(u==null)continue;const n=e.properties[i];if(!n)continue;const s=$(i,u,n);if(s)return{valid:!1,error:s}}return{valid:!0}}function $(r,t,e){switch(e.type){case"string":if(typeof t!="string")return`\u53C2\u6570 ${r} \u7C7B\u578B\u9519\u8BEF: \u671F\u671B string\uFF0C\u5B9E\u9645 ${typeof t}`;if(e.maxLength!==void 0&&t.length>e.maxLength)return`\u53C2\u6570 ${r} \u8D85\u8FC7\u6700\u5927\u957F\u5EA6 ${e.maxLength}\uFF0C\u5B9E\u9645 ${t.length}`;if(e.enum&&!e.enum.includes(t))return`\u53C2\u6570 ${r} \u503C "${t}" \u4E0D\u5728\u5141\u8BB8\u8303\u56F4 [${e.enum.join(", ")}]`;break;case"integer":if(typeof t!="number"||!Number.isInteger(t))return`\u53C2\u6570 ${r} \u7C7B\u578B\u9519\u8BEF: \u671F\u671B integer\uFF0C\u5B9E\u9645 ${typeof t=="number"?"\u6D6E\u70B9\u6570":typeof t}`;if(e.minimum!==void 0&&t<e.minimum)return`\u53C2\u6570 ${r} \u503C ${t} \u5C0F\u4E8E\u6700\u5C0F\u503C ${e.minimum}`;if(e.maximum!==void 0&&t>e.maximum)return`\u53C2\u6570 ${r} \u503C ${t} \u5927\u4E8E\u6700\u5927\u503C ${e.maximum}`;if(e.enum&&!e.enum.includes(t))return`\u53C2\u6570 ${r} \u503C ${t} \u4E0D\u5728\u5141\u8BB8\u8303\u56F4 [${e.enum.join(", ")}]`;break;case"boolean":if(typeof t!="boolean")return`\u53C2\u6570 ${r} \u7C7B\u578B\u9519\u8BEF: \u671F\u671B boolean\uFF0C\u5B9E\u9645 ${typeof t}`;break;case"array":if(!Array.isArray(t))return`\u53C2\u6570 ${r} \u7C7B\u578B\u9519\u8BEF: \u671F\u671B array\uFF0C\u5B9E\u9645 ${typeof t}`;if(e.maxItems!==void 0&&t.length>e.maxItems)return`\u53C2\u6570 ${r} \u8D85\u8FC7\u6700\u5927\u5143\u7D20\u6570 ${e.maxItems}\uFF0C\u5B9E\u9645 ${t.length}`;if(e.items)for(let i=0;i<t.length;i++){const u=t[i];if(e.items.type==="string"&&typeof u!="string")return`\u53C2\u6570 ${r}[${i}] \u7C7B\u578B\u9519\u8BEF: \u671F\u671B string\uFF0C\u5B9E\u9645 ${typeof u}`;if(e.items.type==="integer"){if(typeof u!="number"||!Number.isInteger(u))return`\u53C2\u6570 ${r}[${i}] \u7C7B\u578B\u9519\u8BEF: \u671F\u671B integer\uFF0C\u5B9E\u9645 ${typeof u}`;if(e.items.enum&&!e.items.enum.includes(u))return`\u53C2\u6570 ${r}[${i}] \u503C ${u} \u4E0D\u5728\u5141\u8BB8\u8303\u56F4 [${e.items.enum.join(", ")}]`}}break}}export{B as validateToolArgs};
@@ -1,12 +1,12 @@
1
- import{existsSync as A,readFileSync as j}from"node:fs";import{mkdir as x,readdir as E,readFile as S,rm as w,stat as z,writeFile as g}from"node:fs/promises";import d from"node:os";import u from"node:path";import{GRIX_HOME_ENV as C}from"../core/config/paths.js";import{isProcessRunning as D,runCommand as m,spawnDetached as F,isWindowsElevated as J,killProcessesByCommandLine as B}from"./process-control.js";import{getServicePrefix as T,parseConfigDirFromPlistXML as Z,parseConfigDirFromSystemdUnit as q,resolveBareDaemonMarkerPath as k,resolveLinuxUserUnitPath as K,resolveMacOSLaunchAgentPath as Y}from"./service-paths.js";function p(t){return String(t??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}function L(t){return`'${String(t??"").replace(/'/g,"'\\''")}'`}function M(t){return String(t??"").replace(/\\/g,"\\\\").replace(/"/g,'\\"').replace(/\n/g,"\\n").replace(/\r/g,"\\r")}function N(t){return`"${M(t).replace(/%/g,"%%")}"`}function tt(t){return`"${M(t).replace(/\$/g,()=>"$$").replace(/%/g,"%%")}"`}function U(t){return[t.nodePath,t.cliPath,...t.configDir?["--config-dir",t.configDir]:[]]}function et(t){const e=U(t);return`<?xml version="1.0" encoding="UTF-8"?>
1
+ import{existsSync as T,readFileSync as M}from"node:fs";import{mkdir as k,readdir as F,readFile as S,rm as y,stat as q,writeFile as $}from"node:fs/promises";import d from"node:os";import c from"node:path";import{GRIX_HOME_ENV as C}from"../core/config/paths.js";import{isProcessRunning as I,runCommand as f,spawnDetached as _,isWindowsElevated as K,killProcessesByCommandLine as U}from"./process-control.js";import{getServicePrefix as D,parseConfigDirFromPlistXML as Z,parseConfigDirFromSystemdUnit as Y,resolveBareDaemonMarkerPath as x,resolveLinuxUserUnitPath as tt,resolveMacOSLaunchAgentPath as et}from"./service-paths.js";function g(t){return String(t??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}function A(t){return`'${String(t??"").replace(/'/g,"'\\''")}'`}function G(t){return String(t??"").replace(/\\/g,"\\\\").replace(/"/g,'\\"').replace(/\n/g,"\\n").replace(/\r/g,"\\r")}function R(t){return`"${G(t).replace(/%/g,"%%")}"`}function nt(t){return`"${G(t).replace(/\$/g,()=>"$$").replace(/%/g,"%%")}"`}function H(t){return[t.nodePath,t.cliPath,...t.configDir?["--config-dir",t.configDir]:[]]}function rt(t){const e=H(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>${p(t.serviceID)}</string>
6
+ <string>${g(t.serviceID)}</string>
7
7
  <key>ProgramArguments</key>
8
8
  <array>
9
- ${e.map(r=>` <string>${p(r)}</string>`).join(`
9
+ ${e.map(n=>` <string>${g(n)}</string>`).join(`
10
10
  `)}
11
11
  </array>
12
12
  <key>RunAtLoad</key>
@@ -17,38 +17,38 @@ ${e.map(r=>` <string>${p(r)}</string>`).join(`
17
17
  <key>ThrottleInterval</key>
18
18
  <integer>30</integer>
19
19
  <key>WorkingDirectory</key>
20
- <string>${p(u.dirname(t.cliPath))}</string>
20
+ <string>${g(c.dirname(t.cliPath))}</string>
21
21
  <key>EnvironmentVariables</key>
22
22
  <dict>
23
23
  <key>${C}</key>
24
- <string>${p(t.rootDir)}</string>
24
+ <string>${g(t.rootDir)}</string>
25
25
  ${t.environmentPath?` <key>PATH</key>
26
- <string>${p(t.environmentPath)}</string>
26
+ <string>${g(t.environmentPath)}</string>
27
27
  `:""} </dict>
28
28
  <key>StandardOutPath</key>
29
- <string>${p(t.stdoutPath)}</string>
29
+ <string>${g(t.stdoutPath)}</string>
30
30
  <key>StandardErrorPath</key>
31
- <string>${p(t.stderrPath)}</string>
31
+ <string>${g(t.stderrPath)}</string>
32
32
  </dict>
33
33
  </plist>
34
- `}function rt(t){const e=U(t).map(r=>tt(r)).join(" ");return`[Unit]
34
+ `}function it(t){const e=H(t).map(n=>nt(n)).join(" ");return`[Unit]
35
35
  Description=grix-connector daemon (${t.serviceID})
36
36
  After=network.target
37
37
 
38
38
  [Service]
39
39
  Type=simple
40
40
  ExecStart=${e}
41
- WorkingDirectory=${N(u.dirname(t.cliPath))}
42
- Environment=${N(`${C}=${t.rootDir}`)}
41
+ WorkingDirectory=${R(c.dirname(t.cliPath))}
42
+ Environment=${R(`${C}=${t.rootDir}`)}
43
43
  Restart=always
44
44
  RestartSec=30
45
- StandardOutput=${N(`append:${t.stdoutPath}`)}
46
- StandardError=${N(`append:${t.stderrPath}`)}
45
+ StandardOutput=${R(`append:${t.stdoutPath}`)}
46
+ StandardError=${R(`append:${t.stderrPath}`)}
47
47
 
48
48
  [Install]
49
49
  WantedBy=default.target
50
- `}function v(t,e){const r=t.replace(/[^a-zA-Z0-9._-]/g,"_");return u.join(e,".grix",`${r}-wrapper.vbs`)}function I(t,e){const r=t.replace(/[^a-zA-Z0-9._-]/g,"_"),n=u.join(e,"AppData","Roaming","Microsoft","Windows","Start Menu","Programs","Startup");return u.join(n,`${r}.lnk`)}function $(t){return String(t??"").replace(/"/g,'""')}function nt(t,e){const r=u.join(u.dirname(t),u.basename(e).replace(/\.lnk$/,"-create.vbs")),n=["Option Explicit","Dim shell, shortcut",'Set shell = CreateObject("WScript.Shell")',`Set shortcut = shell.CreateShortcut("${$(e)}")`,'shortcut.TargetPath = "wscript.exe"',`shortcut.Arguments = "//B //NoLogo \\"${$(t)}\\""`,"shortcut.WindowStyle = 7","shortcut.Save",""].join(`\r
51
- `);return{path:r,content:n}}async function it(t){const{wrapperPath:e,shortcutPath:r,runCommand:n}=t,i=nt(e,r);for(let o=1;o<=3;o+=1){try{await g(i.path,i.content,"utf8"),await n("wscript.exe",[i.path,"//B","//NoLogo"])}catch{}finally{await w(i.path,{force:!0}).catch(()=>{})}if(A(r))return!0}return console.warn(`Warning: failed to create Windows logon-autostart shortcut at ${r}. 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 ot(t){try{const e=await t("whoami",["/user","/fo","csv","/nh"],{allowFailure:!0}),r=String(e?.stdout??"").match(/"(S-1-[0-9-]+)"/);if(r)return r[1]}catch{}return null}function at(t){const e=`//B //NoLogo "${t.wrapperPath}"`;return`<?xml version="1.0" encoding="UTF-16"?>
50
+ `}function P(t,e){const n=t.replace(/[^a-zA-Z0-9._-]/g,"_");return c.join(e,".grix",`${n}-wrapper.vbs`)}function j(t,e){const n=t.replace(/[^a-zA-Z0-9._-]/g,"_"),r=c.join(e,"AppData","Roaming","Microsoft","Windows","Start Menu","Programs","Startup");return c.join(r,`${n}.lnk`)}function h(t){return String(t??"").replace(/"/g,'""')}function N(t){return`'${String(t??"").replace(/'/g,"''")}'`}function at(t,e){const n=c.join(c.dirname(t),c.basename(e).replace(/\.lnk$/,"-create.vbs")),r=["Option Explicit","Dim shell, shortcut",'Set shell = CreateObject("WScript.Shell")',`Set shortcut = shell.CreateShortcut("${h(e)}")`,'shortcut.TargetPath = "wscript.exe"',`shortcut.Arguments = "//B //NoLogo \\"${h(t)}\\""`,"shortcut.WindowStyle = 7","shortcut.Save",""].join(`\r
51
+ `);return{path:n,content:r}}async function ot(t){const{wrapperPath:e,shortcutPath:n,runCommand:r}=t,i=at(e,n);for(let a=1;a<=3;a+=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(T(n))return!0}return console.warn(`Warning: failed to create Windows logon-autostart shortcut at ${n}. 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 st(t){try{const e=await t("whoami",["/user","/fo","csv","/nh"],{allowFailure:!0}),n=String(e?.stdout??"").match(/"(S-1-[0-9-]+)"/);if(n)return n[1]}catch{}return null}function ct(t){const e=`//B //NoLogo "${t.wrapperPath}"`;return`<?xml version="1.0" encoding="UTF-16"?>
52
52
  <Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
53
53
  <RegistrationInfo>
54
54
  <Description>grix-connector daemon autostart</Description>
@@ -56,12 +56,12 @@ WantedBy=default.target
56
56
  <Triggers>
57
57
  <LogonTrigger>
58
58
  <Enabled>true</Enabled>
59
- <UserId>${p(t.userId)}</UserId>
59
+ <UserId>${g(t.userId)}</UserId>
60
60
  </LogonTrigger>
61
61
  </Triggers>
62
62
  <Principals>
63
63
  <Principal id="Author">
64
- <UserId>${p(t.userId)}</UserId>
64
+ <UserId>${g(t.userId)}</UserId>
65
65
  <LogonType>InteractiveToken</LogonType>
66
66
  <RunLevel>LeastPrivilege</RunLevel>
67
67
  </Principal>
@@ -87,16 +87,17 @@ WantedBy=default.target
87
87
  <Actions Context="Author">
88
88
  <Exec>
89
89
  <Command>wscript.exe</Command>
90
- <Arguments>${p(e)}</Arguments>
90
+ <Arguments>${g(e)}</Arguments>
91
91
  </Exec>
92
92
  </Actions>
93
93
  </Task>
94
- `}async function st(t){const{serviceID:e,wrapperPath:r,runCommand:n}=t,i=await ot(n)??d.userInfo().username,o=u.join(u.dirname(r),`${e}-task.xml`);try{const a=at({userId:i,wrapperPath:r});return await g(o,"\uFEFF"+a,"utf16le"),await n("schtasks",["/Create","/TN",e,"/XML",o,"/F"]),!0}catch{return!1}finally{await w(o,{force:!0}).catch(()=>{})}}function ct(t){const e=[`"${$(t.nodePath)}"`,`"${$(t.cliPath)}"`,...t.configDir?["--config-dir",`"${$(t.configDir)}"`]:[]].join(" ");return["Option Explicit","Dim shell, processEnv, command, delayMs, rapidCount",`command = "${$(e)}"`,'Set shell = CreateObject("WScript.Shell")','Set processEnv = shell.Environment("Process")',`processEnv("${C}") = "${$(t.rootDir)}"`,"delayMs = 5000","rapidCount = 0","Do"," shell.Run command, 0, True"," rapidCount = rapidCount + 1"," If rapidCount >= 10 Then"," WScript.Sleep 300000"," rapidCount = 0"," Else"," WScript.Sleep delayMs"," End If","Loop",""].join(`\r
95
- `)}function b(t){return`gui/${t??0}`}function _(t){const e=String(t?.stdout??"").trim();return String(t?.stderr??"").trim()||e||`exit=${Number(t?.exitCode??-1)}`}function W(t,e,r,n){if(t?.trim())return u.resolve(t);const i=e||r;return i?u.resolve(u.dirname(u.dirname(i))):u.join(n,".grix")}async function H(t){try{await z(t)}catch{throw new Error(`launchd plist missing: ${t}`)}}async function G(t){const{serviceID:e,domain:r,runCommand:n}=t,i=`${r}/${e}`,o=await n("launchctl",["bootout",i],{allowFailure:!0});if(Number(o?.exitCode??-1)!==0){const c=await n("launchctl",["print",i],{allowFailure:!0});if(Number(c?.exitCode??1)===0){await new Promise(s=>setTimeout(s,250));const a=await n("launchctl",["print",i],{allowFailure:!0});if(Number(a?.exitCode??1)===0)throw new Error(`launchctl bootout ${i}: ${_(o)}`)}return}for(let c=0;c<20;c+=1){const a=await n("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 Q(t){const{serviceID:e,domain:r,definitionPath:n,runCommand:i}=t,o=`${r}/${e}`,c=await i("launchctl",["bootstrap",r,n],{allowFailure:!0});if(Number(c?.exitCode??-1)===0)return;const a=await i("launchctl",["print",o],{allowFailure:!0});Number(a?.exitCode??1)===0&&await G(t);const s=await i("launchctl",["bootstrap",r,n],{allowFailure:!0});if(Number(s?.exitCode??-1)!==0)throw new Error(`launchctl bootstrap ${r} failed: initial=${_(c)}; retry=${_(s)}`)}function ut(){return{platform:"darwin",kind:"launchd",async install({serviceID:t,nodePath:e,cliPath:r,configDir:n,rootDir:i,stdoutPath:o,stderrPath:c,environmentPath:a="",homeDir:s=d.homedir()}){const l=Y(t,s),f=W(i,o,c,s);return await x(u.dirname(l),{recursive:!0}),await g(l,et({serviceID:t,nodePath:e,cliPath:r,configDir:n,rootDir:f,stdoutPath:o,stderrPath:c,environmentPath:a}),{encoding:"utf8",mode:384}),{definitionPath:l}},async start({serviceID:t,definitionPath:e,runCommand:r=m,uid:n=process.getuid?.()??0}){const i=b(n);await H(e),await Q({serviceID:t,domain:i,definitionPath:e,runCommand:r})},async stop({serviceID:t,runCommand:e=m,uid:r=process.getuid?.()??0}){const n=b(r);await e("launchctl",["bootout",`${n}/${t}`],{allowFailure:!0})},async restart({serviceID:t,definitionPath:e,runCommand:r=m,uid:n=process.getuid?.()??0}){const i=b(n),o={serviceID:t,domain:i,definitionPath:e,runCommand:r};await H(e),await G(o),await Q(o)},async uninstall({serviceID:t,definitionPath:e,runCommand:r=m,uid:n=process.getuid?.()??0}){const i=b(n);await r("launchctl",["bootout",`${i}/${t}`],{allowFailure:!0}),await w(e,{force:!0})},async discoverServices({homeDir:t=d.homedir()}={}){const e=u.join(t,"Library","LaunchAgents"),r=await E(e).catch(()=>[]),n=T("darwin"),i=[];for(const o of r){if(!o.startsWith(n)||!o.endsWith(".plist"))continue;const c=o.slice(0,-6),a=u.join(e,o);let s=null;try{const l=await S(a,"utf8");s=Z(l)}catch{}i.push({serviceID:c,definitionPath:a,configDir:s})}return i},async isServiceLoaded({serviceID:t,runCommand:e=m,uid:r=process.getuid?.()??0}){const n=b(r),i=await e("launchctl",["print",`${n}/${t}`],{allowFailure:!0});return Number(i?.exitCode??1)===0}}}function lt(){return{platform:"win32",kind:"task-scheduler",async install({serviceID:t,nodePath:e,cliPath:r,configDir:n,rootDir:i,stdoutPath:o,stderrPath:c,runCommand:a=m,homeDir:s=d.homedir()}){const l=v(t,s),f=W(i,o,c,s);await x(u.dirname(l),{recursive:!0}),await g(l,ct({nodePath:e,cliPath:r,configDir:n,rootDir:f}),"utf8");let h=!1;J()&&(h=await st({serviceID:t,wrapperPath:l,runCommand:a}));const y=I(t,s);return h?await w(y,{force:!0}).catch(()=>{}):await it({wrapperPath:l,shortcutPath:y,runCommand:a}),{definitionPath:h?`task:${t}`:`startup:${t}`}},async start({serviceID:t,definitionPath:e,runCommand:r=m,homeDir:n=d.homedir()}){const i=v(t,n);if(e.startsWith("task:"))try{await r("schtasks",["/Run","/TN",t]);return}catch{}F("wscript.exe",["//B","//NoLogo",i])},async stop({serviceID:t,runCommand:e=m,homeDir:r=d.homedir()}){await e("schtasks",["/End","/TN",t],{allowFailure:!0});const n=`${t}-wrapper.vbs`;await B(n,{platform:"win32"})},async restart({serviceID:t,definitionPath:e,runCommand:r=m,homeDir:n=d.homedir()}){await r("schtasks",["/End","/TN",t],{allowFailure:!0});const i=`${t}-wrapper.vbs`;await B(i,{platform:"win32"});const o=v(t,n);if(e?.startsWith("task:"))try{await r("schtasks",["/Run","/TN",t]);return}catch{}F("wscript.exe",["//B","//NoLogo",o])},async uninstall({serviceID:t,runCommand:e=m,homeDir:r=d.homedir()}){await e("schtasks",["/Delete","/TN",t,"/F"],{allowFailure:!0});const n=v(t,r);await w(n,{force:!0});const i=I(t,r);await w(i,{force:!0})},async discoverServices({homeDir:t=d.homedir(),runCommand:e=m}={}){const r=T("win32"),n=await e("schtasks",["/Query","/FO","CSV","/NH"],{allowFailure:!0}),i=[];if(Number(n?.exitCode??-1)===0){const a=String(n.stdout??"").split(/\r?\n/);for(const s of a){const l=s.match(/^"([^"]+)"/);if(!l)continue;const f=l[1];if(!f.startsWith(r))continue;let h=null;try{const y=v(f,t),O=(await S(y,"utf8")).match(/--config-dir\s+""([^""]+)""/);O&&(h=O[1])}catch{}i.push({serviceID:f,definitionPath:`task:${f}`,configDir:h})}}const o=u.join(t,"AppData","Roaming","Microsoft","Windows","Start Menu","Programs","Startup"),c=await E(o).catch(()=>[]);for(const a of c){if(!a.startsWith(r)||!a.endsWith(".lnk"))continue;const s=a.slice(0,-4);if(i.some(f=>f.serviceID===s))continue;let l=null;try{const f=v(s,t),y=(await S(f,"utf8")).match(/--config-dir\s+""([^""]+)""/);y&&(l=y[1])}catch{}i.push({serviceID:s,definitionPath:`startup:${s}`,configDir:l})}return i},async isServiceLoaded({serviceID:t,runCommand:e=m,homeDir:r=d.homedir()}){const n=await e("schtasks",["/Query","/TN",t,"/NH"],{allowFailure:!0});return Number(n?.exitCode??1)===0?!0:A(I(t,r))}}}function dt(){return{platform:"linux",kind:"systemd-user",async install({serviceID:t,nodePath:e,cliPath:r,configDir:n,rootDir:i,stdoutPath:o,stderrPath:c,homeDir:a=d.homedir(),runCommand:s=m}){const l=K(t,a),f=W(i,o,c,a);return await x(u.dirname(l),{recursive:!0}),await g(l,rt({serviceID:t,nodePath:e,cliPath:r,configDir:n,rootDir:f,stdoutPath:o,stderrPath:c}),{encoding:"utf8",mode:384}),await s("systemctl",["--user","daemon-reload"]),await s("systemctl",["--user","enable",`${t}.service`]),{definitionPath:l}},async start({serviceID:t,runCommand:e=m}){await e("systemctl",["--user","start",`${t}.service`])},async stop({serviceID:t,runCommand:e=m}){await e("systemctl",["--user","stop",`${t}.service`],{allowFailure:!0})},async restart({serviceID:t,runCommand:e=m}){await e("systemctl",["--user","restart",`${t}.service`])},async uninstall({serviceID:t,definitionPath:e,runCommand:r=m}){await r("systemctl",["--user","stop",`${t}.service`],{allowFailure:!0}),await r("systemctl",["--user","disable",`${t}.service`],{allowFailure:!0}),await w(e,{force:!0}),await r("systemctl",["--user","daemon-reload"])},async discoverServices({homeDir:t=d.homedir()}={}){const e=u.join(t,".config","systemd","user"),r=await E(e).catch(()=>[]),n=T("linux"),i=[];for(const o of r){if(!o.startsWith(n)||!o.endsWith(".service"))continue;const c=o.slice(0,-8),a=u.join(e,o);let s=null;try{const l=await S(a,"utf8");s=q(l)}catch{}i.push({serviceID:c,definitionPath:a,configDir:s})}return i},async isServiceLoaded({serviceID:t,runCommand:e=m}){const r=await e("systemctl",["--user","is-active",`${t}.service`],{allowFailure:!0});return String(r?.stdout??"").trim()==="active"}}}function P(t){try{const e=j(t,"utf8"),r=JSON.parse(e);if(r&&typeof r.service_id=="string"&&typeof r.node_path=="string"&&typeof r.cli_path=="string"&&typeof r.root_dir=="string")return r}catch{}return null}function mt(t){return u.join(t,"daemon.lock.json")}function R(t){const e=mt(t);try{const r=j(e,"utf8"),n=JSON.parse(r);if(typeof n?.pid=="number")return n.pid}catch{}return 0}function V(t){return u.join(t,".profile")}function ft(t){return t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function pt(t){const e=[L(t.nodePath),L(t.cliPath)];t.configDir&&e.push("--config-dir",L(t.configDir));const r=`env ${C}=${L(t.rootDir)} ${e.join(" ")}`;return`${r} status >/dev/null 2>&1 || (nohup ${r} start >/dev/null 2>&1 &)`}function ht(t,e){return`# >>> grix-connector autostart [${t}] >>>
94
+ `}async function lt(t){const{serviceID:e,wrapperPath:n,runCommand:r}=t,i=await st(r)??d.userInfo().username,a=c.join(c.dirname(n),`${e}-task.xml`);try{const o=ct({userId:i,wrapperPath:n});return await $(a,"\uFEFF"+o,"utf16le"),await r("schtasks",["/Create","/TN",e,"/XML",a,"/F"]),!0}catch{return!1}finally{await y(a,{force:!0}).catch(()=>{})}}const ut=3,dt="grix-wrapper-template:";function mt(t){const e=c.join(t.rootDir,"data","upgrade-wrapper-child.json"),n=[`"${t.cliPath}"`,...t.configDir?["--config-dir",`"${t.configDir}"`]:[]],r=["$ErrorActionPreference = 'Stop'",`$recordPath = ${N(e)}`,"$exitCode = 1","try {"," [IO.Directory]::CreateDirectory([IO.Path]::GetDirectoryName($recordPath)) | Out-Null",` $p = Start-Process -FilePath ${N(t.nodePath)} -ArgumentList @(${n.map(N).join(", ")}) -WindowStyle Hidden -PassThru`,` $record = @{ pid = $p.Id; launched_at = [DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds(); boot_time = [DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds() - [Environment]::TickCount64; cli_path = ${N(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(`
95
+ `),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}`,o=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"),m=c.join(t.rootDir,"data","upgrade-pending.json"),p=[`"${h(t.nodePath)}"`,`"${h(o)}"`,"activate","--root",`"${h(t.rootDir)}"`].join(" "),w=`${p} --wrapper-handoff`,L=[`"${h(t.nodePath)}"`,`"${h(s)}"`,"activate","--root",`"${h(t.rootDir)}"`].join(" "),b=`${L} --wrapper-handoff`;return[`' ${dt} ${ut}`,"Option Explicit","Dim shell, processEnv, fso, command, guardianCommand, handoffGuardianCommand, guardianFallbackCommand, handoffGuardianFallbackCommand, guardianPath, guardianFallbackPath, maintenanceFile, pendingFile, delayMs, rapidCount, guardianExit",`command = "${h(l)}"`,`guardianCommand = "${h(p)}"`,`handoffGuardianCommand = "${h(w)}"`,`guardianFallbackCommand = "${h(L)}"`,`handoffGuardianFallbackCommand = "${h(b)}"`,`guardianPath = "${h(o)}"`,`guardianFallbackPath = "${h(s)}"`,`maintenanceFile = "${h(u)}"`,`pendingFile = "${h(m)}"`,'Set shell = CreateObject("WScript.Shell")','Set processEnv = shell.Environment("Process")','Set fso = CreateObject("Scripting.FileSystemObject")',`processEnv("${C}") = "${h(t.rootDir)}"`,'processEnv("GRIX_WRAPPER_SUPERVISED") = "1"',"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
96
+ `)}function v(t){return`gui/${t??0}`}function B(t){const e=String(t?.stdout??"").trim();return String(t?.stderr??"").trim()||e||`exit=${Number(t?.exitCode??-1)}`}function W(t,e,n,r){if(t?.trim())return c.resolve(t);const i=e||n;return i?c.resolve(c.dirname(c.dirname(i))):c.join(r,".grix")}async function V(t){try{await q(t)}catch{throw new Error(`launchd plist missing: ${t}`)}}async function Q(t){const{serviceID:e,domain:n,runCommand:r}=t,i=`${n}/${e}`,a=await r("launchctl",["bootout",i],{allowFailure:!0});if(Number(a?.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 o=await r("launchctl",["print",i],{allowFailure:!0});if(Number(o?.exitCode??1)===0)throw new Error(`launchctl bootout ${i}: ${B(a)}`)}return}for(let l=0;l<20;l+=1){const o=await r("launchctl",["print",i],{allowFailure:!0});if(Number(o?.exitCode??1)!==0)return;await new Promise(s=>setTimeout(s,250))}throw new Error(`launchctl bootout ${i}: service remains loaded`)}async function X(t){const{serviceID:e,domain:n,definitionPath:r,runCommand:i}=t,a=`${n}/${e}`,l=await i("launchctl",["bootstrap",n,r],{allowFailure:!0});if(Number(l?.exitCode??-1)===0)return;const o=await i("launchctl",["print",a],{allowFailure:!0});Number(o?.exitCode??1)===0&&await Q(t);const s=await i("launchctl",["bootstrap",n,r],{allowFailure:!0});if(Number(s?.exitCode??-1)!==0)throw new Error(`launchctl bootstrap ${n} failed: initial=${B(l)}; retry=${B(s)}`)}function ft(){return{platform:"darwin",kind:"launchd",async install({serviceID:t,nodePath:e,cliPath:n,configDir:r,rootDir:i,stdoutPath:a,stderrPath:l,environmentPath:o="",homeDir:s=d.homedir()}){const u=et(t,s),m=W(i,a,l,s);return await k(c.dirname(u),{recursive:!0}),await $(u,rt({serviceID:t,nodePath:e,cliPath:n,configDir:r,rootDir:m,stdoutPath:a,stderrPath:l,environmentPath:o}),{encoding:"utf8",mode:384}),{definitionPath:u}},async start({serviceID:t,definitionPath:e,runCommand:n=f,uid:r=process.getuid?.()??0}){const i=v(r);await V(e),await X({serviceID:t,domain:i,definitionPath:e,runCommand:n})},async stop({serviceID:t,runCommand:e=f,uid:n=process.getuid?.()??0}){const r=v(n);await e("launchctl",["bootout",`${r}/${t}`],{allowFailure:!0})},async restart({serviceID:t,definitionPath:e,runCommand:n=f,uid:r=process.getuid?.()??0}){const i=v(r),a={serviceID:t,domain:i,definitionPath:e,runCommand:n};await V(e),await Q(a),await X(a)},async uninstall({serviceID:t,definitionPath:e,runCommand:n=f,uid:r=process.getuid?.()??0}){const i=v(r);await n("launchctl",["bootout",`${i}/${t}`],{allowFailure:!0}),await y(e,{force:!0})},async discoverServices({homeDir:t=d.homedir()}={}){const e=c.join(t,"Library","LaunchAgents"),n=await F(e).catch(()=>[]),r=D("darwin"),i=[];for(const a of n){if(!a.startsWith(r)||!a.endsWith(".plist"))continue;const l=a.slice(0,-6),o=c.join(e,a);let s=null;try{const u=await S(o,"utf8");s=Z(u)}catch{}i.push({serviceID:l,definitionPath:o,configDir:s})}return i},async isServiceLoaded({serviceID:t,runCommand:e=f,uid:n=process.getuid?.()??0}){const r=v(n),i=await e("launchctl",["print",`${r}/${t}`],{allowFailure:!0});return Number(i?.exitCode??1)===0}}}function ht(){return{platform:"win32",kind:"task-scheduler",async install({serviceID:t,nodePath:e,cliPath:n,configDir:r,rootDir:i,stdoutPath:a,stderrPath:l,runCommand:o=f,homeDir:s=d.homedir()}){const u=P(t,s),m=W(i,a,l,s);await k(c.dirname(u),{recursive:!0}),await $(u,mt({nodePath:e,cliPath:n,configDir:r,rootDir:m}),"utf8");let p=!1;K()&&(p=await lt({serviceID:t,wrapperPath:u,runCommand:o}));const w=j(t,s);return p?await y(w,{force:!0}).catch(()=>{}):await ot({wrapperPath:u,shortcutPath:w,runCommand:o}),{definitionPath:p?`task:${t}`:`startup:${t}`}},async start({serviceID:t,definitionPath:e,runCommand:n=f,homeDir:r=d.homedir()}){const i=P(t,r);if(e.startsWith("task:"))try{await n("schtasks",["/Run","/TN",t]);return}catch{}_("wscript.exe",["//B","//NoLogo",i])},async stop({serviceID:t,runCommand:e=f,homeDir:n=d.homedir()}){await e("schtasks",["/End","/TN",t],{allowFailure:!0});const r=`${t}-wrapper.vbs`;await U(r,{platform:"win32"})},async restart({serviceID:t,definitionPath:e,runCommand:n=f,homeDir:r=d.homedir()}){await n("schtasks",["/End","/TN",t],{allowFailure:!0});const i=`${t}-wrapper.vbs`;await U(i,{platform:"win32"});const a=P(t,r);if(e?.startsWith("task:"))try{await n("schtasks",["/Run","/TN",t]);return}catch{}_("wscript.exe",["//B","//NoLogo",a])},async uninstall({serviceID:t,runCommand:e=f,homeDir:n=d.homedir()}){await e("schtasks",["/Delete","/TN",t,"/F"],{allowFailure:!0});const r=P(t,n);await y(r,{force:!0});const i=j(t,n);await y(i,{force:!0})},async discoverServices({homeDir:t=d.homedir(),runCommand:e=f}={}){const n=D("win32"),r=await e("schtasks",["/Query","/FO","CSV","/NH"],{allowFailure:!0}),i=[];if(Number(r?.exitCode??-1)===0){const o=String(r.stdout??"").split(/\r?\n/);for(const s of o){const u=s.match(/^"([^"]+)"/);if(!u)continue;const m=u[1];if(!m.startsWith(n))continue;let p=null;try{const w=P(m,t),b=(await S(w,"utf8")).match(/--config-dir\s+""([^""]+)""/);b&&(p=b[1])}catch{}i.push({serviceID:m,definitionPath:`task:${m}`,configDir:p})}}const a=c.join(t,"AppData","Roaming","Microsoft","Windows","Start Menu","Programs","Startup"),l=await F(a).catch(()=>[]);for(const o of l){if(!o.startsWith(n)||!o.endsWith(".lnk"))continue;const s=o.slice(0,-4);if(i.some(m=>m.serviceID===s))continue;let u=null;try{const m=P(s,t),w=(await S(m,"utf8")).match(/--config-dir\s+""([^""]+)""/);w&&(u=w[1])}catch{}i.push({serviceID:s,definitionPath:`startup:${s}`,configDir:u})}return i},async isServiceLoaded({serviceID:t,runCommand:e=f,homeDir:n=d.homedir()}){const r=await e("schtasks",["/Query","/TN",t,"/NH"],{allowFailure:!0});return Number(r?.exitCode??1)===0?!0:T(j(t,n))}}}function pt(){return{platform:"linux",kind:"systemd-user",async install({serviceID:t,nodePath:e,cliPath:n,configDir:r,rootDir:i,stdoutPath:a,stderrPath:l,homeDir:o=d.homedir(),runCommand:s=f}){const u=tt(t,o),m=W(i,a,l,o);return await k(c.dirname(u),{recursive:!0}),await $(u,it({serviceID:t,nodePath:e,cliPath:n,configDir:r,rootDir:m,stdoutPath:a,stderrPath:l}),{encoding:"utf8",mode:384}),await s("systemctl",["--user","daemon-reload"]),await s("systemctl",["--user","enable",`${t}.service`]),{definitionPath:u}},async start({serviceID:t,runCommand:e=f}){await e("systemctl",["--user","start",`${t}.service`])},async stop({serviceID:t,runCommand:e=f}){await e("systemctl",["--user","stop",`${t}.service`],{allowFailure:!0})},async restart({serviceID:t,runCommand:e=f}){await e("systemctl",["--user","restart",`${t}.service`])},async uninstall({serviceID:t,definitionPath:e,runCommand:n=f}){await n("systemctl",["--user","stop",`${t}.service`],{allowFailure:!0}),await n("systemctl",["--user","disable",`${t}.service`],{allowFailure:!0}),await y(e,{force:!0}),await n("systemctl",["--user","daemon-reload"])},async discoverServices({homeDir:t=d.homedir()}={}){const e=c.join(t,".config","systemd","user"),n=await F(e).catch(()=>[]),r=D("linux"),i=[];for(const a of n){if(!a.startsWith(r)||!a.endsWith(".service"))continue;const l=a.slice(0,-8),o=c.join(e,a);let s=null;try{const u=await S(o,"utf8");s=Y(u)}catch{}i.push({serviceID:l,definitionPath:o,configDir:s})}return i},async isServiceLoaded({serviceID:t,runCommand:e=f}){const n=await e("systemctl",["--user","is-active",`${t}.service`],{allowFailure:!0});return String(n?.stdout??"").trim()==="active"}}}function E(t){try{const e=M(t,"utf8"),n=JSON.parse(e);if(n&&typeof n.service_id=="string"&&typeof n.node_path=="string"&&typeof n.cli_path=="string"&&typeof n.root_dir=="string")return n}catch{}return null}function gt(t){return c.join(t,"daemon.lock.json")}function O(t){const e=gt(t);try{const n=M(e,"utf8"),r=JSON.parse(n);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 yt(t){const e=[A(t.nodePath),A(t.cliPath)];t.configDir&&e.push("--config-dir",A(t.configDir));const n=`env ${C}=${A(t.rootDir)} ${e.join(" ")}`;return`${n} status >/dev/null 2>&1 || (nohup ${n} start >/dev/null 2>&1 &)`}function $t(t,e){return`# >>> grix-connector autostart [${t}] >>>
96
97
  ${e}
97
98
  # <<< grix-connector autostart [${t}] <<<
98
- `}function X(t){const e=ft(t);return new RegExp(`(?:^|\\n)# >>> grix-connector autostart \\[${e}\\] >>>\\n[\\s\\S]*?\\n# <<< grix-connector autostart \\[${e}\\] <<<\\n?`,"m")}async function wt(t,e,r){let n="";try{n=await S(t,"utf8")}catch{}const i=ht(e,r),o=X(e);let c;if(o.test(n))c=n.replace(o,`
99
- ${i}`).replace(/^\n+/,"");else{const a=n.length===0||n.endsWith(`
99
+ `}function J(t){const e=wt(t);return new RegExp(`(?:^|\\n)# >>> grix-connector autostart \\[${e}\\] >>>\\n[\\s\\S]*?\\n# <<< grix-connector autostart \\[${e}\\] <<<\\n?`,"m")}async function St(t,e,n){let r="";try{r=await S(t,"utf8")}catch{}const i=$t(e,n),a=J(e);let l;if(a.test(r))l=r.replace(a,`
100
+ ${i}`).replace(/^\n+/,"");else{const o=r.length===0||r.endsWith(`
100
101
  `)?"":`
101
- `;c=`${n}${a}${i}`}c!==n&&(await x(u.dirname(t),{recursive:!0}),await g(t,c,{encoding:"utf8"}))}async function gt(t,e){let r="";try{r=await S(t,"utf8")}catch{return}const n=X(e);if(!n.test(r))return;const i=r.replace(n,"").replace(/^\n+/,"");await g(t,i,{encoding:"utf8"})}function yt(){return{platform:"linux",kind:"bare-daemon",async install({serviceID:t,nodePath:e,cliPath:r,configDir:n,rootDir:i,stdoutPath:o,stderrPath:c,homeDir:a=d.homedir()}){const s=k(t,a);await x(u.dirname(s),{recursive:!0});const l=W(i,o,c,a),f={schema_version:1,service_id:t,node_path:e,cli_path:r,config_dir:n??"",root_dir:l,stdout_path:o,stderr_path:c,installed_at:Date.now()};await g(s,`${JSON.stringify(f,null,2)}
102
- `,{encoding:"utf8",mode:384});const h=pt({nodePath:e,cliPath:r,configDir:n??"",rootDir:l});return await wt(V(a),t,h),{definitionPath:s}},async start({serviceID:t,homeDir:e=d.homedir()}){const r=k(t,e),n=P(r);if(!n)throw new Error(`bare-daemon marker missing: ${r}`);const i=[n.cli_path];n.config_dir&&i.push("--config-dir",n.config_dir),F(n.node_path,i,{env:{...process.env,[C]:n.root_dir}})},async stop({serviceID:t,homeDir:e=d.homedir()}){const r=k(t,e),n=P(r);if(!n)return;const i=R(n.root_dir);if(i>0&&D(i))try{process.kill(i,"SIGTERM")}catch{}},async restart(t){await this.stop(t);const e=Date.now()+5e3,r=k(t.serviceID,t.homeDir??d.homedir()),n=P(r);for(;n&&Date.now()<e;){const i=R(n.root_dir);if(i<=0||!D(i))break;await new Promise(o=>setTimeout(o,100))}await this.start(t)},async uninstall({serviceID:t,homeDir:e=d.homedir()}){const r=k(t,e),n=P(r);if(n){const i=R(n.root_dir);if(i>0&&D(i))try{process.kill(i,"SIGTERM")}catch{}}await w(r,{force:!0}),await gt(V(e),t)},async discoverServices({homeDir:t=d.homedir()}={}){const e=u.join(t,".grix","service"),r=await E(e).catch(()=>[]),n=T("linux"),i=[];for(const o of r){if(!o.startsWith(n)||!o.endsWith(".bare.json"))continue;const c=o.slice(0,-10),a=u.join(e,o),s=P(a);i.push({serviceID:c,definitionPath:a,configDir:s?.config_dir||null})}return i},async isServiceLoaded({serviceID:t,homeDir:e=d.homedir()}){const r=k(t,e),n=P(r);if(!n)return!1;const i=R(n.root_dir);return i>0&&D(i)}}}function $t(t=process.getuid?.()??0){if(process.env.GRIX_FORCE_BARE_DAEMON==="1")return!1;const e=process.env.XDG_RUNTIME_DIR;return!!(e&&A(u.join(e,"bus"))||A(`/run/user/${t}/bus`))}function Et(t=process.platform,e={}){return t==="darwin"?ut():t==="win32"?lt():e.systemdUserAvailable??$t()?dt():yt()}export{Et as getPlatformServiceAdapter,$t as isSystemdUserBusAvailable,gt as removeProfileAutostart,wt as upsertProfileAutostart};
102
+ `;l=`${r}${o}${i}`}l!==r&&(await k(c.dirname(t),{recursive:!0}),await $(t,l,{encoding:"utf8"}))}async function xt(t,e){let n="";try{n=await S(t,"utf8")}catch{return}const r=J(e);if(!r.test(n))return;const i=n.replace(r,"").replace(/^\n+/,"");await $(t,i,{encoding:"utf8"})}function Pt(){return{platform:"linux",kind:"bare-daemon",async install({serviceID:t,nodePath:e,cliPath:n,configDir:r,rootDir:i,stdoutPath:a,stderrPath:l,homeDir:o=d.homedir()}){const s=x(t,o);await k(c.dirname(s),{recursive:!0});const u=W(i,a,l,o),m={schema_version:1,service_id:t,node_path:e,cli_path:n,config_dir:r??"",root_dir:u,stdout_path:a,stderr_path:l,installed_at:Date.now()};await $(s,`${JSON.stringify(m,null,2)}
103
+ `,{encoding:"utf8",mode:384});const p=yt({nodePath:e,cliPath:n,configDir:r??"",rootDir:u});return await St(z(o),t,p),{definitionPath:s}},async start({serviceID:t,homeDir:e=d.homedir()}){const n=x(t,e),r=E(n);if(!r)throw new Error(`bare-daemon marker missing: ${n}`);const i=[r.cli_path];r.config_dir&&i.push("--config-dir",r.config_dir),_(r.node_path,i,{env:{...process.env,[C]:r.root_dir}})},async stop({serviceID:t,homeDir:e=d.homedir()}){const n=x(t,e),r=E(n);if(!r)return;const i=O(r.root_dir);if(i>0&&I(i))try{process.kill(i,"SIGTERM")}catch{}},async restart(t){await this.stop(t);const e=Date.now()+5e3,n=x(t.serviceID,t.homeDir??d.homedir()),r=E(n);for(;r&&Date.now()<e;){const i=O(r.root_dir);if(i<=0||!I(i))break;await new Promise(a=>setTimeout(a,100))}await this.start(t)},async uninstall({serviceID:t,homeDir:e=d.homedir()}){const n=x(t,e),r=E(n);if(r){const i=O(r.root_dir);if(i>0&&I(i))try{process.kill(i,"SIGTERM")}catch{}}await y(n,{force:!0}),await xt(z(e),t)},async discoverServices({homeDir:t=d.homedir()}={}){const e=c.join(t,".grix","service"),n=await F(e).catch(()=>[]),r=D("linux"),i=[];for(const a of n){if(!a.startsWith(r)||!a.endsWith(".bare.json"))continue;const l=a.slice(0,-10),o=c.join(e,a),s=E(o);i.push({serviceID:l,definitionPath:o,configDir:s?.config_dir||null})}return i},async isServiceLoaded({serviceID:t,homeDir:e=d.homedir()}){const n=x(t,e),r=E(n);if(!r)return!1;const i=O(r.root_dir);return i>0&&I(i)}}}function Et(t=process.getuid?.()??0){if(process.env.GRIX_FORCE_BARE_DAEMON==="1")return!1;const e=process.env.XDG_RUNTIME_DIR;return!!(e&&T(c.join(e,"bus"))||T(`/run/user/${t}/bus`))}function Dt(t=process.platform,e={}){return t==="darwin"?ft():t==="win32"?ht():e.systemdUserAvailable??Et()?pt():Pt()}export{dt as WINDOWS_WRAPPER_TEMPLATE_MARKER,ut as WINDOWS_WRAPPER_TEMPLATE_VERSION,mt as buildWindowsWrapperScript,Dt as getPlatformServiceAdapter,Et as isSystemdUserBusAvailable,xt as removeProfileAutostart,P as resolveWindowsWrapperPath,St as upsertProfileAutostart};
@@ -1 +1 @@
1
- import{execFileSync as E,execSync as m,spawn as S}from"node:child_process";import o from"node:process";import{setTimeout as I}from"node:timers/promises";let d;function R(){if(o.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={}){S(e,t,{detached:!0,stdio:"ignore",windowsHide:!0,env:n.env??o.env}).unref()}function w(e,t,n={}){const{cwd:r,env:u=o.env,allowFailure:a=!1}=n;return new Promise((c,i)=>{const l=S(e,t,{cwd:r||void 0,env:u,stdio:["ignore","pipe","pipe"],windowsHide:!0}),C=[],P=[];l.stdout&&l.stdout.on("data",s=>C.push(s)),l.stderr&&l.stderr.on("data",s=>P.push(s)),l.on("error",s=>{if(a){c({exitCode:-1,stdout:"",stderr:s instanceof Error?s.message:String(s)});return}i(s)}),l.on("close",s=>{const f=s??0,y=Buffer.concat(C).toString("utf8"),x=Buffer.concat(P).toString("utf8");if(f!==0&&!a){const v=x.trim()||y.trim()||`${e} exited with code ${f}`;i(new Error(v));return}c({exitCode:f,stdout:y,stderr:x})})})}function p(e){if(e<=0)return!1;try{return o.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 r=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(r)return r}catch{}try{const t=`${o.env.SystemRoot||"C:\\Windows"}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`,r=E(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 r||null}catch{return null}}function T(e){if(e<=0||!Number.isInteger(e))return null;if(o.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 k(e,t={}){const{platform:n=o.platform,runCommandImpl:r=w}=t;if(e<=0)return!1;if(n==="win32")return await r("taskkill",["/PID",String(e),"/T","/F"],{allowFailure:!0}),!0;try{return o.kill(-e,"SIGTERM"),!0}catch{}try{return o.kill(e,"SIGTERM"),!0}catch{return!1}}async function _(e,t={}){const{timeoutMs:n=5e3,intervalMs:r=100}=t;if(e<=0)return!0;const u=Date.now()+n;for(;Date.now()<=u;){if(!p(e))return!0;await I(r)}return!p(e)}function W(){return`${o.env.SystemRoot||"C:\\Windows"}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`}function g(e){return e.split(/\r?\n/).map(t=>parseInt(t.trim(),10)).filter(t=>t>0&&!Number.isNaN(t))}async function M(e){const t=w;if(o.platform==="win32"){const r=await t("wmic",["process","where",`CommandLine Like '%${e}%'`,"get","ProcessId"],{allowFailure:!0});if(r.exitCode===0)return g(r.stdout);const u=e.replace(/'/g,"''"),a=await t(W(),["-NoProfile","-NonInteractive","-WindowStyle","Hidden","-Command",`Get-CimInstance Win32_Process | Where-Object { $_.CommandLine -like '*${u}*' } | Select-Object -ExpandProperty ProcessId`],{allowFailure:!0});return a.exitCode!==0?[]:g(a.stdout)}const n=await t("pgrep",["-f",e],{allowFailure:!0});return n.exitCode!==0?[]:g(n.stdout)}async function G(e,t={}){const{exceptPid:n=0,platform:r=o.platform}=t,u=await M(e),a=new Set([o.pid,o.ppid,n]),c=u.filter(i=>!a.has(i));if(c.length===0)return 0;if(r==="win32")for(const i of c)await k(i,{platform:"win32",runCommandImpl:w});else{for(const i of c)try{o.kill(i,"SIGTERM")}catch{}await I(500);for(const i of c)if(p(i))try{o.kill(i,"SIGKILL")}catch{}}return c.length}export{T as getProcessCommandLine,p as isProcessRunning,R as isWindowsElevated,G as killProcessesByCommandLine,w as runCommand,H as spawnDetached,k as terminateProcessTree,_ as waitForProcessExit};
1
+ import{execFileSync as v,execSync as m,spawn as y}from"node:child_process";import n from"node:process";import{setTimeout as I}from"node:timers/promises";let d;function F(e=n.platform,t=n.env){return e==="win32"&&t.GRIX_WRAPPER_SUPERVISED!=="1"}function _(){if(n.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,r={}){y(e,t,{detached:!0,stdio:"ignore",windowsHide:!0,env:r.env??n.env}).unref()}function w(e,t,r={}){const{cwd:o,env:u=n.env,allowFailure:a=!1}=r;return new Promise((c,i)=>{const l=y(e,t,{cwd:o||void 0,env:u,stdio:["ignore","pipe","pipe"],windowsHide:!0}),g=[],C=[];l.stdout&&l.stdout.on("data",s=>g.push(s)),l.stderr&&l.stderr.on("data",s=>C.push(s)),l.on("error",s=>{if(a){c({exitCode:-1,stdout:"",stderr:s instanceof Error?s.message:String(s)});return}i(s)}),l.on("close",s=>{const f=s??0,S=Buffer.concat(g).toString("utf8"),x=Buffer.concat(C).toString("utf8");if(f!==0&&!a){const E=x.trim()||S.trim()||`${e} exited with code ${f}`;i(new Error(E));return}c({exitCode:f,stdout:S,stderr:x})})})}function p(e){if(e<=0)return!1;try{return n.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=`${n.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(n.platform==="win32")return L(e);try{const r=m(`ps -ww -o command= -p ${e}`,{encoding:"utf8",stdio:["ignore","pipe","ignore"],timeout:h}).trim();return r||null}catch{return null}}async function W(e,t={}){const{platform:r=n.platform,runCommandImpl:o=w}=t;if(e<=0)return!1;if(r==="win32")return await o("taskkill",["/PID",String(e),"/T","/F"],{allowFailure:!0}),!0;try{return n.kill(-e,"SIGTERM"),!0}catch{}try{return n.kill(e,"SIGTERM"),!0}catch{return!1}}async function G(e,t={}){const{timeoutMs:r=5e3,intervalMs:o=100}=t;if(e<=0)return!0;const u=Date.now()+r;for(;Date.now()<=u;){if(!p(e))return!0;await I(o)}return!p(e)}function R(){return`${n.env.SystemRoot||"C:\\Windows"}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`}function P(e){return e.split(/\r?\n/).map(t=>parseInt(t.trim(),10)).filter(t=>t>0&&!Number.isNaN(t))}async function k(e){const t=w;if(n.platform==="win32"){const o=await t("wmic",["process","where",`CommandLine Like '%${e}%'`,"get","ProcessId"],{allowFailure:!0});if(o.exitCode===0)return P(o.stdout);const u=e.replace(/'/g,"''"),a=await t(R(),["-NoProfile","-NonInteractive","-WindowStyle","Hidden","-Command",`Get-CimInstance Win32_Process | Where-Object { $_.CommandLine -like '*${u}*' } | Select-Object -ExpandProperty ProcessId`],{allowFailure:!0});return a.exitCode!==0?[]:P(a.stdout)}const r=await t("pgrep",["-f",e],{allowFailure:!0});return r.exitCode!==0?[]:P(r.stdout)}async function D(e,t={}){const{exceptPid:r=0,platform:o=n.platform}=t,u=await k(e),a=new Set([n.pid,n.ppid,r]),c=u.filter(i=>!a.has(i));if(c.length===0)return 0;if(o==="win32")for(const i of c)await W(i,{platform:"win32",runCommandImpl:w});else{for(const i of c)try{n.kill(i,"SIGTERM")}catch{}await I(500);for(const i of c)if(p(i))try{n.kill(i,"SIGKILL")}catch{}}return c.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};