grix-connector 2.0.1 → 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- import{createServer as g}from"node:http";import{log as c}from"../log/logger.js";class m{server=null;token;handler=null;upgradeHandler=null;probeHandler=null;installHandler=null;constructor(e){this.token=e}setAgentHandler(e){this.handler=e}setUpgradeHandler(e){this.upgradeHandler=e}setProbeHandler(e){this.probeHandler=e}setInstallHandler(e){this.installHandler=e}async start(e){return new Promise((n,t)=>{this.server=g((r,s)=>this.handleRequest(r,s)),this.server.listen(e,"127.0.0.1",()=>{c.info("admin",`Listening on 127.0.0.1:${e}`),n()}),this.server.on("error",t)})}async stop(){if(this.server)return new Promise(e=>{this.server.close(()=>e())})}handleRequest(e,n){const t=e.method??"",r=e.url??"";if(r==="/api/agents"&&t==="GET")this.handleList(n);else if(r==="/api/agents"&&t==="POST")this.readBody(e).then(s=>this.handleAdd(n,s)).catch(s=>this.error(n,s));else if(t==="DELETE"&&r.startsWith("/api/agents/")){const s=decodeURIComponent(r.slice(12));this.handleRemove(n,s)}else if(t==="POST"&&r.match(/^\/api\/agents\/[^/]+\/restart$/)){const s=decodeURIComponent(r.slice(12,r.lastIndexOf("/restart")));this.handleRestart(n,s)}else if(r==="/api/upgrade"&&t==="GET")this.handleCheckUpgrade(n);else if(r==="/api/upgrade"&&t==="POST")this.handleTriggerUpgrade(n);else if(t==="GET"&&r.startsWith("/api/probe"))this.handleProbe(e,n,r);else if(r==="/api/install"&&t==="GET")this.handleInstallList(n);else if(r==="/api/install"&&t==="POST")this.readBody(e).then(s=>this.handleInstall(n,s)).catch(s=>this.error(n,s));else if(t==="GET"&&r.startsWith("/api/install/")){const s=decodeURIComponent(r.slice(13));this.handleInstallProgress(n,s)}else this.json(n,404,{error:"not_found"})}handleList(e){try{const n=this.handler?.list()??[];this.json(e,200,n)}catch(n){this.error(e,n)}}async handleAdd(e,n){try{const t=await this.handler.add(n);this.json(e,201,t??{ok:!0})}catch(t){this.error(e,t)}}handleRemove(e,n){this.handler.remove(n).then(()=>{e.writeHead(204),e.end()}).catch(t=>this.error(e,t))}handleRestart(e,n){this.handler.restart(n).then(()=>{this.json(e,200,{ok:!0})}).catch(t=>this.error(e,t))}handleCheckUpgrade(e){if(!this.upgradeHandler){this.json(e,501,{error:"upgrade not configured"});return}this.upgradeHandler.check().then(n=>{this.json(e,200,n)}).catch(n=>this.error(e,n))}handleTriggerUpgrade(e){if(!this.upgradeHandler){this.json(e,501,{error:"upgrade not configured"});return}this.upgradeHandler.trigger(),this.json(e,200,{ok:!0,message:"upgrade check triggered"})}error(e,n){const t=n;t.code==="NOT_FOUND"?this.json(e,404,{error:t.message??"not found"}):t.code==="UNKNOWN_AGENT"||t.code==="UNSUPPORTED_OS"?this.json(e,400,{error:t.message,code:t.code}):t.code==="ALREADY_INSTALLED"||t.code==="INSTALL_IN_PROGRESS"?this.json(e,409,{error:t.message,code:t.code}):t.code==="INSTALL_FAILED"||t.code==="INSTALL_TIMEOUT"||t.code==="PREFLIGHT_FAILED"||t.code==="VERIFICATION_FAILED"||t.code==="PREREQ_MISSING"||t.code==="PREREQ_INSTALL_FAILED"||t.code==="FALLBACK_EXHAUSTED"||t.code==="ENVIRONMENT_UNSUPPORTED"?this.json(e,500,{error:t.message,code:t.code}):(c.error("admin",`Handler error: ${t.message??n}`),this.json(e,500,{error:t.message??"internal error"}))}json(e,n,t){const r=JSON.stringify(t);e.writeHead(n,{"Content-Type":"application/json"}),e.end(r)}readBody(e){return new Promise((n,t)=>{let r="";e.setEncoding("utf8"),e.on("data",s=>{r+=s}),e.on("end",()=>{try{n(JSON.parse(r))}catch{t(new Error("invalid JSON body"))}}),e.on("error",t)})}handleProbe(e,n,t){if(!this.probeHandler){this.json(n,501,{error:"probe not configured"});return}const r=t.indexOf("?"),s=r>=0?t.slice(0,r):t,a=r>=0?new URLSearchParams(t.slice(r+1)):new URLSearchParams,i={};a.get("conversation")==="true"&&(i.conversation=!0),a.get("fresh")==="true"&&(i.fresh=!0);const l=Number(a.get("timeoutMs"));Number.isFinite(l)&&l>0&&(i.timeoutMs=l);const d=s.match(/^\/api\/probe\/(.+)$/);if(d){const o=decodeURIComponent(d[1]);this.probeHandler.probeOne(o,i).then(h=>{this.json(n,200,h)}).catch(h=>this.error(n,h));return}this.probeHandler.probeAll(i).then(o=>{this.json(n,200,o)}).catch(o=>this.error(n,o))}handleInstallList(e){if(!this.installHandler){this.json(e,501,{error:"install not configured"});return}try{const n=this.installHandler.listInstallable();this.json(e,200,n)}catch(n){this.error(e,n)}}handleInstallProgress(e,n){if(!this.installHandler){this.json(e,501,{error:"install not configured"});return}const t=this.installHandler.getInstallProgress(n);this.json(e,200,{agentType:n,inProgress:t!=null,progress:t})}async handleInstall(e,n){if(!this.installHandler){this.json(e,501,{error:"install not configured"});return}try{const t=n;if(!t||typeof t.agentType!="string"||!t.agentType){this.json(e,400,{error:"agentType is required"});return}const r=await this.installHandler.installAgent(t);if(r.ok)this.json(e,200,r);else{const s=r.error?.code;s==="UNKNOWN_AGENT"||s==="UNSUPPORTED_OS"?this.json(e,400,r):s==="INSTALL_IN_PROGRESS"?this.json(e,409,r):this.json(e,500,r)}}catch(t){this.error(e,t)}}}export{m as AdminServer};
1
+ import{createServer as g}from"node:http";import{log as c}from"../log/logger.js";class m{server=null;token;handler=null;upgradeHandler=null;probeHandler=null;installHandler=null;constructor(e){this.token=e}setAgentHandler(e){this.handler=e}setUpgradeHandler(e){this.upgradeHandler=e}setProbeHandler(e){this.probeHandler=e}setInstallHandler(e){this.installHandler=e}async start(e){return new Promise((r,t)=>{this.server=g((n,s)=>this.handleRequest(n,s)),this.server.listen(e,"127.0.0.1",()=>{c.info("admin",`Listening on 127.0.0.1:${e}`),r()}),this.server.on("error",t)})}async stop(){if(this.server)return new Promise(e=>{this.server.close(()=>e())})}handleRequest(e,r){const t=e.method??"",n=e.url??"";if(n==="/api/agents"&&t==="GET")this.handleList(r);else if(n==="/api/agents"&&t==="POST")this.readBody(e).then(s=>this.handleAdd(r,s)).catch(s=>this.error(r,s));else if(t==="DELETE"&&n.startsWith("/api/agents/")){const s=decodeURIComponent(n.slice(12));this.handleRemove(r,s)}else if(t==="POST"&&n.match(/^\/api\/agents\/[^/]+\/restart$/)){const s=decodeURIComponent(n.slice(12,n.lastIndexOf("/restart")));this.handleRestart(r,s)}else if(n==="/api/upgrade"&&t==="GET")this.handleCheckUpgrade(r);else if(n==="/api/upgrade"&&t==="POST")this.handleTriggerUpgrade(r);else if(t==="GET"&&n.startsWith("/api/probe"))this.handleProbe(e,r,n);else if(n==="/api/install"&&t==="GET")this.handleInstallList(r);else if(n==="/api/install"&&t==="POST")this.readBody(e).then(s=>this.handleInstall(r,s)).catch(s=>this.error(r,s));else if(t==="GET"&&n.startsWith("/api/install/")){const s=decodeURIComponent(n.slice(13));this.handleInstallProgress(r,s)}else this.json(r,404,{error:"not_found"})}handleList(e){try{const r=this.handler?.list()??[];this.json(e,200,r)}catch(r){this.error(e,r)}}async handleAdd(e,r){try{const t=await this.handler.add(r);this.json(e,201,t??{ok:!0})}catch(t){this.error(e,t)}}handleRemove(e,r){this.handler.remove(r).then(()=>{e.writeHead(204),e.end()}).catch(t=>this.error(e,t))}handleRestart(e,r){this.handler.restart(r).then(()=>{this.json(e,200,{ok:!0})}).catch(t=>this.error(e,t))}handleCheckUpgrade(e){if(!this.upgradeHandler){this.json(e,501,{error:"upgrade not configured"});return}this.upgradeHandler.check().then(r=>{this.json(e,200,r)}).catch(r=>this.error(e,r))}handleTriggerUpgrade(e){if(!this.upgradeHandler){this.json(e,501,{error:"upgrade not configured"});return}this.upgradeHandler.trigger(),this.json(e,200,{ok:!0,message:"upgrade check triggered"})}error(e,r){const t=r;t.code==="NOT_FOUND"?this.json(e,404,{error:t.message??"not found"}):t.code==="UNKNOWN_AGENT"||t.code==="UNSUPPORTED_OS"?this.json(e,400,{error:t.message,code:t.code}):t.code==="ALREADY_INSTALLED"||t.code==="INSTALL_IN_PROGRESS"?this.json(e,409,{error:t.message,code:t.code}):t.code==="INSTALL_FAILED"||t.code==="INSTALL_TIMEOUT"||t.code==="PREFLIGHT_FAILED"||t.code==="VERIFICATION_FAILED"||t.code==="PREREQ_MISSING"||t.code==="PREREQ_INSTALL_FAILED"||t.code==="FALLBACK_EXHAUSTED"||t.code==="ENVIRONMENT_UNSUPPORTED"?this.json(e,500,{error:t.message,code:t.code}):(c.error("admin",`Handler error: ${t.message??r}`),this.json(e,500,{error:t.message??"internal error"}))}json(e,r,t){const n=JSON.stringify(t);e.writeHead(r,{"Content-Type":"application/json"}),e.end(n)}readBody(e){return new Promise((r,t)=>{let n="";e.setEncoding("utf8"),e.on("data",s=>{n+=s}),e.on("end",()=>{try{r(JSON.parse(n))}catch{t(new Error("invalid JSON body"))}}),e.on("error",t)})}handleProbe(e,r,t){if(!this.probeHandler){this.json(r,501,{error:"probe not configured"});return}const n=t.indexOf("?"),s=n>=0?t.slice(0,n):t,a=n>=0?new URLSearchParams(t.slice(n+1)):new URLSearchParams,i={};a.get("conversation")==="true"&&(i.conversation=!0),a.get("fresh")==="true"&&(i.fresh=!0);const l=Number(a.get("timeoutMs"));Number.isFinite(l)&&l>0&&(i.timeoutMs=l);const d=s.match(/^\/api\/probe\/(.+)$/);if(d){const o=decodeURIComponent(d[1]);this.probeHandler.probeOne(o,i).then(h=>{this.json(r,200,h)}).catch(h=>this.error(r,h));return}this.probeHandler.probeAll(i).then(o=>{this.json(r,200,o)}).catch(o=>this.error(r,o))}handleInstallList(e){if(!this.installHandler){this.json(e,501,{error:"install not configured"});return}try{const r=this.installHandler.listInstallable();this.json(e,200,r)}catch(r){this.error(e,r)}}handleInstallProgress(e,r){if(!this.installHandler){this.json(e,501,{error:"install not configured"});return}const t=this.installHandler.getInstallProgress(r);if(!t){this.json(e,200,{agentType:r,status:"unknown",inProgress:!1,progress:null});return}let n,s,a;switch(t.phase){case"completed":n="done",s="\u5B89\u88C5\u5B8C\u6210";break;case"failed":n="error",a=t.outputTail||"\u5B89\u88C5\u5931\u8D25";break;case"preflight":n="pending",s="\u68C0\u67E5\u524D\u7F6E\u4F9D\u8D56...";break;case"installing_prereq":n="downloading",s=t.currentPrereq?`\u6B63\u5728\u5B89\u88C5\u524D\u7F6E\u4F9D\u8D56: ${t.currentPrereq}`:"\u6B63\u5728\u5B89\u88C5\u524D\u7F6E\u4F9D\u8D56...";break;case"installing":n="installing",s=`\u6B63\u5728\u5B89\u88C5 ${r}...`;break;case"verifying":n="installing",s="\u9A8C\u8BC1\u5B89\u88C5...";break;default:n="unknown"}this.json(e,200,{agentType:r,status:n,inProgress:!0,progress:t.elapsedMs?Math.min(.9,t.elapsedMs/3e4):.1,message:s,error:a})}async handleInstall(e,r){if(!this.installHandler){this.json(e,501,{error:"install not configured"});return}try{const t=r;if(!t||typeof t.agentType!="string"||!t.agentType){this.json(e,400,{error:"agentType is required"});return}const n=await this.installHandler.installAgent(t);if(n.ok)this.json(e,200,n);else{const s=n.error?.code;s==="UNKNOWN_AGENT"||s==="UNSUPPORTED_OS"?this.json(e,400,n):s==="INSTALL_IN_PROGRESS"?this.json(e,409,n):this.json(e,500,n)}}catch(t){this.error(e,t)}}}export{m as AdminServer};
@@ -1,2 +1,2 @@
1
- import{EventEmitter as g}from"node:events";import{randomUUID as k}from"node:crypto";import f from"node:os";import _ from"ws";import{log as o}from"../log/index.js";import{detectTailnetIPv4 as p,ensureServerAndGetPort as v}from"../files/file-serve.js";function $(m){return m.replace(/(?<=[\[:,\[]\s*)(\d{16,})(?=\s*[,}\]\n])/g,'"$1"')}function b(m){const e=[...m??["stream_chunk","local_action_v1","agent_invoke"]];return e.includes("agent_invoke")||e.push("agent_invoke"),e.includes("event_result_ack")||e.push("event_result_ack"),e}const w="aibot-agent-api-v1",q=1;class h extends g{static DROPPABLE_COMMANDS=new Set(["update_binding_card"]);static BUFFER_OVERFLOW_RETAIN_COMMANDS=new Set(["event_result","codex_event","client_stream_chunk"]);static MAX_OUTBOUND_BUFFER_SIZE=1e3;static BACKPRESSURE_THRESHOLD=64*1024;ws=null;seq=0;heartbeatTimer=null;heartbeatSec=30;connected=!1;reconnecting=!1;reconnectAttempts=0;everConnected=!1;config;packetLog;pendingInvokes=new Map;seqEventMap=new Map;pendingRequests=new Map;outboundBuffer=[];ackPolicy=null;constructor(e,t){super(),this.packetLog=t?.packetLog??null,this.config={url:e.url,agentId:e.agentId,apiKey:e.apiKey,clientType:e.clientType,clientVersion:e.clientVersion??"",adapterHint:e.adapterHint??"",capabilities:b(e.capabilities),localActions:e.localActions??["exec_approve","exec_reject"],skills:e.skills}}get isConnected(){return this.connected}async connect(){const e=await p();let t;if(e!==void 0)try{t=await v(e)}catch(n){o.warn("aibot",`file server pre-start failed: ${n}`)}return new Promise((n,c)=>{const r=new _(this.config.url);this.ws=r;const u=setTimeout(()=>{c(new Error("Auth timeout: no auth_ack received within 15s")),this.cleanupSocket()},15e3),a=++this.seq,d=setTimeout(()=>{this.pendingRequests.delete(a),c(new Error("Auth request timeout")),this.cleanupSocket()},15e3);this.pendingRequests.set(a,{expected:["auth_ack"],resolve:i=>{clearTimeout(u);const s=i.payload;s.code===0?(this.connected=!0,this.everConnected=!0,this.reconnectAttempts=0,s.heartbeat_sec&&(this.heartbeatSec=s.heartbeat_sec),s.ack_policy&&(this.ackPolicy=s.ack_policy,o.info("aibot",`ack_policy received: push_ack_timeout_ms=${s.ack_policy.push_ack_timeout_ms??"default"} max_retries=${s.ack_policy.max_retries??"default"} timeout_action=${s.ack_policy.timeout_action??"default"}`)),this.startHeartbeat(),this.flushOutboundBuffer(),this.emit("auth",s),n(s)):c(new Error(`Auth failed: code=${s.code} msg=${s.msg}`))},reject:i=>{clearTimeout(u),c(i)},timer:d}),r.on("open",()=>{const i={agent_id:this.config.agentId,api_key:this.config.apiKey,client_type:this.config.clientType,protocol_version:w,contract_version:q,capabilities:this.config.capabilities??[],local_actions:this.config.localActions,skills:this.config.skills};this.config.clientVersion&&(i.client="grix-connector",i.client_version=this.config.clientVersion,i.host_type=this.config.clientType,i.host_version=this.config.clientVersion),this.config.adapterHint&&(i.adapter_hint=this.config.adapterHint),i.host_meta={hostname:f.hostname(),platform:f.platform(),arch:f.arch(),os_release:f.release(),...e!==void 0&&{tailnet_ip:e},...t!==void 0&&t>0&&{file_server_port:t}},this.config.concurrency&&(i.concurrency=this.config.concurrency),this.sendPacket("auth",i,a)}),r.on("message",i=>{let s;try{s=JSON.parse($(i.toString()))}catch{return}try{this.handlePacket(s)}catch(l){this.emitClientError(new Error(`handlePacket error: ${l}`))}}),r.on("close",(i,s)=>{this.connected=!1,this.stopHeartbeat(),this.rejectAllPendingRequests("websocket closed"),this.emit("close",i,s.toString());const l=i!==1e3&&this.everConnected;o.info("aibot",`ws closed agent=${this.config.clientType}:${this.config.agentId} code=${i} reason=${s.toString()||"<none>"} everConnected=${this.everConnected} reconnecting=${this.reconnecting} willReconnect=${l}`),l&&this.attemptReconnect()}),r.on("error",i=>{this.emitClientError(i instanceof Error?i:new Error(String(i))),this.connected||c(i)})})}handlePacket(e){if(this.packetLog?.logInboundPacket(e.cmd,e.seq,e.payload),e.seq>0&&this.pendingRequests.has(e.seq)){const t=this.pendingRequests.get(e.seq);this.pendingRequests.delete(e.seq),clearTimeout(t.timer),t.expected.includes(e.cmd)?t.resolve(e):t.reject(new Error(`unexpected response: got ${e.cmd}, expected ${t.expected.join("/")}`));return}switch(e.cmd){case"auth_ack":break;case"ping":{this.sendPacket("pong",e.payload??{});break}case"event_msg":{this.emit("event",e.payload);break}case"local_action":{this.emit("localAction",e.payload);break}case"event_stop":{this.emit("stop",e.payload);break}case"event_revoke":{this.emit("revoke",e.payload);break}case"event_edit":{this.emit("edit",e.payload);break}case"event_cancel":{this.emit("eventCancel",e.payload);break}case"queue_clear":{this.emit("queueClear",e.payload);break}case"queue_snapshot_query":{this.emit("queueSnapshotQuery",e.payload);break}case"kicked":{if(this.emit("kicked",e.payload),this.connected=!1,this.stopHeartbeat(),this.rejectAllPendingRequests("kicked"),this.outboundBuffer.length=0,this.reconnectAttempts=Math.max(this.reconnectAttempts,3),this.ws){try{this.ws.close(4001,"kicked")}catch{}this.ws=null}break}case"error":{const t=e.payload,n=[t.ref_cmd?`ref_cmd=${t.ref_cmd}`:"",t.ref_id?`ref_id=${t.ref_id}`:""].filter(Boolean).join(" ");this.emitClientError(new Error(`Server error: code=${t.code} msg=${t.msg}${n?` ${n}`:""}`));break}case"agent_invoke_result":{this.handleInvokeResult(e.payload);break}case"mcp_frame":{const t=e.payload;this.emit("mcpFrame",t.session_id??"",t.frame??null);break}case"send_ack":break;case"send_nack":{const t=e.payload;if(t.code===4003&&e.seq>0){const n=this.seqEventMap.get(e.seq);n&&(this.seqEventMap.delete(e.seq),this.purgeBufferedStreamChunks(n),o.warn("aibot",`stream chunk rejected (4003), purging buffered chunks for event=${n}`),this.emit("streamRejected",n,t.code))}break}case"local_action_ack":break;default:break}}sendEventAck(e){this.sendPacket("event_ack",e)||o.warn("aibot",`event_ack NOT sent (ws not open) event=${e.event_id} ws=${this.ws?`state=${this.ws.readyState}`:"null"}`)}sendStreamChunk(e){!e.delta_content&&!e.is_finish&&(o.warn("aibot",`stream_chunk delta_content empty, patched to newline event=${e.event_id??""} session=${e.session_id} chunk_seq=${e.chunk_seq} is_finish=${e.is_finish}`),e={...e,delta_content:`
2
- `}),this.sendPacket("client_stream_chunk",e)}sendMsg(e){this.sendPacket("send_msg",e)||o.warn("aibot",`send_msg NOT sent (ws not open) event=${e.event_id??""} session=${e.session_id??""} ws=${this.ws?`state=${this.ws.readyState}`:"null"}`)}editMsg(e){this.sendPacket("edit_msg",e)}sendEventResult(e){if(!this.ws||this.ws.readyState!==_.OPEN){this.sendPacket("event_result",e);return}this.sendEventResultReliable(e)}sendLocalActionResult(e){this.sendPacket("local_action_result",e)}sendEventStopAck(e){this.sendPacket("event_stop_ack",e)}sendEventStopResult(e){this.sendPacket("event_stop_result",e)}sendSessionActivitySet(e){this.sendPacket("session_activity_set",e)}sendCodexEvent(e){this.sendPacket("codex_event",e)}sendUpdateBindingCard(e){this.sendPacket("update_binding_card",e)}sendSkillsUpdate(e){this.sendPacket("agent_skills_update",e)}sendPing(){this.sendPacket("ping",{})}sendEventState(e){this.sendPacket("event_state",e)}sendEventCancelResult(e){this.sendPacket("event_cancel_result",e)}sendQueueClearResult(e){this.sendPacket("queue_clear_result",e)}sendQueueSnapshot(e){this.sendPacket("queue_snapshot",e)}agentInvoke(e,t,n=15e3){return new Promise((c,r)=>{const u=k(),a=Math.max(1e3,Math.min(n,6e4)),d=setTimeout(()=>{this.pendingInvokes.delete(u),r(new Error(`agent_invoke timeout: ${e}`))},a);this.pendingInvokes.set(u,{resolve:c,reject:r,timer:d}),this.sendPacket("agent_invoke",{invoke_id:u,action:e,params:t,timeout_ms:a})})}sendMcpFrame(e,t){this.sendPacket("mcp_frame",{session_id:e,frame:t})}request(e,t,n){return new Promise((c,r)=>{const u=++this.seq,a=setTimeout(()=>{this.pendingRequests.delete(u),r(new Error(`request timeout: ${e} (expected ${n.expected.join("/")})`))},n.timeoutMs);this.pendingRequests.set(u,{expected:n.expected,resolve:c,reject:r,timer:a}),this.sendPacket(e,t,u)||(this.pendingRequests.delete(u),clearTimeout(a),r(new Error(`send failed: ${e}`)))})}async sendStreamChunkRequest(e,t=2e4){return this.request("client_stream_chunk",e,{expected:["send_ack","send_nack","error"],timeoutMs:t})}async sendText(e,t=2e4){return this.request("send_msg",{msg_type:1,...e},{expected:["send_ack","send_nack","error"],timeoutMs:t})}async sendMedia(e,t=2e4){return this.request("send_msg",{...e,msg_type:2},{expected:["send_ack","send_nack","error"],timeoutMs:t})}async editMessage(e,t=2e4){return this.request("edit_msg",e,{expected:["send_ack","send_nack","error"],timeoutMs:t})}async deleteMessage(e,t,n=2e4){return this.request("delete_msg",{session_id:e,msg_id:t},{expected:["send_ack","send_nack","error"],timeoutMs:n})}async sendEventResultRequest(e,t=5e3){return this.request("event_result",e,{expected:["send_ack","send_nack","error"],timeoutMs:t})}disconnect(){o.info("aibot",`disconnect() agent=${this.config.clientType}:${this.config.agentId} wasConnected=${this.connected} reconnecting=${this.reconnecting} reconnectAttempts=${this.reconnectAttempts}`),this.connected=!1,this.everConnected=!1,this.reconnecting=!1,this.reconnectAttempts=0,this.stopHeartbeat(),this.rejectAllPendingInvokes("disconnect"),this.rejectAllPendingRequests("disconnect"),this.outboundBuffer.length=0,this.ws&&(this.ws.close(1e3,"client disconnect"),this.ws=null)}async attemptReconnect(){if(!this.reconnecting)for(this.reconnecting=!0,o.info("aibot",`attemptReconnect start agent=${this.config.clientType}:${this.config.agentId} fromAttempts=${this.reconnectAttempts}`),this.emit("disconnected");this.reconnecting;){const e=Math.min(1e3*2**this.reconnectAttempts,3e4),t=Math.floor(e*.2*Math.random());if(this.reconnectAttempts++,await new Promise(n=>setTimeout(n,e+t)),!this.reconnecting)return;try{const n=await this.connect(),c=this.reconnectAttempts;this.reconnectAttempts=0,this.reconnecting=!1,o.info("aibot",`reconnect succeeded agent=${this.config.clientType}:${this.config.agentId} attempt=${c}`),this.emit("auth",n);return}catch(n){o.warn("aibot",`reconnect failed agent=${this.config.clientType}:${this.config.agentId} attempt=${this.reconnectAttempts} err=${n instanceof Error?n.message:n}`)}}}sendPacket(e,t,n){if(this.ws&&this.ws.readyState===_.OPEN){const c=this.ws.bufferedAmount>h.BACKPRESSURE_THRESHOLD;if(!c||!h.DROPPABLE_COMMANDS.has(e)){if(c&&h.DROPPABLE_COMMANDS.has(e))return!1;const r=n??++this.seq;if(e==="client_stream_chunk"&&t&&typeof t=="object"){const a=t.event_id;if(a&&(this.seqEventMap.set(r,a),this.seqEventMap.size>200)){const d=this.seqEventMap.keys().next().value;d!==void 0&&this.seqEventMap.delete(d)}}const u={cmd:e,seq:r,payload:t};this.packetLog?.logOutboundPacket(e,r,t,"sent");try{const a=this.ws.readyState,d=this.ws.bufferedAmount;return this.ws.send(JSON.stringify(u),i=>{if(e==="event_result"){const s=t;i?o.warn("aibot",`event_result ws send callback failed event=${s.event_id??""} status=${s.status??""} seq=${r} readyState=${a} bufferedAmount=${d} err=${i.message}`):o.info("aibot",`event_result ws send callback ok event=${s.event_id??""} status=${s.status??""} seq=${r} readyState=${a} bufferedAmount=${d}`)}else if(e==="client_stream_chunk"){const s=t;i?o.warn("aibot",`stream_chunk ws send failed event=${s.event_id??""} session=${s.session_id??""} seq=${r} chunk_seq=${s.chunk_seq??""} is_finish=${s.is_finish??""} readyState=${a} bufferedAmount=${d} err=${i.message}`):o.info("aibot",`stream_chunk ws send ok event=${s.event_id??""} session=${s.session_id??""} seq=${r} chunk_seq=${s.chunk_seq??""} is_finish=${s.is_finish??""} readyState=${a} bufferedAmount=${d}`)}else if(e==="event_ack"){const s=t;i?o.warn("aibot",`event_ack ws send failed event=${s.event_id??""} seq=${r} readyState=${a} bufferedAmount=${d} err=${i.message}`):o.info("aibot",`event_ack ws send ok event=${s.event_id??""} seq=${r} readyState=${a} bufferedAmount=${d}`)}else if(e==="send_msg"){const s=t;i?o.warn("aibot",`send_msg ws send failed event=${s.event_id??""} session=${s.session_id??""} seq=${r} readyState=${a} bufferedAmount=${d} err=${i.message}`):o.info("aibot",`send_msg ws send ok event=${s.event_id??""} session=${s.session_id??""} seq=${r} readyState=${a} bufferedAmount=${d}`)}else if(i){const s=t;o.warn("aibot",`${e} ws send failed seq=${r} session=${s.session_id??""} event=${s.event_id??""} client_msg_id=${s.client_msg_id??""} readyState=${a} bufferedAmount=${d} err=${i.message}`)}}),!0}catch(a){return this.emitClientError(new Error(`sendPacket failed: ${a}`)),!1}}}if(h.DROPPABLE_COMMANDS.has(e))return this.packetLog?.logOutboundPacket(e,n??0,t,"dropped"),!1;if(n!==void 0)return this.packetLog?.logOutboundPacket(e,n,t,"dropped"),!1;if(this.outboundBuffer.length>=h.MAX_OUTBOUND_BUFFER_SIZE&&(this.outboundBuffer=this.outboundBuffer.filter(c=>h.BUFFER_OVERFLOW_RETAIN_COMMANDS.has(c.cmd)),this.outboundBuffer.length>=h.MAX_OUTBOUND_BUFFER_SIZE&&this.outboundBuffer.shift()),this.outboundBuffer.push({cmd:e,payload:t}),this.packetLog?.logOutboundPacket(e,n??0,t,"buffered"),e==="client_stream_chunk"){const c=t;o.info("aibot",`stream_chunk buffered (ws not open) event=${c.event_id??""} session=${c.session_id??""} chunk_seq=${c.chunk_seq??""} is_finish=${c.is_finish??""} ws=${this.ws?`state=${this.ws.readyState}`:"null"}`)}return!1}async sendEventResultReliable(e){const t=this.ackPolicy?.max_retries??3,n=this.ackPolicy?.push_ack_timeout_ms??5e3,c=750;for(let r=1;r<=t;r++){const u=this.ws?.readyState??-1,a=this.ws?.bufferedAmount??0;o.info("aibot",`event_result send attempt event=${e.event_id} status=${e.status} attempt=${r}/${t} readyState=${u} bufferedAmount=${a}`);try{const d=await this.sendEventResultRequest(e,n);if(d.cmd==="send_ack"){const s=d.payload;o.info("aibot",`event_result ack event=${e.event_id} status=${e.status} attempt=${r}/${t} ack_event=${s.event_id??""} ack_status=${s.status??""}`);return}const i=d.payload;if(o.warn("aibot",`event_result rejected event=${e.event_id} status=${e.status} attempt=${r}/${t} cmd=${d.cmd} code=${i.code??""} msg=${i.msg??""}${i.ref_cmd?` ref_cmd=${i.ref_cmd}`:""}${i.ref_id?` ref_id=${i.ref_id}`:""}`),i.code===4003){o.warn("aibot",`event_result stopping retries: 4003 ownership denied event=${e.event_id}`);return}return}catch(d){const i=d instanceof Error?d.message:String(d);if(o.warn("aibot",`event_result attempt failed event=${e.event_id} status=${e.status} attempt=${r}/${t} err=${i}`),r===t){this.emitClientError(new Error(`event_result ack failed after ${t} attempts: event=${e.event_id} status=${e.status}`));return}await new Promise(s=>setTimeout(s,c*r))}}}purgeBufferedStreamChunks(e){const t=this.outboundBuffer.length;this.outboundBuffer=this.outboundBuffer.filter(n=>n.cmd!=="client_stream_chunk"?!0:n.payload?.event_id!==e),this.outboundBuffer.length<t&&o.info("aibot",`purged ${t-this.outboundBuffer.length} buffered stream chunks for event=${e}`)}emitClientError(e){if(this.listenerCount("error")===0){o.warn("aibot",`Client error (no listeners): ${e.message}`);return}this.emit("error",e)}flushOutboundBuffer(){if(this.outboundBuffer.length===0||!this.ws||this.ws.readyState!==_.OPEN)return;const e=this.outboundBuffer;this.outboundBuffer=[];for(const{cmd:t,payload:n}of e){const c=++this.seq;if(t==="client_stream_chunk"&&n&&typeof n=="object"){const u=n.event_id;u&&this.seqEventMap.set(c,u)}const r={cmd:t,seq:c,payload:n};try{this.ws.send(JSON.stringify(r))}catch{break}}if(this.seqEventMap.size>200){const t=[...this.seqEventMap.entries()].sort((n,c)=>n[0]-c[0]);this.seqEventMap.clear();for(const[n,c]of t.slice(-100))this.seqEventMap.set(n,c)}}handleInvokeResult(e){const t=this.pendingInvokes.get(e.invoke_id);t&&(this.pendingInvokes.delete(e.invoke_id),clearTimeout(t.timer),e.code===0?t.resolve(e.data??null):t.reject(new Error(`agent_invoke error code=${e.code}: ${e.msg??""}`)))}rejectAllPendingInvokes(e){for(const[,t]of this.pendingInvokes)clearTimeout(t.timer),t.reject(new Error(`agent_invoke canceled: ${e}`));this.pendingInvokes.clear()}rejectAllPendingRequests(e){for(const[,t]of this.pendingRequests)clearTimeout(t.timer),t.reject(new Error(`request canceled: ${e}`));this.pendingRequests.clear()}cleanupSocket(){if(this.ws){try{this.ws.close()}catch{}this.ws=null}}startHeartbeat(){this.stopHeartbeat(),this.heartbeatTimer=setInterval(()=>{this.connected&&this.request("ping",{ts:Date.now()},{expected:["pong"],timeoutMs:5e3}).catch(()=>{this.connected&&(this.cleanupSocket(),this.attemptReconnect())})},this.heartbeatSec*1e3)}stopHeartbeat(){this.heartbeatTimer&&(clearInterval(this.heartbeatTimer),this.heartbeatTimer=null)}}export{h as AibotClient};
1
+ import{EventEmitter as k}from"node:events";import{randomUUID as p}from"node:crypto";import _ from"node:os";import m from"ws";import{log as c}from"../log/index.js";import{detectTailnetIPv4 as v,ensureServerAndGetPort as $}from"../files/file-serve.js";function b(g){return g.replace(/(?<=[\[:,\[]\s*)(\d{16,})(?=\s*[,}\]\n])/g,'"$1"')}function w(g){const e=[...g??["stream_chunk","local_action_v1","agent_invoke"]];return e.includes("agent_invoke")||e.push("agent_invoke"),e.includes("event_result_ack")||e.push("event_result_ack"),e}const q="aibot-agent-api-v1",E=1;class l extends k{static DROPPABLE_COMMANDS=new Set(["update_binding_card"]);static BUFFER_OVERFLOW_RETAIN_COMMANDS=new Set(["event_result","codex_event","client_stream_chunk"]);static MAX_OUTBOUND_BUFFER_SIZE=1e3;static BACKPRESSURE_THRESHOLD=64*1024;ws=null;seq=0;heartbeatTimer=null;heartbeatSec=30;connected=!1;reconnecting=!1;reconnectAttempts=0;everConnected=!1;config;packetLog;pendingInvokes=new Map;seqEventMap=new Map;pendingRequests=new Map;outboundBuffer=[];ackPolicy=null;constructor(e,t){super(),this.packetLog=t?.packetLog??null,this.config={url:e.url,agentId:e.agentId,apiKey:e.apiKey,clientType:e.clientType,clientVersion:e.clientVersion??"",adapterHint:e.adapterHint??"",capabilities:w(e.capabilities),localActions:e.localActions??["exec_approve","exec_reject"],skills:e.skills}}get isConnected(){return this.connected}async connect(){let e,t;const n=(async()=>{try{if(e=await v(),e!==void 0)try{t=await $(e)}catch(r){c.warn("aibot",`file server pre-start failed: ${r}`)}}catch(r){c.warn("aibot",`tailnet detect failed: ${r}`)}})();return new Promise((r,i)=>{const u=new m(this.config.url);this.ws=u;const a=setTimeout(()=>{i(new Error("Auth timeout: no auth_ack received within 15s")),this.cleanupSocket()},15e3),o=++this.seq,d=setTimeout(()=>{this.pendingRequests.delete(o),i(new Error("Auth request timeout")),this.cleanupSocket()},15e3);this.pendingRequests.set(o,{expected:["auth_ack"],resolve:s=>{clearTimeout(a);const h=s.payload;h.code===0?(this.connected=!0,this.everConnected=!0,this.reconnectAttempts=0,h.heartbeat_sec&&(this.heartbeatSec=h.heartbeat_sec),h.ack_policy&&(this.ackPolicy=h.ack_policy,c.info("aibot",`ack_policy received: push_ack_timeout_ms=${h.ack_policy.push_ack_timeout_ms??"default"} max_retries=${h.ack_policy.max_retries??"default"} timeout_action=${h.ack_policy.timeout_action??"default"}`)),this.startHeartbeat(),this.flushOutboundBuffer(),this.emit("auth",h),r(h)):i(new Error(`Auth failed: code=${h.code} msg=${h.msg}`))},reject:s=>{clearTimeout(a),i(s)},timer:d}),u.on("open",async()=>{await n;const s={agent_id:this.config.agentId,api_key:this.config.apiKey,client_type:this.config.clientType,protocol_version:q,contract_version:E,capabilities:this.config.capabilities??[],local_actions:this.config.localActions,skills:this.config.skills};this.config.clientVersion&&(s.client="grix-connector",s.client_version=this.config.clientVersion,s.host_type=this.config.clientType,s.host_version=this.config.clientVersion),this.config.adapterHint&&(s.adapter_hint=this.config.adapterHint),s.host_meta={hostname:_.hostname(),platform:_.platform(),arch:_.arch(),os_release:_.release(),...e!==void 0&&{tailnet_ip:e},...t!==void 0&&t>0&&{file_server_port:t}},this.config.concurrency&&(s.concurrency=this.config.concurrency),this.sendPacket("auth",s,o)}),u.on("message",s=>{let h;try{h=JSON.parse(b(s.toString()))}catch{return}try{this.handlePacket(h)}catch(f){this.emitClientError(new Error(`handlePacket error: ${f}`))}}),u.on("close",(s,h)=>{this.connected=!1,this.stopHeartbeat(),this.rejectAllPendingRequests("websocket closed"),this.emit("close",s,h.toString());const f=s!==1e3&&this.everConnected;c.info("aibot",`ws closed agent=${this.config.clientType}:${this.config.agentId} code=${s} reason=${h.toString()||"<none>"} everConnected=${this.everConnected} reconnecting=${this.reconnecting} willReconnect=${f}`),f&&this.attemptReconnect()}),u.on("error",s=>{this.emitClientError(s instanceof Error?s:new Error(String(s))),this.connected||i(s)})})}handlePacket(e){if(this.packetLog?.logInboundPacket(e.cmd,e.seq,e.payload),e.seq>0&&this.pendingRequests.has(e.seq)){const t=this.pendingRequests.get(e.seq);this.pendingRequests.delete(e.seq),clearTimeout(t.timer),t.expected.includes(e.cmd)?t.resolve(e):t.reject(new Error(`unexpected response: got ${e.cmd}, expected ${t.expected.join("/")}`));return}switch(e.cmd){case"auth_ack":break;case"ping":{this.sendPacket("pong",e.payload??{});break}case"event_msg":{this.emit("event",e.payload);break}case"local_action":{this.emit("localAction",e.payload);break}case"event_stop":{this.emit("stop",e.payload);break}case"event_revoke":{this.emit("revoke",e.payload);break}case"event_edit":{this.emit("edit",e.payload);break}case"event_cancel":{this.emit("eventCancel",e.payload);break}case"queue_clear":{this.emit("queueClear",e.payload);break}case"queue_snapshot_query":{this.emit("queueSnapshotQuery",e.payload);break}case"kicked":{if(this.emit("kicked",e.payload),this.connected=!1,this.stopHeartbeat(),this.rejectAllPendingRequests("kicked"),this.outboundBuffer.length=0,this.reconnectAttempts=Math.max(this.reconnectAttempts,3),this.ws){try{this.ws.close(4001,"kicked")}catch{}this.ws=null}break}case"error":{const t=e.payload,n=[t.ref_cmd?`ref_cmd=${t.ref_cmd}`:"",t.ref_id?`ref_id=${t.ref_id}`:""].filter(Boolean).join(" ");this.emitClientError(new Error(`Server error: code=${t.code} msg=${t.msg}${n?` ${n}`:""}`));break}case"agent_invoke_result":{this.handleInvokeResult(e.payload);break}case"mcp_frame":{const t=e.payload;this.emit("mcpFrame",t.session_id??"",t.frame??null);break}case"send_ack":break;case"send_nack":{const t=e.payload;if(t.code===4003&&e.seq>0){const n=this.seqEventMap.get(e.seq);n&&(this.seqEventMap.delete(e.seq),this.purgeBufferedStreamChunks(n),c.warn("aibot",`stream chunk rejected (4003), purging buffered chunks for event=${n}`),this.emit("streamRejected",n,t.code))}break}case"local_action_ack":break;default:break}}sendEventAck(e){this.sendPacket("event_ack",e)||c.warn("aibot",`event_ack NOT sent (ws not open) event=${e.event_id} ws=${this.ws?`state=${this.ws.readyState}`:"null"}`)}sendStreamChunk(e){!e.delta_content&&!e.is_finish&&(c.warn("aibot",`stream_chunk delta_content empty, patched to newline event=${e.event_id??""} session=${e.session_id} chunk_seq=${e.chunk_seq} is_finish=${e.is_finish}`),e={...e,delta_content:`
2
+ `}),this.sendPacket("client_stream_chunk",e)}sendMsg(e){this.sendPacket("send_msg",e)||c.warn("aibot",`send_msg NOT sent (ws not open) event=${e.event_id??""} session=${e.session_id??""} ws=${this.ws?`state=${this.ws.readyState}`:"null"}`)}editMsg(e){this.sendPacket("edit_msg",e)}sendEventResult(e){if(!this.ws||this.ws.readyState!==m.OPEN){this.sendPacket("event_result",e);return}this.sendEventResultReliable(e)}sendLocalActionResult(e){this.sendPacket("local_action_result",e)}sendEventStopAck(e){this.sendPacket("event_stop_ack",e)}sendEventStopResult(e){this.sendPacket("event_stop_result",e)}sendSessionActivitySet(e){this.sendPacket("session_activity_set",e)}sendCodexEvent(e){this.sendPacket("codex_event",e)}sendUpdateBindingCard(e){this.sendPacket("update_binding_card",e)}sendSkillsUpdate(e){this.sendPacket("agent_skills_update",e)}sendPing(){this.sendPacket("ping",{})}sendEventState(e){this.sendPacket("event_state",e)}sendEventCancelResult(e){this.sendPacket("event_cancel_result",e)}sendQueueClearResult(e){this.sendPacket("queue_clear_result",e)}sendQueueSnapshot(e){this.sendPacket("queue_snapshot",e)}agentInvoke(e,t,n=15e3){return new Promise((r,i)=>{const u=p(),a=Math.max(1e3,Math.min(n,6e4)),o=setTimeout(()=>{this.pendingInvokes.delete(u),i(new Error(`agent_invoke timeout: ${e}`))},a);this.pendingInvokes.set(u,{resolve:r,reject:i,timer:o}),this.sendPacket("agent_invoke",{invoke_id:u,action:e,params:t,timeout_ms:a})})}sendMcpFrame(e,t){this.sendPacket("mcp_frame",{session_id:e,frame:t})}request(e,t,n){return new Promise((r,i)=>{const u=++this.seq,a=setTimeout(()=>{this.pendingRequests.delete(u),i(new Error(`request timeout: ${e} (expected ${n.expected.join("/")})`))},n.timeoutMs);this.pendingRequests.set(u,{expected:n.expected,resolve:r,reject:i,timer:a}),this.sendPacket(e,t,u)||(this.pendingRequests.delete(u),clearTimeout(a),i(new Error(`send failed: ${e}`)))})}async sendStreamChunkRequest(e,t=2e4){return this.request("client_stream_chunk",e,{expected:["send_ack","send_nack","error"],timeoutMs:t})}async sendText(e,t=2e4){return this.request("send_msg",{msg_type:1,...e},{expected:["send_ack","send_nack","error"],timeoutMs:t})}async sendMedia(e,t=2e4){return this.request("send_msg",{...e,msg_type:2},{expected:["send_ack","send_nack","error"],timeoutMs:t})}async editMessage(e,t=2e4){return this.request("edit_msg",e,{expected:["send_ack","send_nack","error"],timeoutMs:t})}async deleteMessage(e,t,n=2e4){return this.request("delete_msg",{session_id:e,msg_id:t},{expected:["send_ack","send_nack","error"],timeoutMs:n})}async sendEventResultRequest(e,t=5e3){return this.request("event_result",e,{expected:["send_ack","send_nack","error"],timeoutMs:t})}disconnect(){c.info("aibot",`disconnect() agent=${this.config.clientType}:${this.config.agentId} wasConnected=${this.connected} reconnecting=${this.reconnecting} reconnectAttempts=${this.reconnectAttempts}`),this.connected=!1,this.everConnected=!1,this.reconnecting=!1,this.reconnectAttempts=0,this.stopHeartbeat(),this.rejectAllPendingInvokes("disconnect"),this.rejectAllPendingRequests("disconnect"),this.outboundBuffer.length=0,this.ws&&(this.ws.close(1e3,"client disconnect"),this.ws=null)}async attemptReconnect(){if(!this.reconnecting)for(this.reconnecting=!0,c.info("aibot",`attemptReconnect start agent=${this.config.clientType}:${this.config.agentId} fromAttempts=${this.reconnectAttempts}`),this.emit("disconnected");this.reconnecting;){const e=Math.min(1e3*2**this.reconnectAttempts,3e4),t=Math.floor(e*.2*Math.random());if(this.reconnectAttempts++,await new Promise(n=>setTimeout(n,e+t)),!this.reconnecting)return;try{const n=await this.connect(),r=this.reconnectAttempts;this.reconnectAttempts=0,this.reconnecting=!1,c.info("aibot",`reconnect succeeded agent=${this.config.clientType}:${this.config.agentId} attempt=${r}`),this.emit("auth",n);return}catch(n){c.warn("aibot",`reconnect failed agent=${this.config.clientType}:${this.config.agentId} attempt=${this.reconnectAttempts} err=${n instanceof Error?n.message:n}`)}}}sendPacket(e,t,n){if(this.ws&&this.ws.readyState===m.OPEN){const r=this.ws.bufferedAmount>l.BACKPRESSURE_THRESHOLD;if(!r||!l.DROPPABLE_COMMANDS.has(e)){if(r&&l.DROPPABLE_COMMANDS.has(e))return!1;const i=n??++this.seq;if(e==="client_stream_chunk"&&t&&typeof t=="object"){const a=t.event_id;if(a&&(this.seqEventMap.set(i,a),this.seqEventMap.size>200)){const o=this.seqEventMap.keys().next().value;o!==void 0&&this.seqEventMap.delete(o)}}const u={cmd:e,seq:i,payload:t};this.packetLog?.logOutboundPacket(e,i,t,"sent");try{const a=this.ws.readyState,o=this.ws.bufferedAmount;return this.ws.send(JSON.stringify(u),d=>{if(e==="event_result"){const s=t;d?c.warn("aibot",`event_result ws send callback failed event=${s.event_id??""} status=${s.status??""} seq=${i} readyState=${a} bufferedAmount=${o} err=${d.message}`):c.info("aibot",`event_result ws send callback ok event=${s.event_id??""} status=${s.status??""} seq=${i} readyState=${a} bufferedAmount=${o}`)}else if(e==="client_stream_chunk"){const s=t;d?c.warn("aibot",`stream_chunk ws send failed event=${s.event_id??""} session=${s.session_id??""} seq=${i} chunk_seq=${s.chunk_seq??""} is_finish=${s.is_finish??""} readyState=${a} bufferedAmount=${o} err=${d.message}`):c.info("aibot",`stream_chunk ws send ok event=${s.event_id??""} session=${s.session_id??""} seq=${i} chunk_seq=${s.chunk_seq??""} is_finish=${s.is_finish??""} readyState=${a} bufferedAmount=${o}`)}else if(e==="event_ack"){const s=t;d?c.warn("aibot",`event_ack ws send failed event=${s.event_id??""} seq=${i} readyState=${a} bufferedAmount=${o} err=${d.message}`):c.info("aibot",`event_ack ws send ok event=${s.event_id??""} seq=${i} readyState=${a} bufferedAmount=${o}`)}else if(e==="send_msg"){const s=t;d?c.warn("aibot",`send_msg ws send failed event=${s.event_id??""} session=${s.session_id??""} seq=${i} readyState=${a} bufferedAmount=${o} err=${d.message}`):c.info("aibot",`send_msg ws send ok event=${s.event_id??""} session=${s.session_id??""} seq=${i} readyState=${a} bufferedAmount=${o}`)}else if(d){const s=t;c.warn("aibot",`${e} ws send failed seq=${i} session=${s.session_id??""} event=${s.event_id??""} client_msg_id=${s.client_msg_id??""} readyState=${a} bufferedAmount=${o} err=${d.message}`)}}),!0}catch(a){return this.emitClientError(new Error(`sendPacket failed: ${a}`)),!1}}}if(l.DROPPABLE_COMMANDS.has(e))return this.packetLog?.logOutboundPacket(e,n??0,t,"dropped"),!1;if(n!==void 0)return this.packetLog?.logOutboundPacket(e,n,t,"dropped"),!1;if(this.outboundBuffer.length>=l.MAX_OUTBOUND_BUFFER_SIZE&&(this.outboundBuffer=this.outboundBuffer.filter(r=>l.BUFFER_OVERFLOW_RETAIN_COMMANDS.has(r.cmd)),this.outboundBuffer.length>=l.MAX_OUTBOUND_BUFFER_SIZE&&this.outboundBuffer.shift()),this.outboundBuffer.push({cmd:e,payload:t}),this.packetLog?.logOutboundPacket(e,n??0,t,"buffered"),e==="client_stream_chunk"){const r=t;c.info("aibot",`stream_chunk buffered (ws not open) event=${r.event_id??""} session=${r.session_id??""} chunk_seq=${r.chunk_seq??""} is_finish=${r.is_finish??""} ws=${this.ws?`state=${this.ws.readyState}`:"null"}`)}return!1}async sendEventResultReliable(e){const t=this.ackPolicy?.max_retries??3,n=this.ackPolicy?.push_ack_timeout_ms??5e3,r=750;for(let i=1;i<=t;i++){const u=this.ws?.readyState??-1,a=this.ws?.bufferedAmount??0;c.info("aibot",`event_result send attempt event=${e.event_id} status=${e.status} attempt=${i}/${t} readyState=${u} bufferedAmount=${a}`);try{const o=await this.sendEventResultRequest(e,n);if(o.cmd==="send_ack"){const s=o.payload;c.info("aibot",`event_result ack event=${e.event_id} status=${e.status} attempt=${i}/${t} ack_event=${s.event_id??""} ack_status=${s.status??""}`);return}const d=o.payload;if(c.warn("aibot",`event_result rejected event=${e.event_id} status=${e.status} attempt=${i}/${t} cmd=${o.cmd} code=${d.code??""} msg=${d.msg??""}${d.ref_cmd?` ref_cmd=${d.ref_cmd}`:""}${d.ref_id?` ref_id=${d.ref_id}`:""}`),d.code===4003){c.warn("aibot",`event_result stopping retries: 4003 ownership denied event=${e.event_id}`);return}return}catch(o){const d=o instanceof Error?o.message:String(o);if(c.warn("aibot",`event_result attempt failed event=${e.event_id} status=${e.status} attempt=${i}/${t} err=${d}`),i===t){this.emitClientError(new Error(`event_result ack failed after ${t} attempts: event=${e.event_id} status=${e.status}`));return}await new Promise(s=>setTimeout(s,r*i))}}}purgeBufferedStreamChunks(e){const t=this.outboundBuffer.length;this.outboundBuffer=this.outboundBuffer.filter(n=>n.cmd!=="client_stream_chunk"?!0:n.payload?.event_id!==e),this.outboundBuffer.length<t&&c.info("aibot",`purged ${t-this.outboundBuffer.length} buffered stream chunks for event=${e}`)}emitClientError(e){if(this.listenerCount("error")===0){c.warn("aibot",`Client error (no listeners): ${e.message}`);return}this.emit("error",e)}flushOutboundBuffer(){if(this.outboundBuffer.length===0||!this.ws||this.ws.readyState!==m.OPEN)return;const e=this.outboundBuffer;this.outboundBuffer=[];for(const{cmd:t,payload:n}of e){const r=++this.seq;if(t==="client_stream_chunk"&&n&&typeof n=="object"){const u=n.event_id;u&&this.seqEventMap.set(r,u)}const i={cmd:t,seq:r,payload:n};try{this.ws.send(JSON.stringify(i))}catch{break}}if(this.seqEventMap.size>200){const t=[...this.seqEventMap.entries()].sort((n,r)=>n[0]-r[0]);this.seqEventMap.clear();for(const[n,r]of t.slice(-100))this.seqEventMap.set(n,r)}}handleInvokeResult(e){const t=this.pendingInvokes.get(e.invoke_id);t&&(this.pendingInvokes.delete(e.invoke_id),clearTimeout(t.timer),e.code===0?t.resolve(e.data??null):t.reject(new Error(`agent_invoke error code=${e.code}: ${e.msg??""}`)))}rejectAllPendingInvokes(e){for(const[,t]of this.pendingInvokes)clearTimeout(t.timer),t.reject(new Error(`agent_invoke canceled: ${e}`));this.pendingInvokes.clear()}rejectAllPendingRequests(e){for(const[,t]of this.pendingRequests)clearTimeout(t.timer),t.reject(new Error(`request canceled: ${e}`));this.pendingRequests.clear()}cleanupSocket(){if(this.ws){try{this.ws.close()}catch{}this.ws=null}}startHeartbeat(){this.stopHeartbeat(),this.heartbeatTimer=setInterval(()=>{this.connected&&this.request("ping",{ts:Date.now()},{expected:["pong"],timeoutMs:5e3}).catch(()=>{this.connected&&(this.cleanupSocket(),this.attemptReconnect())})},this.heartbeatSec*1e3)}stopHeartbeat(){this.heartbeatTimer&&(clearInterval(this.heartbeatTimer),this.heartbeatTimer=null)}}export{l as AibotClient};
@@ -1 +1 @@
1
- import{readdir as r,stat as m}from"node:fs/promises";import{join as l,extname as d}from"node:path";const x={pdf:"application/pdf",doc:"application/msword",docx:"application/vnd.openxmlformats-officedocument.wordprocessingml.document",xls:"application/vnd.ms-excel",xlsx:"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",ppt:"application/vnd.ms-powerpoint",pptx:"application/vnd.openxmlformats-officedocument.presentationml.presentation",txt:"text/plain",md:"text/markdown",csv:"text/csv",json:"application/json",xml:"application/xml",yaml:"text/yaml",yml:"text/yaml",html:"text/html",css:"text/css",js:"text/javascript",ts:"text/typescript",zip:"application/zip",rar:"application/x-rar-compressed","7z":"application/x-7z-compressed",tar:"application/x-tar",gz:"application/gzip",jpg:"image/jpeg",jpeg:"image/jpeg",png:"image/png",gif:"image/gif",webp:"image/webp",svg:"image/svg+xml",mp4:"video/mp4",mov:"video/quicktime",avi:"video/x-msvideo",mkv:"video/x-matroska",webm:"video/webm",mp3:"audio/mpeg",wav:"audio/wav",flac:"audio/flac",aac:"audio/aac"};function n(a){const p=d(a).slice(1).toLowerCase();return x[p]}async function f(a,p=!1){const c=await r(a,{withFileTypes:!0}),s=[];for(const t of c){if(!p&&t.name.startsWith("."))continue;const i=l(a,t.name),e={id:i,name:t.name,is_directory:t.isDirectory()};try{if(t.isDirectory()){const o=await m(i);e.modified_at=o.mtime.toISOString()}else{const o=await m(i);e.size=o.size,e.modified_at=o.mtime.toISOString(),e.mime_type=n(t.name)}}catch{}s.push(e)}return s.sort((t,i)=>t.is_directory!==i.is_directory?t.is_directory?-1:1:t.name.localeCompare(i.name)),s}export{f as listFiles,n as resolveMimeType};
1
+ import{readdir as r,stat as m}from"node:fs/promises";import{join as l,extname as d}from"node:path";const x={pdf:"application/pdf",doc:"application/msword",docx:"application/vnd.openxmlformats-officedocument.wordprocessingml.document",xls:"application/vnd.ms-excel",xlsx:"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",ppt:"application/vnd.ms-powerpoint",pptx:"application/vnd.openxmlformats-officedocument.presentationml.presentation",txt:"text/plain",md:"text/markdown",csv:"text/csv",json:"application/json",xml:"application/xml",yaml:"text/yaml",yml:"text/yaml",html:"text/html",css:"text/css",js:"text/javascript",ts:"text/typescript",zip:"application/zip",rar:"application/x-rar-compressed","7z":"application/x-7z-compressed",tar:"application/x-tar",gz:"application/gzip",jpg:"image/jpeg",jpeg:"image/jpeg",png:"image/png",gif:"image/gif",webp:"image/webp",svg:"image/svg+xml",mp4:"video/mp4",mov:"video/quicktime",avi:"video/x-msvideo",mkv:"video/x-matroska",webm:"video/webm",mp3:"audio/mpeg",wav:"audio/wav",flac:"audio/flac",aac:"audio/aac"};function n(a){const p=d(a).slice(1).toLowerCase();return x[p]}async function f(a,p=!1){const c=await r(a,{withFileTypes:!0}),s=[];for(const i of c){if(!p&&i.name.startsWith("."))continue;const t=l(a,i.name),e={id:t,name:i.name,is_directory:i.isDirectory()};try{if(i.isDirectory()){const o=await m(t);e.modified_at=o.mtime.toISOString()}else{const o=await m(t);e.size=o.size,e.modified_at=o.mtime.toISOString(),e.mime_type=n(i.name)}}catch{}s.push(e)}return s.sort((i,t)=>i.is_directory!==t.is_directory?i.is_directory?-1:1:i.name.localeCompare(t.name)),s}export{f as listFiles,n as resolveMimeType};
@@ -1,8 +1,8 @@
1
- import{execFile as P,spawn as D}from"node:child_process";import{existsSync as k}from"node:fs";import{join as T}from"node:path";import{log as h}from"../log/logger.js";import{resolveCliPath as v,getCliVersion as S}from"../util/cli-probe.js";import{getInstallCommand as y,getCliBinary as A,isKnownAgent as O,detectPlatformOS as U}from"./registry.js";import{checkPrerequisites as x,getMissingPrerequisites as L}from"./preflight.js";import{installMissingPrerequisites as V}from"./prereq-installer.js";import{detectEnvironment as C,formatEnvironmentInfo as q,isEnvironmentSupported as F}from"./env-detect.js";import{generateManualGuide as b}from"./manual-guide.js";import{npmInstallWithMirror as G}from"./npm-registry.js";import{getAllAgentInstallInfo as B}from"./registry.js";class a extends Error{code;constructor(n,t){super(t),this.name="InstallerError",this.code=n}}const $=64*1024,W=20,N=2,j=3e3;class se{os;activeInstalls=new Map;constructor(){this.os=U()}listInstallable(){return{platform:this.os,agents:B(this.os)}}getProgress(n){return this.activeInstalls.get(n)}isInProgress(n){return this.activeInstalls.has(n)}async install(n){const{agentType:t}=n,e=Date.now();if(this.activeInstalls.has(t))return this.fail(t,"preflight",e,new a("INSTALL_IN_PROGRESS",`${t} is already being installed`));try{return await this._doInstall(n,e)}catch(i){this.activeInstalls.delete(t);const r=i instanceof Error?i.message:String(i);return h.error("installer",`${t} install unexpected error: ${r}`),{agentType:t,ok:!1,phase:"failed",error:{code:"INTERNAL",message:`Unexpected error: ${r}`},durationMs:Date.now()-e,output:""}}}async _doInstall(n,t){const{agentType:e}=n;let i;try{i=await C()}catch(l){const u=l instanceof Error?l.message:String(l);return this.fail(e,"preflight",t,new a("INTERNAL",`Environment detection failed: ${u}`))}h.info("installer",`Install request: ${e}
2
- ${q(i)}`);const r=F(i);if(!r.supported)return this.fail(e,"preflight",t,new a("ENVIRONMENT_UNSUPPORTED",`Current environment is not supported for automatic installation: ${r.reason}. Please install ${e} manually.`),i);if(this.setProgress(e,"preflight",t),!O(e))return this.fail(e,"preflight",t,new a("UNKNOWN_AGENT",`Unknown agent type: ${e}`),i);const s=y(e,this.os);if(!s)return this.fail(e,"preflight",t,new a("UNSUPPORTED_OS",`Installation of ${e} is not supported on ${this.os}`),i);const c=A(e),m=await v(c),p=m?(await S(m)).version:null;if(m&&!n.force){this.activeInstalls.delete(e);const l=Date.now()-t;return h.info("installer",`${e} already installed at ${m}${p?` (v${p})`:""}`),{agentType:e,ok:!0,phase:"completed",installedPath:m,installedVersion:p,durationMs:l,output:"",environment:i}}const f=s.prerequisites??[];let o=[];if(f.length>0){h.info("installer",`Checking prerequisites for ${e}: ${f.join(", ")}`),o=await x(f,this.os),h.info("installer",`Prerequisites: ${o.map(u=>`${u.label}=${u.met?u.version:"missing"}`).join(", ")}`);const l=L(o);if(l.length>0){const u=l.map(d=>`${d.label}${d.minVersion?` >= ${d.minVersion}`:""}`).join(", ");if(!n.dryRun){if(n.skipPrereqInstall)return this.fail(e,"preflight",t,new a("PREREQ_MISSING",`Missing prerequisites: ${u}. Install them first or retry without skipPrereqInstall.`),i,o);const d=l.map(I=>`${I.label}${I.minVersion?` >= ${I.minVersion}`:""}`);h.info("installer",`Will auto-install prerequisites: ${d.join(", ")}`),this.setProgress(e,"installing_prereq",t,l[0].label,d);const w=await V(l,this.os);if(!w.allOk){const I=w.results.find(_=>!_.ok),R=I?`Failed to install prerequisite ${I.prereq.label}: ${I.output}`:"Prerequisite installation failed";return this.fail(e,"installing_prereq",t,new a("PREREQ_INSTALL_FAILED",R),i,o)}h.info("installer",`All prerequisites installed for ${e}`),o=await x(f,this.os)}}}if(n.dryRun){this.activeInstalls.delete(e);const l=L(o),u=this.getManualHint(e,this.os),d={agentType:e,environment:i,canInstall:!0,alreadyInstalled:!!m,installedPath:m,installedVersion:p,installCommand:s.command,installMode:s.mode,prerequisites:o,missingPrerequisites:l,fallbackCommand:s.fallback?.command??null,manualHint:u},w=b({agentType:e,os:this.os,env:i,missingPrereqs:l});return{agentType:e,ok:!0,phase:"completed",durationMs:Date.now()-t,output:"dry-run: no commands executed",environment:i,dryRun:d,manualGuide:w}}this.setProgress(e,"installing",t),h.info("installer",`Installing ${e}: ${s.command}`);let g;try{g=await this.executeWithRetry(s,e,t,n.timeoutMs)}catch(l){if(s.fallback&&l instanceof a&&(l.code==="INSTALL_FAILED"||l.code==="INSTALL_TIMEOUT")){h.info("installer",`Primary install failed after retries, trying fallback: ${s.fallback.command}`),this.setProgress(e,"installing",t);try{g=await this.executeWithRetry(s.fallback,e,t,n.timeoutMs),g=`[primary failed, fallback succeeded]
3
- ${g}`}catch(u){const d=l.message,w=u instanceof a?u.message:String(u);return this.fail(e,"installing",t,new a("FALLBACK_EXHAUSTED",`Both primary and fallback install methods failed.
1
+ import{execFile as k,spawn as D}from"node:child_process";import{existsSync as P}from"node:fs";import{join as T}from"node:path";import{log as h}from"../log/logger.js";import{resolveCliPath as v,getCliVersion as S}from"../util/cli-probe.js";import{getInstallCommand as y,getCliBinary as A,isKnownAgent as O,detectPlatformOS as U}from"./registry.js";import{checkPrerequisites as x,getMissingPrerequisites as L}from"./preflight.js";import{installMissingPrerequisites as V}from"./prereq-installer.js";import{detectEnvironment as C,formatEnvironmentInfo as q,isEnvironmentSupported as F}from"./env-detect.js";import{generateManualGuide as b}from"./manual-guide.js";import{npmInstallWithMirror as G}from"./npm-registry.js";import{getAllAgentInstallInfo as B}from"./registry.js";class a extends Error{code;constructor(n,t){super(t),this.name="InstallerError",this.code=n}}const w=64*1024,H=20,N=2,W=3e3;class se{os;activeInstalls=new Map;constructor(){this.os=U()}listInstallable(){return{platform:this.os,agents:B(this.os)}}getProgress(n){return this.activeInstalls.get(n)}isInProgress(n){return this.activeInstalls.has(n)}async install(n){const{agentType:t}=n,e=Date.now();if(this.activeInstalls.has(t))return this.fail(t,"preflight",e,new a("INSTALL_IN_PROGRESS",`${t} is already being installed`));try{return await this._doInstall(n,e)}catch(i){this.activeInstalls.delete(t);const o=i instanceof Error?i.message:String(i);return h.error("installer",`${t} install unexpected error: ${o}`),{agentType:t,ok:!1,phase:"failed",error:{code:"INTERNAL",message:`Unexpected error: ${o}`},durationMs:Date.now()-e,output:""}}}async _doInstall(n,t){const{agentType:e}=n;let i;try{i=await C()}catch(l){const c=l instanceof Error?l.message:String(l);return this.fail(e,"preflight",t,new a("INTERNAL",`Environment detection failed: ${c}`))}h.info("installer",`Install request: ${e}
2
+ ${q(i)}`);const o=F(i);if(!o.supported)return this.fail(e,"preflight",t,new a("ENVIRONMENT_UNSUPPORTED",`Current environment is not supported for automatic installation: ${o.reason}. Please install ${e} manually.`),i);if(this.setProgress(e,"preflight",t),!O(e))return this.fail(e,"preflight",t,new a("UNKNOWN_AGENT",`Unknown agent type: ${e}`),i);const s=y(e,this.os);if(!s){const l=this.getManualHint(e,this.os),c=l?`Installation of ${e} is not supported on ${this.os}. ${l}`:`Installation of ${e} is not supported on ${this.os}`;return this.fail(e,"preflight",t,new a("UNSUPPORTED_OS",c),i)}const u=A(e),m=await v(u),p=m?(await S(m)).version:null;if(m&&!n.force){this.activeInstalls.delete(e);const l=Date.now()-t;return h.info("installer",`${e} already installed at ${m}${p?` (v${p})`:""}`),{agentType:e,ok:!0,phase:"completed",installedPath:m,installedVersion:p,durationMs:l,output:"",environment:i}}const f=s.prerequisites??[];let r=[];if(f.length>0){h.info("installer",`Checking prerequisites for ${e}: ${f.join(", ")}`),r=await x(f,this.os),h.info("installer",`Prerequisites: ${r.map(c=>`${c.label}=${c.met?c.version:"missing"}`).join(", ")}`);const l=L(r);if(l.length>0){const c=l.map(d=>`${d.label}${d.minVersion?` >= ${d.minVersion}`:""}`).join(", ");if(!n.dryRun){if(n.skipPrereqInstall)return this.fail(e,"preflight",t,new a("PREREQ_MISSING",`Missing prerequisites: ${c}. Install them first or retry without skipPrereqInstall.`),i,r);const d=l.map(I=>`${I.label}${I.minVersion?` >= ${I.minVersion}`:""}`);h.info("installer",`Will auto-install prerequisites: ${d.join(", ")}`),this.setProgress(e,"installing_prereq",t,l[0].label,d);const $=await V(l,this.os);if(!$.allOk){const I=$.results.find(_=>!_.ok),R=I?`Failed to install prerequisite ${I.prereq.label}: ${I.output}`:"Prerequisite installation failed";return this.fail(e,"installing_prereq",t,new a("PREREQ_INSTALL_FAILED",R),i,r)}h.info("installer",`All prerequisites installed for ${e}`),r=await x(f,this.os)}}}if(n.dryRun){this.activeInstalls.delete(e);const l=L(r),c=this.getManualHint(e,this.os),d={agentType:e,environment:i,canInstall:!0,alreadyInstalled:!!m,installedPath:m,installedVersion:p,installCommand:s.command,installMode:s.mode,prerequisites:r,missingPrerequisites:l,fallbackCommand:s.fallback?.command??null,manualHint:c},$=b({agentType:e,os:this.os,env:i,missingPrereqs:l});return{agentType:e,ok:!0,phase:"completed",durationMs:Date.now()-t,output:"dry-run: no commands executed",environment:i,dryRun:d,manualGuide:$}}this.setProgress(e,"installing",t),h.info("installer",`Installing ${e}: ${s.command}`);let g;try{g=await this.executeWithRetry(s,e,t,n.timeoutMs)}catch(l){if(s.fallback&&l instanceof a&&(l.code==="INSTALL_FAILED"||l.code==="INSTALL_TIMEOUT")){h.info("installer",`Primary install failed after retries, trying fallback: ${s.fallback.command}`),this.setProgress(e,"installing",t);try{g=await this.executeWithRetry(s.fallback,e,t,n.timeoutMs),g=`[primary failed, fallback succeeded]
3
+ ${g}`}catch(c){const d=l.message,$=c instanceof a?c.message:String(c);return this.fail(e,"installing",t,new a("FALLBACK_EXHAUSTED",`Both primary and fallback install methods failed.
4
4
  Primary: ${d}
5
- Fallback: ${w}`),i,o)}}else return l instanceof a?this.fail(e,"installing",t,new a(l.code,l.message),i,o):this.fail(e,"installing",t,new a("INTERNAL",l instanceof Error?l.message:String(l)),i,o)}if(!n.skipVerify){this.setProgress(e,"verifying",t),h.info("installer",`Verifying ${e} installation...`);let l=await v(c);if(l||(l=await this.resolveViaNpmBin(c)),!l)return this.fail(e,"verifying",t,new a("VERIFICATION_FAILED",`${c} not found on PATH after installation. You may need to open a new terminal or run: source ~/.zshrc (or ~/.bashrc)`),i,o);const{version:u}=await S(l),d=Date.now()-t;return this.activeInstalls.delete(e),h.info("installer",`${e} installed successfully at ${l} (v${u??"unknown"}, ${d}ms)`),{agentType:e,ok:!0,phase:"completed",installedPath:l,installedVersion:u,durationMs:d,output:g,prerequisites:o.length>0?o:void 0,environment:i}}const E=Date.now()-t;return this.activeInstalls.delete(e),h.info("installer",`${e} install command completed (${E}ms, verification skipped)`),{agentType:e,ok:!0,phase:"completed",installedPath:null,installedVersion:null,durationMs:E,output:g,prerequisites:o.length>0?o:void 0,environment:i}}getManualHint(n,t){const i=y(n,t)?.fallback,s={claude:"https://docs.anthropic.com/en/docs/claude-code/overview",codex:"https://github.com/openai/codex",gemini:"https://github.com/google-gemini/gemini-cli",qwen:"https://github.com/QwenLM/qwen-code",cursor:"https://cursor.com/docs/cli/installation",copilot:"https://docs.github.com/en/copilot/managing-copilot/configure-personal-settings/installing-github-copilot-in-the-cli",kiro:"https://kiro.dev/docs/cli/",openclaw:"https://github.com/openclaw/openclaw",reasonix:"https://github.com/esengine/DeepSeek-Reasonix"}[n],c=[];return s&&c.push(`Docs: ${s}`),i&&c.push(`Alternative: ${i.command}`),c.length>0?c.join(" | "):null}setProgress(n,t,e,i,r){this.activeInstalls.set(n,{agentType:n,phase:t,startedAt:e,elapsedMs:Date.now()-e,...i?{currentPrereq:i}:{},...r?{pendingPrereqs:r}:{}})}fail(n,t,e,i,r,s,c){const m=c??this.activeInstalls.get(n)?.outputTail??"";this.activeInstalls.delete(n),h.error("installer",`${n} install failed at ${t}: ${i.message}`);const p=s?L(s):[],f=b({agentType:n,os:this.os,env:r??{platform:this.os,osVersion:"unknown",arch:process.arch,shell:process.env.SHELL??null,nodeVersion:null,npmVersion:null,isDocker:!1,isCI:!1},missingPrereqs:p,primaryFailed:t==="installing",fallbackFailed:i.code==="FALLBACK_EXHAUSTED",error:i.message});return{agentType:n,ok:!1,phase:"failed",error:{code:i.code,message:i.message},durationMs:Date.now()-e,output:m,environment:r,prerequisites:s,manualGuide:f}}async executeWithRetry(n,t,e,i){let r=null;for(let s=0;s<=N;s++)try{return s>0&&(h.info("installer",`Retry ${s}/${N} for ${t}...`),await this.sleep(j)),await this.executeCommand(n,t,e,i)}catch(c){if(r=c instanceof a?c:new a("INTERNAL",String(c)),!(r.code==="INSTALL_TIMEOUT")||s>=N)throw r;h.info("installer",`Attempt ${s+1} failed (retryable): ${r.message}`)}throw r??new a("INTERNAL","Unexpected retry loop exit")}sleep(n){return new Promise(t=>setTimeout(t,n))}executeCommand(n,t,e,i){const r=i??n.timeoutMs;switch(n.mode){case"npm":return this.executeNpm(n.npmPackage,r,t,e);case"shell":return this.executeShell(n.command,r,t,e);case"exec":return this.executeExec(n.command,n.execArgs??[],r,t,e);default:return Promise.reject(new a("INTERNAL",`Unknown install mode: ${n.mode}`))}}async executeNpm(n,t,e,i){try{const{output:r,registry:s}=await G(n,t,$);return h.info("installer",`npm install ${n} succeeded via ${s}`),r}catch(r){const s=r instanceof Error?r.message:String(r);throw s.includes("timed out")||s.includes("ETIMEDOUT")?new a("INSTALL_TIMEOUT",`npm install timed out (tried all mirrors): ${s}`):new a("INSTALL_FAILED",`npm install failed (tried all mirrors): ${s}`)}}executeShell(n,t,e,i){return new Promise((r,s)=>{const c=process.platform==="win32",m=c?"cmd.exe":"sh",p=c?["/d","/s","/c",n]:["-c",n];h.info("installer",`exec: ${m} ${p.join(" ")}`);const f=D(m,p,{timeout:t,stdio:["ignore","pipe","pipe"],env:{...process.env,NONINTERACTIVE:"1",DEBIAN_FRONTEND:"noninteractive"}});let o="",g=!1;const E=setTimeout(()=>{g=!0;try{f.kill("SIGTERM")}catch{}setTimeout(()=>{try{f.kill("SIGKILL")}catch{}},5e3).unref()},t);f.stdout?.on("data",l=>{const u=l.toString("utf-8");o+=u,o.length>$&&(o=o.slice(-$)),this.updateOutputTail(e,i,o)}),f.stderr?.on("data",l=>{const u=l.toString("utf-8");o+=u,o.length>$&&(o=o.slice(-$)),this.updateOutputTail(e,i,o)}),f.on("error",l=>{clearTimeout(E),s(new a("INSTALL_FAILED",`Spawn error: ${l.message}`))}),f.on("close",l=>{if(clearTimeout(E),g){s(new a("INSTALL_TIMEOUT",`Install timed out after ${t/1e3}s`));return}if(l!==0){const u=o.slice(-1024);s(new a("INSTALL_FAILED",`Process exited with code ${l}: ${u}`));return}r(o.trim())})})}executeExec(n,t,e,i,r){return new Promise((s,c)=>{h.info("installer",`exec: ${n} ${t.join(" ")}`);const m=P(n,t,{timeout:e,maxBuffer:$},(p,f,o)=>{if(p){if(p.killed)c(new a("INSTALL_TIMEOUT",`${n} timed out after ${e/1e3}s`));else{const g=o?.trim()||p.message;c(new a("INSTALL_FAILED",`${n} failed: ${g}`))}return}s(`${f??""}
6
- ${o??""}`.trim())});this.trackOutput(m,i,r)})}trackOutput(n,t,e){n.on("close",()=>{const i=this.activeInstalls.get(t);i&&(i.elapsedMs=Date.now()-e)})}async resolveViaNpmBin(n){return new Promise(t=>{const e=process.platform==="win32";P(e?"cmd.exe":"npm",e?["/c","npm","prefix","-g"]:["prefix","-g"],{timeout:5e3,encoding:"utf-8"},(s,c)=>{if(s){t(null);return}const m=c.trim();if(!m){t(null);return}const p=this.os==="windows"?T(m,`${n}.cmd`):T(m,"bin",n);if(k(p)){t(p);return}const f=T(m,n);if(k(f)){t(f);return}t(null)})})}updateOutputTail(n,t,e){const r=e.split(`
7
- `).slice(-W).join(`
8
- `),s=this.activeInstalls.get(n);s&&(s.outputTail=r,s.elapsedMs=Date.now()-t)}}export{se as AgentInstaller,a as InstallerError};
5
+ Fallback: ${$}`),i,r)}}else return l instanceof a?this.fail(e,"installing",t,new a(l.code,l.message),i,r):this.fail(e,"installing",t,new a("INTERNAL",l instanceof Error?l.message:String(l)),i,r)}if(!n.skipVerify&&!s.skipVerification){this.setProgress(e,"verifying",t),h.info("installer",`Verifying ${e} installation...`);let l=await v(u);if(l||(l=await this.resolveViaNpmBin(u)),!l)return this.fail(e,"verifying",t,new a("VERIFICATION_FAILED",`${u} not found on PATH after installation. You may need to open a new terminal or run: source ~/.zshrc (or ~/.bashrc)`),i,r);const{version:c}=await S(l),d=Date.now()-t;return this.activeInstalls.delete(e),h.info("installer",`${e} installed successfully at ${l} (v${c??"unknown"}, ${d}ms)`),{agentType:e,ok:!0,phase:"completed",installedPath:l,installedVersion:c,durationMs:d,output:g,prerequisites:r.length>0?r:void 0,environment:i}}const E=Date.now()-t;return this.activeInstalls.delete(e),h.info("installer",`${e} install command completed (${E}ms, verification skipped)`),{agentType:e,ok:!0,phase:"completed",installedPath:null,installedVersion:null,durationMs:E,output:g,prerequisites:r.length>0?r:void 0,environment:i}}getManualHint(n,t){const i=y(n,t)?.fallback,s={claude:"https://docs.anthropic.com/en/docs/claude-code/overview",codex:"https://github.com/openai/codex",gemini:"https://github.com/google-gemini/gemini-cli",qwen:"https://github.com/QwenLM/qwen-code",cursor:"https://cursor.com/docs/cli/installation",copilot:"https://docs.github.com/en/copilot/managing-copilot/configure-personal-settings/installing-github-copilot-in-the-cli",kiro:"https://kiro.dev/docs/cli/",openclaw:"https://github.com/openclaw/openclaw",reasonix:"https://github.com/esengine/DeepSeek-Reasonix",openhuman:"https://github.com/tinyhumansai/openhuman/issues/128"}[n],u=[];return s&&u.push(`Docs: ${s}`),i&&u.push(`Alternative: ${i.command}`),u.length>0?u.join(" | "):null}setProgress(n,t,e,i,o){this.activeInstalls.set(n,{agentType:n,phase:t,startedAt:e,elapsedMs:Date.now()-e,...i?{currentPrereq:i}:{},...o?{pendingPrereqs:o}:{}})}fail(n,t,e,i,o,s,u){const m=u??this.activeInstalls.get(n)?.outputTail??"";this.activeInstalls.delete(n),h.error("installer",`${n} install failed at ${t}: ${i.message}`);const p=s?L(s):[],f=b({agentType:n,os:this.os,env:o??{platform:this.os,osVersion:"unknown",arch:process.arch,shell:process.env.SHELL??null,nodeVersion:null,npmVersion:null,isDocker:!1,isCI:!1},missingPrereqs:p,primaryFailed:t==="installing",fallbackFailed:i.code==="FALLBACK_EXHAUSTED",error:i.message});return{agentType:n,ok:!1,phase:"failed",error:{code:i.code,message:i.message},durationMs:Date.now()-e,output:m,environment:o,prerequisites:s,manualGuide:f}}async executeWithRetry(n,t,e,i){let o=null;for(let s=0;s<=N;s++)try{return s>0&&(h.info("installer",`Retry ${s}/${N} for ${t}...`),await this.sleep(W)),await this.executeCommand(n,t,e,i)}catch(u){if(o=u instanceof a?u:new a("INTERNAL",String(u)),!(o.code==="INSTALL_TIMEOUT")||s>=N)throw o;h.info("installer",`Attempt ${s+1} failed (retryable): ${o.message}`)}throw o??new a("INTERNAL","Unexpected retry loop exit")}sleep(n){return new Promise(t=>setTimeout(t,n))}executeCommand(n,t,e,i){const o=i??n.timeoutMs;switch(n.mode){case"npm":return this.executeNpm(n.npmPackage,o,t,e);case"shell":return this.executeShell(n.command,o,t,e);case"exec":return this.executeExec(n.command,n.execArgs??[],o,t,e);default:return Promise.reject(new a("INTERNAL",`Unknown install mode: ${n.mode}`))}}async executeNpm(n,t,e,i){try{const{output:o,registry:s}=await G(n,t,w);return h.info("installer",`npm install ${n} succeeded via ${s}`),o}catch(o){const s=o instanceof Error?o.message:String(o);throw s.includes("timed out")||s.includes("ETIMEDOUT")?new a("INSTALL_TIMEOUT",`npm install timed out (tried all mirrors): ${s}`):new a("INSTALL_FAILED",`npm install failed (tried all mirrors): ${s}`)}}executeShell(n,t,e,i){return new Promise((o,s)=>{const u=process.platform==="win32",m=u?"cmd.exe":"sh",p=u?["/d","/s","/c",n]:["-c",n];h.info("installer",`exec: ${m} ${p.join(" ")}`);const f=D(m,p,{timeout:t,stdio:["ignore","pipe","pipe"],env:{...process.env,NONINTERACTIVE:"1",DEBIAN_FRONTEND:"noninteractive"}});let r="",g=!1;const E=setTimeout(()=>{g=!0;try{f.kill("SIGTERM")}catch{}setTimeout(()=>{try{f.kill("SIGKILL")}catch{}},5e3).unref()},t);f.stdout?.on("data",l=>{const c=l.toString("utf-8");r+=c,r.length>w&&(r=r.slice(-w)),this.updateOutputTail(e,i,r)}),f.stderr?.on("data",l=>{const c=l.toString("utf-8");r+=c,r.length>w&&(r=r.slice(-w)),this.updateOutputTail(e,i,r)}),f.on("error",l=>{clearTimeout(E),s(new a("INSTALL_FAILED",`Spawn error: ${l.message}`))}),f.on("close",l=>{if(clearTimeout(E),g){s(new a("INSTALL_TIMEOUT",`Install timed out after ${t/1e3}s`));return}if(l!==0){const c=r.slice(-1024);s(new a("INSTALL_FAILED",`Process exited with code ${l}: ${c}`));return}o(r.trim())})})}executeExec(n,t,e,i,o){return new Promise((s,u)=>{h.info("installer",`exec: ${n} ${t.join(" ")}`);const m=k(n,t,{timeout:e,maxBuffer:w},(p,f,r)=>{if(p){if(p.killed)u(new a("INSTALL_TIMEOUT",`${n} timed out after ${e/1e3}s`));else{const g=r?.trim()||p.message;u(new a("INSTALL_FAILED",`${n} failed: ${g}`))}return}s(`${f??""}
6
+ ${r??""}`.trim())});this.trackOutput(m,i,o)})}trackOutput(n,t,e){n.on("close",()=>{const i=this.activeInstalls.get(t);i&&(i.elapsedMs=Date.now()-e)})}async resolveViaNpmBin(n){return new Promise(t=>{const e=process.platform==="win32";k(e?"cmd.exe":"npm",e?["/c","npm","prefix","-g"]:["prefix","-g"],{timeout:5e3,encoding:"utf-8"},(s,u)=>{if(s){t(null);return}const m=u.trim();if(!m){t(null);return}const p=this.os==="windows"?T(m,`${n}.cmd`):T(m,"bin",n);if(P(p)){t(p);return}const f=T(m,n);if(P(f)){t(f);return}t(null)})})}updateOutputTail(n,t,e){const o=e.split(`
7
+ `).slice(-H).join(`
8
+ `),s=this.activeInstalls.get(n);s&&(s.outputTail=o,s.elapsedMs=Date.now()-t)}}export{se as AgentInstaller,a as InstallerError};
@@ -1,3 +1,4 @@
1
- import{execFile as h}from"node:child_process";import{join as l}from"node:path";import{promisify as v}from"node:util";import{log as o}from"../log/logger.js";const u=v(h),m=5e3;function f(e){const t=e.replace(/^[^0-9]*/,"").split(".");return[parseInt(t[0]??"0",10)||0,parseInt(t[1]??"0",10)||0]}function g(e,n){const[t,r]=f(e),[c,i]=f(n);return t>c||t===c&&r>=i}async function s(e,n=["--version"]){try{const{stdout:t}=await u(e,n,{timeout:m,encoding:"utf-8"});return{found:!0,version:(t||"").trim().split(`
2
- `)[0]?.trim()||null}}catch{return{found:!1,version:null}}}async function w(){const e=await s("node",["--version"]);return e.version?{found:!0,version:e.version.replace(/^v/,"")}:e}async function b(){if(process.platform==="win32"){o.info("preflight","[npm-detect] Starting Windows npm detection");const e=["npm",l(process.env.ProgramFiles||"C:\\Program Files","nodejs","npm.cmd"),l(process.env.APPDATA||"","npm","npm.cmd")];try{o.info("preflight","[npm-detect] Trying to locate node.exe via where.exe");const{stdout:n}=await u("where.exe",["node"],{timeout:m,encoding:"utf-8"}),t=l(n.trim().split(`
3
- `)[0],".."),r=l(t,"npm.cmd");e.push(r),o.info("preflight",`[npm-detect] Inferred npm path from node: ${r}`)}catch(n){o.warn("preflight",`[npm-detect] Failed to locate node.exe: ${n instanceof Error?n.message:String(n)}`)}o.info("preflight",`[npm-detect] Will try ${e.length} paths: ${e.join(", ")}`);for(const n of e)try{o.info("preflight",`[npm-detect] Trying: ${n}`);const{stdout:t}=await u("cmd.exe",["/c",n,"--version"],{timeout:m,encoding:"utf-8"}),r=t.trim();return o.info("preflight",`[npm-detect] SUCCESS at ${n}, version: ${r}`),{found:!0,version:r}}catch(t){o.warn("preflight",`[npm-detect] Failed at ${n}: ${t instanceof Error?t.message:String(t)}`)}return o.error("preflight","[npm-detect] All paths failed, npm not found"),{found:!1,version:null}}return s("npm",["--version"])}async function y(){const e=await s("curl",["--version"]);return e.version?{found:!0,version:e.version.split(" ")[0]?.trim()??null}:e}async function P(){const e=await s("gh",["--version"]);return e.version?{found:!0,version:e.version.match(/(\d+\.\d+\.\d+)/)?.[1]??e.version}:e}async function x(){const e=await s("brew",["--version"]);return e.version?{found:!0,version:e.version.match(/(\d+\.\d+\.\d+)/)?.[1]??e.version}:e}function V(e){return[{id:"node",label:"Node.js",minVersion:"18.0",detect:w},{id:"npm",label:"npm",minVersion:"9.0",detect:b},{id:"curl",label:"curl",minVersion:null,detect:y},{id:"gh",label:"GitHub CLI (gh)",minVersion:null,detect:P},...e!=="windows"?[{id:"brew",label:"Homebrew",minVersion:null,detect:x}]:[]]}async function k(e,n){const r=V(n).find(p=>p.id===e);if(!r)return{id:e,label:e,met:!1,version:null,minVersion:null,autoInstallable:!1};const{found:c,version:i}=await r.detect();let a=c;a&&r.minVersion&&i&&(a=g(i,r.minVersion));const d=C(e,n);return{id:e,label:r.label,met:a,version:i,minVersion:r.minVersion,autoInstallable:a?!1:d}}async function A(e,n){return await Promise.all(e.map(r=>k(r,n)))}function F(e){return e.every(n=>n.met)}function M(e){return e.filter(n=>!n.met)}function C(e,n){switch(e){case"node":case"npm":return!0;case"curl":return!0;case"gh":return!0;case"brew":return n==="macos";default:return!1}}export{F as allPrerequisitesMet,k as checkPrerequisite,A as checkPrerequisites,M as getMissingPrerequisites};
1
+ import{execFile as h}from"node:child_process";import{join as c}from"node:path";import{promisify as v}from"node:util";import{log as o}from"../log/logger.js";const u=v(h),m=5e3;function f(e){const t=e.replace(/^[^0-9]*/,"").split(".");return[parseInt(t[0]??"0",10)||0,parseInt(t[1]??"0",10)||0]}function g(e,n){const[t,r]=f(e),[i,s]=f(n);return t>i||t===i&&r>=s}async function a(e,n=["--version"]){try{const{stdout:t}=await u(e,n,{timeout:m,encoding:"utf-8"});return{found:!0,version:(t||"").trim().split(`
2
+ `)[0]?.trim()||null}}catch{return{found:!1,version:null}}}async function w(){const e=await a("node",["--version"]);return e.version?{found:!0,version:e.version.replace(/^v/,"")}:e}async function b(){if(process.platform==="win32"){o.info("preflight","[npm-detect] Starting Windows npm detection");const e=["npm",c(process.env.ProgramFiles||"C:\\Program Files","nodejs","npm.cmd"),c(process.env.APPDATA||"","npm","npm.cmd")];try{o.info("preflight","[npm-detect] Trying to locate node.exe via where.exe");const{stdout:n}=await u("where.exe",["node"],{timeout:m,encoding:"utf-8"}),t=c(n.trim().split(`
3
+ `)[0],".."),r=c(t,"npm.cmd");e.push(r),o.info("preflight",`[npm-detect] Inferred npm path from node: ${r}`)}catch(n){o.warn("preflight",`[npm-detect] Failed to locate node.exe: ${n instanceof Error?n.message:String(n)}`)}o.info("preflight",`[npm-detect] Will try ${e.length} paths: ${e.join(", ")}`);for(const n of e)try{o.info("preflight",`[npm-detect] Trying: ${n}`);const{stdout:t}=await u("cmd.exe",["/c",n,"--version"],{timeout:m,encoding:"utf-8"}),r=t.trim();return o.info("preflight",`[npm-detect] SUCCESS at ${n}, version: ${r}`),{found:!0,version:r}}catch(t){o.warn("preflight",`[npm-detect] Failed at ${n}: ${t instanceof Error?t.message:String(t)}`)}return o.error("preflight","[npm-detect] All paths failed, npm not found"),{found:!1,version:null}}return a("npm",["--version"])}async function P(){const e=await a("curl",["--version"]);return e.version?{found:!0,version:e.version.split(" ")[0]?.trim()??null}:e}async function y(){if(process.platform==="win32"){const n=["gh",c(process.env.ProgramFiles||"C:\\Program Files","GitHub CLI","gh.exe"),c(process.env["ProgramFiles(x86)"]||"C:\\Program Files (x86)","GitHub CLI","gh.exe")];for(const t of n)try{const{stdout:r}=await u(t,["--version"],{timeout:m,encoding:"utf-8"}),i=(r||"").trim();return{found:!0,version:i.match(/(\d+\.\d+\.\d+)/)?.[1]??(i.split(`
4
+ `)[0]?.trim()||null)}}catch{}return{found:!1,version:null}}const e=await a("gh",["--version"]);return e.version?{found:!0,version:e.version.match(/(\d+\.\d+\.\d+)/)?.[1]??e.version}:e}async function x(){const e=await a("brew",["--version"]);return e.version?{found:!0,version:e.version.match(/(\d+\.\d+\.\d+)/)?.[1]??e.version}:e}function C(e){return[{id:"node",label:"Node.js",minVersion:"18.0",detect:w},{id:"npm",label:"npm",minVersion:"9.0",detect:b},{id:"curl",label:"curl",minVersion:null,detect:P},{id:"gh",label:"GitHub CLI (gh)",minVersion:null,detect:y},...e!=="windows"?[{id:"brew",label:"Homebrew",minVersion:null,detect:x}]:[]]}async function I(e,n){const r=C(n).find(p=>p.id===e);if(!r)return{id:e,label:e,met:!1,version:null,minVersion:null,autoInstallable:!1};const{found:i,version:s}=await r.detect();let l=i;l&&r.minVersion&&s&&(l=g(s,r.minVersion));const d=V(e,n);return{id:e,label:r.label,met:l,version:s,minVersion:r.minVersion,autoInstallable:l?!1:d}}async function S(e,n){return await Promise.all(e.map(r=>I(r,n)))}function A(e){return e.every(n=>n.met)}function M(e){return e.filter(n=>!n.met)}function V(e,n){switch(e){case"node":case"npm":return!0;case"curl":return!0;case"gh":return!0;case"brew":return n==="macos";default:return!1}}export{A as allPrerequisitesMet,I as checkPrerequisite,S as checkPrerequisites,M as getMissingPrerequisites};
@@ -1 +1 @@
1
- function i(e,n){return{command:`npm install -g ${e}`,mode:"npm",npmPackage:e,timeoutMs:n?.timeoutMs??12e4,prerequisites:["node","npm"],minNodeVersion:n?.minNodeVersion}}function l(e,n){return{command:e,mode:"shell",timeoutMs:n?.timeoutMs??12e4,prerequisites:n?.prerequisites}}function r(e,n,s){return{command:e,mode:"exec",execArgs:n,timeoutMs:s?.timeoutMs??6e4,prerequisites:s?.prerequisites}}const o={claude:{cliBinary:"claude",macos:i("@anthropic-ai/claude-code",{minNodeVersion:"18.0"}),linux:i("@anthropic-ai/claude-code",{minNodeVersion:"18.0"}),windows:i("@anthropic-ai/claude-code",{minNodeVersion:"18.0"})},codex:{cliBinary:"codex",macos:{...l("curl -fsSL https://chatgpt.com/codex/install.sh | sh",{prerequisites:["curl"]}),fallback:i("@openai/codex")},linux:{...l("curl -fsSL https://chatgpt.com/codex/install.sh | sh",{prerequisites:["curl"]}),fallback:i("@openai/codex")},windows:{...l('powershell -ExecutionPolicy ByPass -c "irm https://chatgpt.com/codex/install.ps1 | iex"'),fallback:i("@openai/codex")}},gemini:{cliBinary:"gemini",macos:{...i("@google/gemini-cli",{minNodeVersion:"18.0"}),fallback:l("npx @google/gemini-cli --yes",{timeoutMs:12e4})},linux:i("@google/gemini-cli",{minNodeVersion:"18.0"}),windows:i("@google/gemini-cli",{minNodeVersion:"18.0"})},qwen:{cliBinary:"qwen",macos:{...l('bash -c "$(curl -fsSL https://qwen-code-assets.oss-cn-hangzhou.aliyuncs.com/installation/install-qwen.sh)"',{prerequisites:["curl"]}),fallback:i("@qwen-code/qwen-code@latest",{minNodeVersion:"22.0"})},linux:{...l('bash -c "$(curl -fsSL https://qwen-code-assets.oss-cn-hangzhou.aliyuncs.com/installation/install-qwen.sh)"',{prerequisites:["curl"]}),fallback:i("@qwen-code/qwen-code@latest",{minNodeVersion:"22.0"})},windows:{...l(`powershell -Command "Invoke-WebRequest 'https://qwen-code-assets.oss-cn-hangzhou.aliyuncs.com/installation/install-qwen.bat' -OutFile (Join-Path $env:TEMP 'install-qwen.bat'); & (Join-Path $env:TEMP 'install-qwen.bat')"`),fallback:i("@qwen-code/qwen-code@latest",{minNodeVersion:"22.0"})}},cursor:{cliBinary:"agent",macos:l("curl https://cursor.com/install -fsS | bash",{prerequisites:["curl"]}),linux:l("curl https://cursor.com/install -fsS | bash",{prerequisites:["curl"]}),windows:l("irm 'https://cursor.com/install?win32=true' | iex")},copilot:{cliBinary:"copilot",macos:r("gh",["extension","install","github/gh-copilot"],{prerequisites:["gh"]}),linux:r("gh",["extension","install","github/gh-copilot"],{prerequisites:["gh"]}),windows:r("gh",["extension","install","github/gh-copilot"],{prerequisites:["gh"]})},kiro:{cliBinary:"kiro-cli",macos:l("curl -fsSL https://cli.kiro.dev/install | bash",{prerequisites:["curl"]}),linux:l("curl -fsSL https://cli.kiro.dev/install | bash",{prerequisites:["curl"]}),windows:null},openclaw:{cliBinary:"openclaw",macos:i("openclaw@latest",{minNodeVersion:"22.0"}),linux:i("openclaw@latest",{minNodeVersion:"22.0"}),windows:i("openclaw@latest",{minNodeVersion:"22.0"})},reasonix:{cliBinary:"reasonix",macos:i("reasonix"),linux:i("reasonix"),windows:i("reasonix")},pi:{cliBinary:"pi",macos:i("@earendil-works/pi-coding-agent",{minNodeVersion:"22.19"}),linux:i("@earendil-works/pi-coding-agent",{minNodeVersion:"22.19"}),windows:i("@earendil-works/pi-coding-agent",{minNodeVersion:"22.19"})},agy:{cliBinary:"agy",macos:l("curl -fsSL https://antigravity.google/cli/install.sh | bash",{prerequisites:["curl"]}),linux:l("curl -fsSL https://antigravity.google/cli/install.sh | bash",{prerequisites:["curl"]}),windows:l('powershell -ExecutionPolicy ByPass -c "irm https://antigravity.google/cli/install.ps1 | iex"')},hermes:{cliBinary:"hermes",macos:null,linux:null,windows:null},codewhale:{cliBinary:"codewhale",macos:null,linux:null,windows:null},opencode:{cliBinary:"opencode",macos:null,linux:null,windows:null},openhuman:{cliBinary:"openhuman-core",macos:null,linux:null,windows:null}};function a(){switch(process.platform){case"darwin":return"macos";case"linux":return"linux";case"win32":return"windows";default:return"linux"}}function u(e,n){const s=o[e];return s?s[n]:null}function d(e){return o[e]?.cliBinary??null}function c(e,n){const s=o[e];if(!s)return null;const t=s[n];return{agentType:e,cliBinary:s.cliBinary,supported:t!==null,installCommand:t?.command??null,prerequisites:t?.prerequisites}}function m(e){return Object.keys(o).sort().map(n=>c(n,e)).filter(n=>n!==null)}function p(e){return e in o}export{a as detectPlatformOS,c as getAgentInstallInfo,m as getAllAgentInstallInfo,d as getCliBinary,u as getInstallCommand,p as isKnownAgent};
1
+ function i(n,s){return{command:`npm install -g ${n}`,mode:"npm",npmPackage:n,timeoutMs:s?.timeoutMs??12e4,prerequisites:["node","npm"],minNodeVersion:s?.minNodeVersion}}function e(n,s){return{command:n,mode:"shell",timeoutMs:s?.timeoutMs??12e4,prerequisites:s?.prerequisites}}function r(n,s,o){return{command:n,mode:"exec",execArgs:s,timeoutMs:o?.timeoutMs??6e4,prerequisites:o?.prerequisites,skipVerification:o?.skipVerification}}const l={claude:{cliBinary:"claude",macos:i("@anthropic-ai/claude-code",{minNodeVersion:"18.0"}),linux:i("@anthropic-ai/claude-code",{minNodeVersion:"18.0"}),windows:i("@anthropic-ai/claude-code",{minNodeVersion:"18.0"})},codex:{cliBinary:"codex",macos:{...e("curl -fsSL https://chatgpt.com/codex/install.sh | sh",{prerequisites:["curl"]}),fallback:i("@openai/codex")},linux:{...e("curl -fsSL https://chatgpt.com/codex/install.sh | sh",{prerequisites:["curl"]}),fallback:i("@openai/codex")},windows:{...e('powershell -ExecutionPolicy ByPass -c "irm https://chatgpt.com/codex/install.ps1 | iex"'),fallback:i("@openai/codex")}},gemini:{cliBinary:"gemini",macos:{...i("@google/gemini-cli",{minNodeVersion:"18.0"}),fallback:e("npx @google/gemini-cli --yes",{timeoutMs:12e4})},linux:i("@google/gemini-cli",{minNodeVersion:"18.0"}),windows:i("@google/gemini-cli",{minNodeVersion:"18.0"})},qwen:{cliBinary:"qwen",macos:{...e('bash -c "$(curl -fsSL https://qwen-code-assets.oss-cn-hangzhou.aliyuncs.com/installation/install-qwen.sh)"',{prerequisites:["curl"]}),fallback:i("@qwen-code/qwen-code@latest",{minNodeVersion:"22.0"})},linux:{...e('bash -c "$(curl -fsSL https://qwen-code-assets.oss-cn-hangzhou.aliyuncs.com/installation/install-qwen.sh)"',{prerequisites:["curl"]}),fallback:i("@qwen-code/qwen-code@latest",{minNodeVersion:"22.0"})},windows:{...e(`powershell -Command "Invoke-WebRequest 'https://qwen-code-assets.oss-cn-hangzhou.aliyuncs.com/installation/install-qwen.bat' -OutFile (Join-Path $env:TEMP 'install-qwen.bat'); & (Join-Path $env:TEMP 'install-qwen.bat')"`),fallback:i("@qwen-code/qwen-code@latest",{minNodeVersion:"22.0"})}},cursor:{cliBinary:"agent",macos:e("curl https://cursor.com/install -fsS | bash",{prerequisites:["curl"]}),linux:e("curl https://cursor.com/install -fsS | bash",{prerequisites:["curl"]}),windows:e("irm 'https://cursor.com/install?win32=true' | iex")},copilot:{cliBinary:"copilot",macos:r("gh",["extension","install","github/gh-copilot"],{prerequisites:["gh"],skipVerification:!0}),linux:r("gh",["extension","install","github/gh-copilot"],{prerequisites:["gh"],skipVerification:!0}),windows:r("gh",["extension","install","github/gh-copilot"],{prerequisites:["gh"],skipVerification:!0})},kiro:{cliBinary:"kiro-cli",macos:e("curl -fsSL https://cli.kiro.dev/install | bash",{prerequisites:["curl"]}),linux:e("curl -fsSL https://cli.kiro.dev/install | bash",{prerequisites:["curl"]}),windows:null},openclaw:{cliBinary:"openclaw",macos:i("openclaw@latest",{minNodeVersion:"22.0"}),linux:i("openclaw@latest",{minNodeVersion:"22.0"}),windows:i("openclaw@latest",{minNodeVersion:"22.0"})},reasonix:{cliBinary:"reasonix",macos:i("reasonix"),linux:i("reasonix"),windows:i("reasonix")},pi:{cliBinary:"pi",macos:i("@earendil-works/pi-coding-agent",{minNodeVersion:"22.19"}),linux:i("@earendil-works/pi-coding-agent",{minNodeVersion:"22.19"}),windows:i("@earendil-works/pi-coding-agent",{minNodeVersion:"22.19"})},agy:{cliBinary:"agy",macos:e("curl -fsSL https://antigravity.google/cli/install.sh | bash",{prerequisites:["curl"]}),linux:e("curl -fsSL https://antigravity.google/cli/install.sh | bash",{prerequisites:["curl"]}),windows:e('powershell -ExecutionPolicy ByPass -c "irm https://antigravity.google/cli/install.ps1 | iex"')},hermes:{cliBinary:"hermes",macos:null,linux:null,windows:null},codewhale:{cliBinary:"codewhale",macos:i("codewhale"),linux:i("codewhale"),windows:i("codewhale")},opencode:{cliBinary:"opencode",macos:{...e("curl -fsSL https://opencode.ai/install | bash",{prerequisites:["curl"]}),fallback:i("opencode-ai")},linux:{...e("curl -fsSL https://opencode.ai/install | bash",{prerequisites:["curl"]}),fallback:i("opencode-ai")},windows:i("opencode-ai")},openhuman:{cliBinary:"openhuman-core",macos:e("curl -fsSL https://raw.githubusercontent.com/tinyhumansai/openhuman/main/scripts/install.sh | bash",{prerequisites:["curl"]}),linux:e("curl -fsSL https://raw.githubusercontent.com/tinyhumansai/openhuman/main/scripts/install.sh | bash",{prerequisites:["curl"]}),windows:null}};function c(){switch(process.platform){case"darwin":return"macos";case"linux":return"linux";case"win32":return"windows";default:return"linux"}}function u(n,s){const o=l[n];return o?o[s]:null}function p(n){return l[n]?.cliBinary??null}function a(n,s){const o=l[n];if(!o)return null;const t=o[s];return{agentType:n,cliBinary:o.cliBinary,supported:t!==null,installCommand:t?.command??null,prerequisites:t?.prerequisites}}function d(n){return Object.keys(l).sort().map(s=>a(s,n)).filter(s=>s!==null)}function m(n){return n in l}export{c as detectPlatformOS,a as getAgentInstallInfo,d as getAllAgentInstallInfo,p as getCliBinary,u as getInstallCommand,m as isKnownAgent};
@@ -1 +1 @@
1
- import*as i from"@sentry/node";import{log as s}from"../log/logger.js";import{resolveClientVersion as c}from"../util/client-version.js";let a=!1;const p="https://e8e202d7625372b1314b3ff4e85a7ff9@o119262.ingest.us.sentry.io/4511410543329280";function f(){if(!process.env.GRIX_SENTRY_DISABLE)return process.env.SENTRY_DSN||process.env.GRIX_SENTRY_DSN||p}function T(){const e=f();if(!e){s.info("sentry","Sentry \u9519\u8BEF\u4E0A\u62A5\u5DF2\u7981\u7528\uFF08GRIX_SENTRY_DISABLE\uFF09");return}try{i.init({dsn:e,release:`grix-connector@${c()}`,environment:process.env.SENTRY_ENVIRONMENT||process.env.NODE_ENV||"production",sendDefaultPii:!1,initialScope:{tags:{component:"grix-connector"}},defaultIntegrations:!1,tracesSampleRate:0}),a=!0,s.info("sentry","Sentry \u9519\u8BEF\u4E0A\u62A5\u5DF2\u542F\u7528")}catch(n){s.error("sentry",`Sentry \u521D\u59CB\u5316\u5931\u8D25: ${n instanceof Error?n.message:String(n)}`)}}function g(){return a}const E=new Set(["INSTALL_FAILED","INSTALL_TIMEOUT","FALLBACK_EXHAUSTED","PREREQ_INSTALL_FAILED","VERIFICATION_FAILED","ENVIRONMENT_UNSUPPORTED","INTERNAL"]);function S(e){if(e.ok)return!1;const n=e.error?.code;return!!n&&E.has(n)}function I(e){if(!a||!S(e))return;const n=e.error?.code??"INTERNAL",t=e.error?.message??"unknown install failure",r=e.environment;try{i.withScope(o=>{o.setLevel("error"),o.setTags({agent_type:e.agentType,error_code:n,phase:e.phase}),o.setContext("install",{agentType:e.agentType,code:n,phase:e.phase,durationMs:e.durationMs,os:r?.platform,osVersion:r?.osVersion,arch:r?.arch,nodeVersion:r?.nodeVersion,npmVersion:r?.npmVersion,isDocker:r?.isDocker,isCI:r?.isCI,outputTail:(e.output??"").slice(-2e3)}),o.setFingerprint(["agent-install-failure",e.agentType,n]),i.captureException(new Error(`Agent install failed [${e.agentType}/${n}]: ${t}`))}),s.info("sentry",`\u5DF2\u4E0A\u62A5\u5B89\u88C5\u5931\u8D25: ${e.agentType}/${n}`)}catch(o){s.error("sentry",`\u4E0A\u62A5\u5B89\u88C5\u5931\u8D25\u65F6\u51FA\u9519: ${o instanceof Error?o.message:String(o)}`)}}function N(e,n){if(a)try{i.withScope(t=>{t.setLevel("fatal"),t.setTag("crash_type",n);const r=e instanceof Error?e:new Error(String(e));i.captureException(r)}),s.info("sentry",`\u5DF2\u4E0A\u62A5\u5D29\u6E83: ${n}`)}catch(t){s.error("sentry",`\u4E0A\u62A5\u5D29\u6E83\u65F6\u51FA\u9519: ${t instanceof Error?t.message:String(t)}`)}}async function u(e=2e3){if(a)try{await i.close(e)}catch{}}export{u as closeSentry,T as initSentry,g as isSentryEnabled,N as reportFatal,I as reportInstallFailure,S as shouldReportInstallFailure};
1
+ import{log as i}from"../log/logger.js";import{resolveClientVersion as c}from"../util/client-version.js";let r=null,a=!1;const p="https://e8e202d7625372b1314b3ff4e85a7ff9@o119262.ingest.us.sentry.io/4511410543329280";function f(){if(!process.env.GRIX_SENTRY_DISABLE)return process.env.SENTRY_DSN||process.env.GRIX_SENTRY_DSN||p}async function g(){const e=f();if(!e){i.info("sentry","Sentry \u9519\u8BEF\u4E0A\u62A5\u5DF2\u7981\u7528\uFF08GRIX_SENTRY_DISABLE\uFF09");return}try{r=await import("@sentry/node")}catch(n){i.warn("sentry",`Sentry \u6A21\u5757\u52A0\u8F7D\u5931\u8D25\uFF08\u5E73\u53F0\u4E0D\u517C\u5BB9\uFF09\uFF0C\u9519\u8BEF\u4E0A\u62A5\u5DF2\u7981\u7528: ${n instanceof Error?n.message:String(n)}`);return}try{r.init({dsn:e,release:`grix-connector@${c()}`,environment:process.env.SENTRY_ENVIRONMENT||process.env.NODE_ENV||"production",sendDefaultPii:!1,initialScope:{tags:{component:"grix-connector"}},defaultIntegrations:!1,tracesSampleRate:0}),a=!0,i.info("sentry","Sentry \u9519\u8BEF\u4E0A\u62A5\u5DF2\u542F\u7528")}catch(n){i.error("sentry",`Sentry \u521D\u59CB\u5316\u5931\u8D25: ${n instanceof Error?n.message:String(n)}`)}}function T(){return a}const E=new Set(["INSTALL_FAILED","INSTALL_TIMEOUT","FALLBACK_EXHAUSTED","PREREQ_INSTALL_FAILED","VERIFICATION_FAILED","ENVIRONMENT_UNSUPPORTED","INTERNAL"]);function S(e){if(e.ok)return!1;const n=e.error?.code;return!!n&&E.has(n)}function I(e){if(!a||!r||!S(e))return;const n=e.error?.code??"INTERNAL",o=e.error?.message??"unknown install failure",t=e.environment;try{r.withScope(s=>{s.setLevel("error"),s.setTags({agent_type:e.agentType,error_code:n,phase:e.phase}),s.setContext("install",{agentType:e.agentType,code:n,phase:e.phase,durationMs:e.durationMs,os:t?.platform,osVersion:t?.osVersion,arch:t?.arch,nodeVersion:t?.nodeVersion,npmVersion:t?.npmVersion,isDocker:t?.isDocker,isCI:t?.isCI,outputTail:(e.output??"").slice(-2e3)}),s.setFingerprint(["agent-install-failure",e.agentType,n]),r.captureException(new Error(`Agent install failed [${e.agentType}/${n}]: ${o}`))}),i.info("sentry",`\u5DF2\u4E0A\u62A5\u5B89\u88C5\u5931\u8D25: ${e.agentType}/${n}`)}catch(s){i.error("sentry",`\u4E0A\u62A5\u5B89\u88C5\u5931\u8D25\u65F6\u51FA\u9519: ${s instanceof Error?s.message:String(s)}`)}}function N(e,n){if(!(!a||!r))try{r.withScope(o=>{o.setLevel("fatal"),o.setTag("crash_type",n);const t=e instanceof Error?e:new Error(String(e));r.captureException(t)}),i.info("sentry",`\u5DF2\u4E0A\u62A5\u5D29\u6E83: ${n}`)}catch(o){i.error("sentry",`\u4E0A\u62A5\u5D29\u6E83\u65F6\u51FA\u9519: ${o instanceof Error?o.message:String(o)}`)}}async function u(e=2e3){if(!(!a||!r))try{await r.close(e)}catch{}}export{u as closeSentry,g as initSentry,T as isSentryEnabled,N as reportFatal,I as reportInstallFailure,S as shouldReportInstallFailure};
@@ -1 +1 @@
1
- import{readJSONFile as c,writeJSONFileAtomic as l}from"../util/json-file.js";import{log as g}from"../log/index.js";import{SESSION_MODE_IDS as s}from"../../adapter/claude/protocol-contract.js";const a=s.fullAuto;function n(r){const e=String(r??"").trim().toLowerCase();return e===s.approval||e===s.fullAuto?e:a}class x{bindings=new Map;filePath;writePromise=Promise.resolve();constructor(e){this.filePath=e??null}load(){if(!this.filePath)return;const e=c(this.filePath);if(Array.isArray(e)){this.bindings.clear();for(const d of e)d.aibotSessionId&&this.bindings.set(d.aibotSessionId,{...d,modeId:n(d.modeId)})}}set(e,d,t){const i=this.bindings.get(e),o=n(t?.modeId??i?.modeId);this.bindings.set(e,{aibotSessionId:e,cwd:d,acpSessionId:i?.acpSessionId,claudeSessionId:i?.claudeSessionId,codexThreadId:i?.codexThreadId,codexModelId:i?.codexModelId,codexModeId:i?.codexModeId,codewhaleThreadId:i?.codewhaleThreadId,codexReasoningEffort:i?.codexReasoningEffort,codexSandboxMode:i?.codexSandboxMode,piSessionPath:i?.piSessionPath,modeId:o,modelId:i?.modelId,acpModelId:i?.acpModelId,acpModeId:i?.acpModeId,updatedAt:Date.now()}),this.scheduleWrite()}setAcpSessionId(e,d){this.updateBinding(e,{acpSessionId:d})}setClaudeSessionId(e,d){this.updateBinding(e,{claudeSessionId:d})}getClaudeSessionId(e){return this.bindings.get(e)?.claudeSessionId}setCodexThreadId(e,d){this.updateBinding(e,{codexThreadId:d})}getCodexThreadId(e){return this.bindings.get(e)?.codexThreadId}setCodexContext(e,d){this.updateBinding(e,{codexModelId:d.modelId,codexModeId:d.modeId,codexReasoningEffort:d.reasoningEffort,codexSandboxMode:d.sandboxMode})}getCodexModelId(e){return this.bindings.get(e)?.codexModelId}getCodexModeId(e){return this.bindings.get(e)?.codexModeId}getCodexReasoningEffort(e){return this.bindings.get(e)?.codexReasoningEffort}getCodexSandboxMode(e){return this.bindings.get(e)?.codexSandboxMode}setPiSessionPath(e,d){this.updateBinding(e,{piSessionPath:d})}getPiSessionPath(e){return this.bindings.get(e)?.piSessionPath}setCodeWhaleThreadId(e,d){this.updateBinding(e,{codewhaleThreadId:d})}getCodeWhaleThreadId(e){return this.bindings.get(e)?.codewhaleThreadId}setModeId(e,d){const t=this.bindings.get(e);if(!t)return;const i=n(d);t.modeId!==i&&(this.bindings.set(e,{...t,modeId:i,updatedAt:Date.now()}),this.scheduleWrite())}ensureModeId(e,d=a){const t=this.bindings.get(e);t&&(t.modeId||(this.bindings.set(e,{...t,modeId:n(d),updatedAt:Date.now()}),this.scheduleWrite()))}getAcpSessionId(e){return this.bindings.get(e)?.acpSessionId}getModeId(e){const d=this.bindings.get(e)?.modeId;return d?n(d):void 0}setModelId(e,d){const t=this.bindings.get(e);if(!t)return;const i=d.trim();t.modelId!==i&&(this.bindings.set(e,{...t,modelId:i,updatedAt:Date.now()}),this.scheduleWrite())}getModelId(e){return this.bindings.get(e)?.modelId}setAcpModelId(e,d){const t=this.bindings.get(e);if(!t)return;const i=String(d??"").trim(),o=i===""?void 0:i;t.acpModelId!==o&&(this.bindings.set(e,{...t,acpModelId:o,updatedAt:Date.now()}),this.scheduleWrite())}getAcpModelId(e){return this.bindings.get(e)?.acpModelId}setAcpModeId(e,d){const t=this.bindings.get(e);if(!t)return;const i=String(d??"").trim(),o=i===""?void 0:i;t.acpModeId!==o&&(this.bindings.set(e,{...t,acpModeId:o,updatedAt:Date.now()}),this.scheduleWrite())}getAcpModeId(e){return this.bindings.get(e)?.acpModeId}get(e){return this.bindings.get(e)}getMostRecentlyUpdatedSessionId(e){const d=e?.requireCwd!==!1;let t,i=-1;for(const o of this.bindings.values())d&&!String(o.cwd??"").trim()||o.updatedAt>i&&(i=o.updatedAt,t=o.aibotSessionId);return t}delete(e){this.bindings.delete(e),this.scheduleWrite()}entries(){return this.bindings.entries()}async flush(){await this.writePromise}scheduleWrite(){this.filePath&&(this.writePromise=this.writePromise.then(()=>this.write()).catch(e=>{g.error("session-binding-store",`Persist failed: ${e instanceof Error?e.message:e}`)}))}async write(){if(!this.filePath)return;const e=Array.from(this.bindings.values());await l(this.filePath,e)}updateBinding(e,d){const t=this.bindings.get(e),i={aibotSessionId:e,acpSessionId:Object.prototype.hasOwnProperty.call(d,"acpSessionId")?d.acpSessionId:t?.acpSessionId,claudeSessionId:Object.prototype.hasOwnProperty.call(d,"claudeSessionId")?d.claudeSessionId:t?.claudeSessionId,codexThreadId:Object.prototype.hasOwnProperty.call(d,"codexThreadId")?d.codexThreadId:t?.codexThreadId,codexModelId:Object.prototype.hasOwnProperty.call(d,"codexModelId")?d.codexModelId:t?.codexModelId,codexModeId:Object.prototype.hasOwnProperty.call(d,"codexModeId")?d.codexModeId:t?.codexModeId,codewhaleThreadId:Object.prototype.hasOwnProperty.call(d,"codewhaleThreadId")?d.codewhaleThreadId:t?.codewhaleThreadId,codexReasoningEffort:Object.prototype.hasOwnProperty.call(d,"codexReasoningEffort")?d.codexReasoningEffort:t?.codexReasoningEffort,codexSandboxMode:Object.prototype.hasOwnProperty.call(d,"codexSandboxMode")?d.codexSandboxMode:t?.codexSandboxMode,piSessionPath:Object.prototype.hasOwnProperty.call(d,"piSessionPath")?d.piSessionPath:t?.piSessionPath,cwd:t?.cwd,modeId:n(t?.modeId),modelId:t?.modelId,acpModelId:t?.acpModelId,acpModeId:t?.acpModeId,updatedAt:Date.now()};this.bindings.set(e,i),this.scheduleWrite()}}export{x as SessionBindingStore};
1
+ import{readJSONFile as c,writeJSONFileAtomic as g}from"../util/json-file.js";import{log as l}from"../log/index.js";import{SESSION_MODE_IDS as s}from"../../adapter/claude/protocol-contract.js";const a=s.fullAuto;function n(r){const e=String(r??"").trim().toLowerCase();return e===s.approval||e===s.fullAuto?e:a}class p{bindings=new Map;filePath;writePromise=Promise.resolve();constructor(e){this.filePath=e??null}load(){if(!this.filePath)return;const e=c(this.filePath);if(Array.isArray(e)){this.bindings.clear();for(const d of e)d.aibotSessionId&&this.bindings.set(d.aibotSessionId,{...d,modeId:n(d.modeId)})}}set(e,d,t){const i=this.bindings.get(e),o=n(t?.modeId??i?.modeId);this.bindings.set(e,{aibotSessionId:e,cwd:d,acpSessionId:i?.acpSessionId,claudeSessionId:i?.claudeSessionId,codexThreadId:i?.codexThreadId,codexModelId:i?.codexModelId,codexModeId:i?.codexModeId,codewhaleThreadId:i?.codewhaleThreadId,agyConversationId:i?.agyConversationId,codexReasoningEffort:i?.codexReasoningEffort,codexSandboxMode:i?.codexSandboxMode,piSessionPath:i?.piSessionPath,modeId:o,modelId:i?.modelId,acpModelId:i?.acpModelId,acpModeId:i?.acpModeId,updatedAt:Date.now()}),this.scheduleWrite()}setAcpSessionId(e,d){this.updateBinding(e,{acpSessionId:d})}setClaudeSessionId(e,d){this.updateBinding(e,{claudeSessionId:d})}getClaudeSessionId(e){return this.bindings.get(e)?.claudeSessionId}setCodexThreadId(e,d){this.updateBinding(e,{codexThreadId:d})}getCodexThreadId(e){return this.bindings.get(e)?.codexThreadId}setAgyConversationId(e,d){this.updateBinding(e,{agyConversationId:d})}getAgyConversationId(e){return this.bindings.get(e)?.agyConversationId}setCodexContext(e,d){this.updateBinding(e,{codexModelId:d.modelId,codexModeId:d.modeId,codexReasoningEffort:d.reasoningEffort,codexSandboxMode:d.sandboxMode})}getCodexModelId(e){return this.bindings.get(e)?.codexModelId}getCodexModeId(e){return this.bindings.get(e)?.codexModeId}getCodexReasoningEffort(e){return this.bindings.get(e)?.codexReasoningEffort}getCodexSandboxMode(e){return this.bindings.get(e)?.codexSandboxMode}setPiSessionPath(e,d){this.updateBinding(e,{piSessionPath:d})}getPiSessionPath(e){return this.bindings.get(e)?.piSessionPath}setCodeWhaleThreadId(e,d){this.updateBinding(e,{codewhaleThreadId:d})}getCodeWhaleThreadId(e){return this.bindings.get(e)?.codewhaleThreadId}setModeId(e,d){const t=this.bindings.get(e);if(!t)return;const i=n(d);t.modeId!==i&&(this.bindings.set(e,{...t,modeId:i,updatedAt:Date.now()}),this.scheduleWrite())}ensureModeId(e,d=a){const t=this.bindings.get(e);t&&(t.modeId||(this.bindings.set(e,{...t,modeId:n(d),updatedAt:Date.now()}),this.scheduleWrite()))}getAcpSessionId(e){return this.bindings.get(e)?.acpSessionId}getModeId(e){const d=this.bindings.get(e)?.modeId;return d?n(d):void 0}setModelId(e,d){const t=this.bindings.get(e);if(!t)return;const i=d.trim();t.modelId!==i&&(this.bindings.set(e,{...t,modelId:i,updatedAt:Date.now()}),this.scheduleWrite())}getModelId(e){return this.bindings.get(e)?.modelId}setAcpModelId(e,d){const t=this.bindings.get(e);if(!t)return;const i=String(d??"").trim(),o=i===""?void 0:i;t.acpModelId!==o&&(this.bindings.set(e,{...t,acpModelId:o,updatedAt:Date.now()}),this.scheduleWrite())}getAcpModelId(e){return this.bindings.get(e)?.acpModelId}setAcpModeId(e,d){const t=this.bindings.get(e);if(!t)return;const i=String(d??"").trim(),o=i===""?void 0:i;t.acpModeId!==o&&(this.bindings.set(e,{...t,acpModeId:o,updatedAt:Date.now()}),this.scheduleWrite())}getAcpModeId(e){return this.bindings.get(e)?.acpModeId}get(e){return this.bindings.get(e)}getMostRecentlyUpdatedSessionId(e){const d=e?.requireCwd!==!1;let t,i=-1;for(const o of this.bindings.values())d&&!String(o.cwd??"").trim()||o.updatedAt>i&&(i=o.updatedAt,t=o.aibotSessionId);return t}delete(e){this.bindings.delete(e),this.scheduleWrite()}entries(){return this.bindings.entries()}async flush(){await this.writePromise}scheduleWrite(){this.filePath&&(this.writePromise=this.writePromise.then(()=>this.write()).catch(e=>{l.error("session-binding-store",`Persist failed: ${e instanceof Error?e.message:e}`)}))}async write(){if(!this.filePath)return;const e=Array.from(this.bindings.values());await g(this.filePath,e)}updateBinding(e,d){const t=this.bindings.get(e),i={aibotSessionId:e,acpSessionId:Object.prototype.hasOwnProperty.call(d,"acpSessionId")?d.acpSessionId:t?.acpSessionId,claudeSessionId:Object.prototype.hasOwnProperty.call(d,"claudeSessionId")?d.claudeSessionId:t?.claudeSessionId,codexThreadId:Object.prototype.hasOwnProperty.call(d,"codexThreadId")?d.codexThreadId:t?.codexThreadId,codexModelId:Object.prototype.hasOwnProperty.call(d,"codexModelId")?d.codexModelId:t?.codexModelId,codexModeId:Object.prototype.hasOwnProperty.call(d,"codexModeId")?d.codexModeId:t?.codexModeId,codewhaleThreadId:Object.prototype.hasOwnProperty.call(d,"codewhaleThreadId")?d.codewhaleThreadId:t?.codewhaleThreadId,agyConversationId:Object.prototype.hasOwnProperty.call(d,"agyConversationId")?d.agyConversationId:t?.agyConversationId,codexReasoningEffort:Object.prototype.hasOwnProperty.call(d,"codexReasoningEffort")?d.codexReasoningEffort:t?.codexReasoningEffort,codexSandboxMode:Object.prototype.hasOwnProperty.call(d,"codexSandboxMode")?d.codexSandboxMode:t?.codexSandboxMode,piSessionPath:Object.prototype.hasOwnProperty.call(d,"piSessionPath")?d.piSessionPath:t?.piSessionPath,cwd:t?.cwd,modeId:n(t?.modeId),modelId:t?.modelId,acpModelId:t?.acpModelId,acpModeId:t?.acpModeId,updatedAt:Date.now()};this.bindings.set(e,i),this.scheduleWrite()}}export{p as SessionBindingStore};
@@ -1,2 +1,2 @@
1
- import{execFileSync as p}from"node:child_process";import{existsSync as c,readFileSync as m,renameSync as _,statfsSync as S,unlinkSync as E,writeFileSync as N}from"node:fs";import{join as u}from"node:path";import{GRIX_PATHS as f}from"../log/index.js";import{appendRotatingFileSync as I}from"../log/rotation.js";import{resolveClientVersion as w}from"../util/client-version.js";import{npmInstallWithMirror as y}from"../installer/npm-registry.js";class i extends Error{code;constructor(r,n){super(n),this.name="UpgradeError",this.code=r}}function d(){return u(f.log,"upgrade.log")}function a(){return u(f.data,"upgrade-pending.json")}function T(){return c(a())}function k(){const e=a();if(!c(e))return null;try{return JSON.parse(m(e,"utf-8"))}catch{return null}}function $(e,r){const n={from_version:e,target_version:r,upgraded_at:new Date().toISOString(),crash_count:0},s=a(),o=s+".tmp";N(o,JSON.stringify(n),"utf-8"),_(o,s)}function b(){const e=a();if(c(e))try{E(e)}catch{}}function l(e){const r=`[${new Date().toISOString()}] ${e}
2
- `;try{I(d(),r)}catch{}}function O(e=4096){const r=d();if(!c(r))return"";try{const n=m(r,"utf-8");return n.length<=e?n:n.slice(-e)}catch{return""}}function g(){try{const e=process.platform==="win32";return p(e?"cmd.exe":"npm",e?["/c","npm","--version"]:["--version"],{encoding:"utf-8",timeout:1e4}).trim()}catch{throw new i("NPM_NOT_FOUND","npm is not available or timed out")}}function h(){let e;try{const r=process.platform==="win32";e=p(r?"cmd.exe":"npm",r?["/c","npm","prefix","-g"]:["prefix","-g"],{encoding:"utf-8",timeout:1e4}).trim()}catch{return Number.MAX_SAFE_INTEGER}try{if(process.platform==="win32")return Number.MAX_SAFE_INTEGER;const r=S(e);return Math.floor(r.bsize*r.bavail/(1024*1024))}catch{return Number.MAX_SAFE_INTEGER}}function D(){let e;try{e=g()}catch(n){return{ok:!1,errorCode:"NPM_NOT_FOUND",errorMsg:n instanceof Error?n.message:"npm not available"}}const r=h();return r<100?{ok:!1,errorCode:"DISK_FULL",errorMsg:`Only ${r}MB free disk space (need >= 100MB)`,npmVersion:e,diskFreeMb:r}:{ok:!0,npmVersion:e,diskFreeMb:r}}function U(){let e="";try{e=g()}catch{}let r=Number.MAX_SAFE_INTEGER;try{r=h()}catch{}return{npm_version:e,node_version:process.version,disk_free_mb:r,platform:process.platform,arch:process.arch}}async function C(e,r,n=12e4){const s=`${e}@${r}`;l(`npm install -g ${s} starting (with mirror fallback)`);try{const{registry:o}=await y(s,n,10485760);l(`npm install succeeded via ${o}`)}catch(o){const t=o instanceof Error?o.message:String(o);throw l(`npm install failed: ${t}`),/timed out|ETIMEDOUT/i.test(t)?new i("NPM_TIMEOUT",`npm install timed out after ${n/1e3}s (tried all mirrors): ${t}`):t.includes("EACCES")||t.includes("permission denied")?new i("NPM_INSTALL_FAILED",`Permission denied: ${t}`):t.includes("ENOSPC")||t.includes("no space left")?new i("DISK_FULL",`Disk full: ${t}`):t.includes("404")||t.includes("not found")?new i("NPM_INSTALL_FAILED",`Package not found: ${t}`):new i("NPM_INSTALL_FAILED",t)}}function R(e){const r=w();if(r!==e)throw new i("VERSION_MISMATCH",`Installed version ${r} does not match expected ${e}`);return r}export{i as UpgradeError,h as checkDiskSpace,g as checkNpmAvailable,U as collectEnvInfo,O as getUpgradeLogTail,C as npmInstall,T as pendingExists,D as preflightCheck,k as readPending,b as removePending,l as upgradeLog,R as verifyInstalledVersion,$ as writePending};
1
+ import{execFileSync as p}from"node:child_process";import{existsSync as s,readFileSync as m,renameSync as _,unlinkSync as S,writeFileSync as E}from"node:fs";import{join as u}from"node:path";import{GRIX_PATHS as f}from"../log/index.js";import{appendRotatingFileSync as N}from"../log/rotation.js";import{resolveClientVersion as I}from"../util/client-version.js";import{npmInstallWithMirror as w}from"../installer/npm-registry.js";class i extends Error{code;constructor(r,n){super(n),this.name="UpgradeError",this.code=r}}function d(){return u(f.log,"upgrade.log")}function a(){return u(f.data,"upgrade-pending.json")}function P(){return s(a())}function k(){const e=a();if(!s(e))return null;try{return JSON.parse(m(e,"utf-8"))}catch{return null}}function T(e,r){const n={from_version:e,target_version:r,upgraded_at:new Date().toISOString(),crash_count:0},c=a(),o=c+".tmp";E(o,JSON.stringify(n),"utf-8"),_(o,c)}function $(){const e=a();if(s(e))try{S(e)}catch{}}function l(e){const r=`[${new Date().toISOString()}] ${e}
2
+ `;try{N(d(),r)}catch{}}function O(e=4096){const r=d();if(!s(r))return"";try{const n=m(r,"utf-8");return n.length<=e?n:n.slice(-e)}catch{return""}}function g(){try{const e=process.platform==="win32";return p(e?"cmd.exe":"npm",e?["/c","npm","--version"]:["--version"],{encoding:"utf-8",timeout:1e4}).trim()}catch{throw new i("NPM_NOT_FOUND","npm is not available or timed out")}}function h(){let e;try{const r=process.platform==="win32";e=p(r?"cmd.exe":"npm",r?["/c","npm","prefix","-g"]:["prefix","-g"],{encoding:"utf-8",timeout:1e4}).trim()}catch{return Number.MAX_SAFE_INTEGER}return Number.MAX_SAFE_INTEGER}function D(){let e;try{e=g()}catch(n){return{ok:!1,errorCode:"NPM_NOT_FOUND",errorMsg:n instanceof Error?n.message:"npm not available"}}const r=h();return r<100?{ok:!1,errorCode:"DISK_FULL",errorMsg:`Only ${r}MB free disk space (need >= 100MB)`,npmVersion:e,diskFreeMb:r}:{ok:!0,npmVersion:e,diskFreeMb:r}}function b(){let e="";try{e=g()}catch{}let r=Number.MAX_SAFE_INTEGER;try{r=h()}catch{}return{npm_version:e,node_version:process.version,disk_free_mb:r,platform:process.platform,arch:process.arch}}async function U(e,r,n=12e4){const c=`${e}@${r}`;l(`npm install -g ${c} starting (with mirror fallback)`);try{const{registry:o}=await w(c,n,10485760);l(`npm install succeeded via ${o}`)}catch(o){const t=o instanceof Error?o.message:String(o);throw l(`npm install failed: ${t}`),/timed out|ETIMEDOUT/i.test(t)?new i("NPM_TIMEOUT",`npm install timed out after ${n/1e3}s (tried all mirrors): ${t}`):t.includes("EACCES")||t.includes("permission denied")?new i("NPM_INSTALL_FAILED",`Permission denied: ${t}`):t.includes("ENOSPC")||t.includes("no space left")?new i("DISK_FULL",`Disk full: ${t}`):t.includes("404")||t.includes("not found")?new i("NPM_INSTALL_FAILED",`Package not found: ${t}`):new i("NPM_INSTALL_FAILED",t)}}function C(e){const r=I();if(r!==e)throw new i("VERSION_MISMATCH",`Installed version ${r} does not match expected ${e}`);return r}export{i as UpgradeError,h as checkDiskSpace,g as checkNpmAvailable,b as collectEnvInfo,O as getUpgradeLogTail,U as npmInstall,P as pendingExists,D as preflightCheck,k as readPending,$ as removePending,l as upgradeLog,C as verifyInstalledVersion,T as writePending};
package/dist/grix.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import g from"node:path";import{writeFileSync as x}from"node:fs";import{Manager as $}from"./manager.js";import{ensureGrixDirs as L,initLogger as N,log as a,installProcessLogRotation as b,setConsoleOutput as C}from"./core/log/index.js";import{HealthServer as U}from"./core/runtime/index.js";import{writePidFile as H,removePidFile as h}from"./core/runtime/index.js";import{resolveRuntimePaths as v}from"./core/config/index.js";import{ServiceManager as j}from"./service/service-manager.js";import{killProcessesByCommandLine as _,isWindowsElevated as G}from"./service/process-control.js";import{acquireDaemonLock as W,releaseDaemonLock as w}from"./runtime/daemon-lock.js";import{writeDaemonStatus as k,removeDaemonStatus as M}from"./runtime/service-state.js";import{AdminServer as B,generateToken as V,writeTokenFile as X}from"./core/admin/index.js";import{initSentry as q,closeSentry as P,reportFatal as E}from"./core/observability/sentry.js";const c=process.argv.slice(2),A=[],s={};for(let t=0;t<c.length;t++)c[t].startsWith("--")&&c[t+1]&&!c[t+1].startsWith("--")?(s[c[t].slice(2)]=c[t+1],t++):c[t].startsWith("--")?s[c[t].slice(2)]="true":A.push(c[t]);s.help&&(console.log(`grix-connector \u2014 Unified AI Agent Bridge
2
+ import g from"node:path";import{writeFileSync as x}from"node:fs";import{Manager as $}from"./manager.js";import{ensureGrixDirs as L,initLogger as N,log as n,installProcessLogRotation as b,setConsoleOutput as C}from"./core/log/index.js";import{HealthServer as U}from"./core/runtime/index.js";import{writePidFile as H,removePidFile as h}from"./core/runtime/index.js";import{resolveRuntimePaths as v}from"./core/config/index.js";import{ServiceManager as j}from"./service/service-manager.js";import{killProcessesByCommandLine as _,isWindowsElevated as G}from"./service/process-control.js";import{acquireDaemonLock as W,releaseDaemonLock as w}from"./runtime/daemon-lock.js";import{writeDaemonStatus as k,removeDaemonStatus as M}from"./runtime/service-state.js";import{AdminServer as B,generateToken as V,writeTokenFile as X}from"./core/admin/index.js";import{initSentry as q,closeSentry as P,reportFatal as E}from"./core/observability/sentry.js";const c=process.argv.slice(2),A=[],s={};for(let t=0;t<c.length;t++)c[t].startsWith("--")&&c[t+1]&&!c[t+1].startsWith("--")?(s[c[t].slice(2)]=c[t+1],t++):c[t].startsWith("--")?s[c[t].slice(2)]="true":A.push(c[t]);s.help&&(console.log(`grix-connector \u2014 Unified AI Agent Bridge
3
3
 
4
4
  Usage: grix-connector <command> [options]
5
5
 
@@ -28,4 +28,4 @@ Examples:
28
28
  `),process.exit(0));const d=A[0],I=["start","stop","restart","status"];if(d&&I.includes(d)){process.platform==="win32"&&["start","stop","restart"].includes(d)&&!G()&&console.warn(`Warning: Not running as administrator. Task Scheduler registration is skipped;
29
29
  using Startup folder auto-start instead. For full Task Scheduler integration,
30
30
  right-click the terminal and select "Run as administrator".`);const t=v(),m=s["config-dir"]??(s.profile?g.join(t.configDir,s.profile):void 0),l=g.resolve(process.argv[1]||`${t.rootDir}/dist/grix.js`),r=new j({cliPath:l,nodePath:process.execPath});try{let o;switch(d){case"start":(await r.status({rootDir:t.rootDir})).installed?o=await r.start({rootDir:t.rootDir}):o=await r.install({rootDir:t.rootDir,configDir:m});break;case"stop":o=await r.stop({rootDir:t.rootDir});break;case"restart":(await r.status({rootDir:t.rootDir})).installed?o=await r.restart({rootDir:t.rootDir}):o=await r.install({rootDir:t.rootDir,configDir:m});break;case"status":o=await r.status({rootDir:t.rootDir});break}console.log(JSON.stringify(o,null,2)),process.exit(0)}catch(o){console.error(`${d} failed: ${o instanceof Error?o.message:o}`),process.exit(1)}}else d&&(console.error(`Unknown command: ${d}
31
- Valid commands: ${I.join(", ")}`),process.exit(1));const i=v(),J=s["config-dir"]??(s.profile?`${i.configDir}/${s.profile}`:void 0),n=new $,S=new U,R=V(),p=new B(R);let T=!1;async function D(t){if(T)return;T=!0,a.info("main",`Received ${t}, shutting down...`),S.markShuttingDown();const m=setTimeout(()=>{a.error("main","Shutdown timed out, forcing exit"),w(i.daemonLockFile).catch(()=>{}),h(),process.exit(2)},1e4);try{await n.stop(),await p.stop(),await S.stop(),await P(),await w(i.daemonLockFile),await M(i.daemonStatusFile).catch(()=>{}),clearTimeout(m),h(),a.info("main","Shutdown complete"),process.exit(0)}catch(l){a.error("main",`Shutdown error: ${l}`),w(i.daemonLockFile).catch(()=>{}),h(),process.exit(2)}}async function K(){L(),N(),q(),b(i.stdoutLogFile,i.stderrLogFile),C(!1),process.platform==="win32"&&await _("GrixConnectorDaemon",{platform:"win32"});try{await W(i.daemonLockFile,i.rootDir)}catch(e){console.error(e instanceof Error?e.message:e),process.exit(1)}H(),a.info("main",`grix-connector starting (PID ${process.pid})`),await k(i.daemonStatusFile,{state:"starting",pid:process.pid,updated_at:Date.now()});const t=parseInt(s["health-port"]??process.env.GRIX_HEALTH_PORT??"19579",10);await S.start(t);const m=g.join(i.dataDir,"health-port");x(m,String(t),"utf-8"),process.on("SIGINT",()=>D("SIGINT")),process.on("SIGTERM",()=>D("SIGTERM"));let l="",r=0,o;process.on("uncaughtException",e=>{const u=e instanceof Error?e.stack??e.message:String(e);u===l?(r++,(r<=3||r%100===0)&&a.error("main",`Uncaught exception (x${r}): ${u}`)):(r>3&&a.error("main",`Previous exception repeated ${r} times total`),l=u,r=1,a.error("main",`Uncaught exception: ${e instanceof Error?e.stack:e}`),o||(o=setTimeout(()=>{r>3&&a.error("main",`Previous exception repeated ${r} times total`),l="",r=0,o=void 0},1e4).unref())),!F(e)&&(E(e,"uncaughtException"),D("uncaughtException"))}),process.on("unhandledRejection",e=>{a.error("main",`Unhandled rejection: ${e}`),!F(e)&&(E(e,"unhandledRejection"),D("unhandledRejection"))}),S.setStatusProvider(()=>n.getAgentsStatus()),await n.start(J);const f=parseInt(s["admin-port"]??process.env.GRIX_ADMIN_PORT??"19580",10);p.setAgentHandler({list:()=>n.getAgentsStatus(),add:e=>n.addAgent(e),remove:e=>n.removeAgent(e),restart:e=>n.restartAgent(e)}),p.setUpgradeHandler({check:()=>n.checkUpgrade(),trigger:()=>n.triggerUpgrade()}),p.setProbeHandler({probeAll:e=>n.probeAll(e),probeOne:(e,u)=>n.probeOne(e,u)}),p.setInstallHandler({listInstallable:()=>n.listInstallable(),installAgent:e=>n.installAgent(e),getInstallProgress:e=>n.getInstallProgress(e)}),await p.start(f);const y=g.join(i.dataDir,"admin-token"),O=g.join(i.dataDir,"admin-port");X(y,R),x(O,String(f),"utf-8"),await k(i.daemonStatusFile,{state:"running",pid:process.pid,updated_at:Date.now()}),process.send&&process.send("ready"),a.info("main","grix-connector ready")}K().catch(async t=>{a.error("main",`Fatal: ${t}`),E(t,"startup"),await P(),w(i.daemonLockFile).catch(()=>{}),h(),process.exit(1)});const z=new Set(["ECONNRESET","ECONNREFUSED","ETIMEDOUT","EPIPE","EAI_AGAIN","ENOTFOUND","EHOSTUNREACH","ENETUNREACH","EIO"]);function F(t){return t instanceof Error&&"code"in t?z.has(t.code):!1}
31
+ Valid commands: ${I.join(", ")}`),process.exit(1));const i=v(),J=s["config-dir"]??(s.profile?`${i.configDir}/${s.profile}`:void 0),a=new $,S=new U,R=V(),p=new B(R);let T=!1;async function D(t){if(T)return;T=!0,n.info("main",`Received ${t}, shutting down...`),S.markShuttingDown();const m=setTimeout(()=>{n.error("main","Shutdown timed out, forcing exit"),w(i.daemonLockFile).catch(()=>{}),h(),process.exit(2)},1e4);try{await a.stop(),await p.stop(),await S.stop(),await P(),await w(i.daemonLockFile),await M(i.daemonStatusFile).catch(()=>{}),clearTimeout(m),h(),n.info("main","Shutdown complete"),process.exit(0)}catch(l){n.error("main",`Shutdown error: ${l}`),w(i.daemonLockFile).catch(()=>{}),h(),process.exit(2)}}async function K(){L(),N(),await q(),b(i.stdoutLogFile,i.stderrLogFile),C(!1),process.platform==="win32"&&await _("GrixConnectorDaemon",{platform:"win32"});try{await W(i.daemonLockFile,i.rootDir)}catch(e){console.error(e instanceof Error?e.message:e),process.exit(1)}H(),n.info("main",`grix-connector starting (PID ${process.pid})`),await k(i.daemonStatusFile,{state:"starting",pid:process.pid,updated_at:Date.now()});const t=parseInt(s["health-port"]??process.env.GRIX_HEALTH_PORT??"19579",10);await S.start(t);const m=g.join(i.dataDir,"health-port");x(m,String(t),"utf-8"),process.on("SIGINT",()=>D("SIGINT")),process.on("SIGTERM",()=>D("SIGTERM"));let l="",r=0,o;process.on("uncaughtException",e=>{const u=e instanceof Error?e.stack??e.message:String(e);u===l?(r++,(r<=3||r%100===0)&&n.error("main",`Uncaught exception (x${r}): ${u}`)):(r>3&&n.error("main",`Previous exception repeated ${r} times total`),l=u,r=1,n.error("main",`Uncaught exception: ${e instanceof Error?e.stack:e}`),o||(o=setTimeout(()=>{r>3&&n.error("main",`Previous exception repeated ${r} times total`),l="",r=0,o=void 0},1e4).unref())),!F(e)&&(E(e,"uncaughtException"),D("uncaughtException"))}),process.on("unhandledRejection",e=>{n.error("main",`Unhandled rejection: ${e}`),!F(e)&&(E(e,"unhandledRejection"),D("unhandledRejection"))}),S.setStatusProvider(()=>a.getAgentsStatus()),await a.start(J);const f=parseInt(s["admin-port"]??process.env.GRIX_ADMIN_PORT??"19580",10);p.setAgentHandler({list:()=>a.getAgentsStatus(),add:e=>a.addAgent(e),remove:e=>a.removeAgent(e),restart:e=>a.restartAgent(e)}),p.setUpgradeHandler({check:()=>a.checkUpgrade(),trigger:()=>a.triggerUpgrade()}),p.setProbeHandler({probeAll:e=>a.probeAll(e),probeOne:(e,u)=>a.probeOne(e,u)}),p.setInstallHandler({listInstallable:()=>a.listInstallable(),installAgent:e=>a.installAgent(e),getInstallProgress:e=>a.getInstallProgress(e)}),await p.start(f);const y=g.join(i.dataDir,"admin-token"),O=g.join(i.dataDir,"admin-port");X(y,R),x(O,String(f),"utf-8"),await k(i.daemonStatusFile,{state:"running",pid:process.pid,updated_at:Date.now()}),process.send&&process.send("ready"),n.info("main","grix-connector ready")}K().catch(async t=>{n.error("main",`Fatal: ${t}`),E(t,"startup"),await P(),w(i.daemonLockFile).catch(()=>{}),h(),process.exit(1)});const z=new Set(["ECONNRESET","ECONNREFUSED","ETIMEDOUT","EPIPE","EAI_AGAIN","ENOTFOUND","EHOSTUNREACH","ENETUNREACH","EIO"]);function F(t){return t instanceof Error&&"code"in t?z.has(t.code):!1}
package/dist/log.js CHANGED
@@ -1,3 +1,3 @@
1
- import{createWriteStream as g,mkdirSync as l,existsSync as f}from"node:fs";import{join as t}from"node:path";import{homedir as m}from"node:os";const i=t(m(),".grix"),s={base:i,config:t(i,"config"),log:t(i,"log"),data:t(i,"data")};function S(){for(const o of Object.values(s))f(o)||l(o,{recursive:!0})}let a=null;function $(){const o=new Date().toISOString().slice(0,10),r=t(s.log,`grix-acp-${o}.log`);a=g(r,{flags:"a"})}function c(){return new Date().toISOString().slice(11,19)}const u={info(o,r,...n){const e=`${c()} [${o}] ${r}${n.length?" "+n.map(String).join(" "):""}`;console.log(e),a?.write(e+`
2
- `)},error(o,r,...n){const e=`${c()} [${o}] ERROR ${r}${n.length?" "+n.map(String).join(" "):""}`;console.error(e),a?.write(e+`
1
+ import{createWriteStream as g,mkdirSync as l,existsSync as f}from"node:fs";import{join as i}from"node:path";import{homedir as m}from"node:os";const n=i(m(),".grix"),s={base:n,config:i(n,"config"),log:i(n,"log"),data:i(n,"data")};function S(){for(const o of Object.values(s))f(o)||l(o,{recursive:!0})}let a=null;function $(){const o=new Date().toISOString().slice(0,10),r=i(s.log,`grix-acp-${o}.log`);a=g(r,{flags:"a"})}function c(){return new Date().toISOString().slice(11,19)}const u={info(o,r,...t){const e=`${c()} [${o}] ${r}${t.length?" "+t.map(String).join(" "):""}`;console.log(e),a?.write(e+`
2
+ `)},error(o,r,...t){const e=`${c()} [${o}] ERROR ${r}${t.length?" "+t.map(String).join(" "):""}`;console.error(e),a?.write(e+`
3
3
  `)}};export{s as GRIX_PATHS,S as ensureGrixDirs,$ as initLogger,u as log};
package/dist/manager.js CHANGED
@@ -1,2 +1,2 @@
1
- import{readFileSync as Q,readdirSync as F,writeFileSync as B}from"node:fs";import{join as A}from"node:path";import{AgentInstance as S}from"./bridge/bridge.js";import{GRIX_PATHS as x,log as c}from"./core/log/index.js";import{resolveClientVersion as L}from"./core/util/client-version.js";import{UpgradeChecker as R}from"./core/upgrade/upgrade-checker.js";import{AgentGlobalConfigStore as z}from"./core/persistence/agent-global-config-store.js";import{scanSkills as G,dedupeSkills as K}from"./adapter/claude/skill-scanner.js";import{scanDefaultSkills as W,logDefaultSkillsCheck as V,cleanupProjectedSkills as D}from"./default-skills/index.js";import{resolveCopilotCommand as q}from"./core/runtime/copilot-resolve.js";import{getCliVersion as J,resolveCliPath as X}from"./core/util/cli-probe.js";import{AgentInstaller as Y}from"./core/installer/installer.js";import{reportInstallFailure as Z}from"./core/observability/sentry.js";const ee=8e3;function te(){const n=q();return[{clientType:"openclaw",command:"openclaw"},{clientType:"claude",command:"claude"},{clientType:"codex",command:"codex"},{clientType:"gemini",command:"gemini"},{clientType:"qwen",command:"qwen"},{clientType:"hermes",command:"hermes"},{clientType:"reasonix",command:"reasonix"},{clientType:"codewhale",command:"codewhale"},{clientType:"opencode",command:"opencode"},{clientType:"pi",command:"pi"},{clientType:"kiro",command:"kiro-cli"},{clientType:"copilot",command:n.command},{clientType:"agy",command:"agy"}]}async function ne(){return Promise.all(te().map(async n=>{const e=await X(n.command);if(!e)return{client_type:n.clientType,command:n.command,installed:!1,path:null,version:null};const t=await J(e,n.versionArgs??["--version"]);return{client_type:n.clientType,command:n.command,installed:!0,path:e,version:t.version,error:t.error}}))}function ae(n){switch(n){case"claude":return{adapterType:"claude",command:"claude"};case"codex":return{adapterType:"codex",command:"codex",options:{sandboxMode:"danger-full-access"}};case"gemini":return{adapterType:"acp",command:"gemini",autoInjectArgs:{acp:!0},enableSessionBinding:!0};case"qwen":return{adapterType:"acp",command:"qwen",adapterHint:"qwen/base",autoInjectArgs:{acp:!0},enableSessionBinding:!0};case"pi":return{adapterType:"pi",command:"pi"};case"cursor":return{adapterType:"cursor",command:"agent"};case"reasonix":return{adapterType:"acp",command:"reasonix",args:["acp"],enableSessionBinding:!0};case"codewhale":return{adapterType:"codewhale",command:"codewhale",enableSessionBinding:!0};case"openhuman":return{adapterType:"openhuman",command:"openhuman-core",enableSessionBinding:!0};case"kiro":return{adapterType:"acp",command:"kiro-cli",args:["acp"],enableSessionBinding:!0};case"opencode":return{adapterType:"opencode",command:"opencode",args:["serve"],enableSessionBinding:!0};case"copilot":{const e=q();return{adapterType:"acp",command:e.command,args:[...e.prefixArgs,"--acp"],enableSessionBinding:!0}}case"agy":return{adapterType:"agy",command:"agy",enableSessionBinding:!0};case"hermes":throw new Error('client_type "hermes" is not handled by grix-connector. Hermes runs as a separate project \u2014 see https://github.com/askie/grix-hermes-python');default:throw new Error(`Unsupported client_type: ${n}`)}}function oe(n){const e=String(n??"").trim().toLowerCase().replace(/[^a-z0-9._-]+/g,"-").replace(/-+/g,"-").replace(/^-|-$/g,"")||"default";return A(x.data,`session-bindings-${e}.json`)}function ie(n){const e=String(n??"").trim().toLowerCase().replace(/[^a-z0-9._-]+/g,"-").replace(/-+/g,"-").replace(/^-|-$/g,"")||"default";return A(x.data,`active-events-${e}.json`)}function re(...n){const e=[],t=new Set;for(const o of n)for(const a of o??[]){const r=String(a??"").trim(),u=r.toLowerCase();!r||t.has(u)||(t.add(u),e.push(r))}return e.length>0?e:void 0}function se(n,e){const t={claude:{maxConcurrent:1,maxQueued:5,queueTimeoutMs:0},codex:{maxConcurrent:1,maxQueued:5,queueTimeoutMs:0},cursor:{maxConcurrent:1,maxQueued:3,queueTimeoutMs:0},acp:{maxConcurrent:1,maxQueued:3,queueTimeoutMs:0},pi:{maxConcurrent:1,maxQueued:5,queueTimeoutMs:0},codewhale:{maxConcurrent:1,maxQueued:3,queueTimeoutMs:0},openhuman:{maxConcurrent:1,maxQueued:3,queueTimeoutMs:0},opencode:{maxConcurrent:1,maxQueued:3,queueTimeoutMs:0},agy:{maxConcurrent:1,maxQueued:3,queueTimeoutMs:0}},o=t[n]??t.acp;return{maxConcurrent:e?.max_concurrent??o.maxConcurrent??1,maxQueued:e?.max_queued??o.maxQueued??3,queueTimeoutMs:e?.queue_timeout_ms??o.queueTimeoutMs??0,cancelableQueued:!0,cancelableRunning:!0}}function E(n){const e=L(),t=String(n.client_type??"").trim().toLowerCase(),o=ae(t),a=String(n.ws_url??"").trim(),r="get_session_usage",u="get_rate_limits",l="get_agent_global_config";if(!n.name?.trim())throw new Error("agent name is required");if(!a)throw new Error(`agent ${n.name}: ws_url is required`);if(!n.agent_id?.trim())throw new Error(`agent ${n.name}: agent_id is required`);if(!n.api_key?.trim())throw new Error(`agent ${n.name}: api_key is required`);const s=o.adapterType,d=s==="acp",f=t==="qwen",p={...o.options??{}},i=s==="codex"?{capabilities:["local_action_v1","agent_invoke"],localActions:["session_control","get_context","set_model","set_mode","set_reasoning_effort","set_sandbox_mode","exec_approve","exec_reject","file_list","create_folder","turn_interrupt","permission_approve","permission_reject","thread_compact",r,u]}:null,m=s==="claude"?{localActions:["session_control","set_mode","set_model","claude_interaction_reply","exec_approve","exec_reject","file_list","create_folder","thread_compact",r,u]}:null,_=f?{capabilities:["stream_chunk","local_action_v1"],localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","file_list","create_folder",r],adapterHint:"qwen/base"}:null,h=s==="pi"?{adapterHint:"pi/base",capabilities:["local_action_v1"],localActions:["session_control","set_model","get_context","file_list","create_folder",r]}:null,g=s==="openhuman"?{adapterHint:"openhuman/base",capabilities:["local_action_v1"],localActions:["session_control","set_model","file_list","create_folder",r]}:null,w=s==="cursor"?{adapterHint:"cursor/base",capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","set_mode","get_context","file_list","create_folder",r,u]}:null,b=s==="codewhale"?{capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","file_list","create_folder",r]}:null,T=s==="opencode"?{adapterHint:"opencode/base",capabilities:["stream_chunk","local_action_v1"],localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","file_list","create_folder",r]}:null,k=s==="agy"?{adapterHint:"agy/base",capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","file_list","create_folder",r]}:null,P=d&&!f?{localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","file_list","create_folder",r]}:null,H=t==="kiro"?{localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","file_list","create_folder","thread_compact",r,u]}:null,N=s==="codex"||s==="claude"||t==="gemini"?["session_control","set_model","set_mode"]:void 0,O=[r,u];d&&p.raw_transport===void 0&&(p.raw_transport=t==="gemini");const U=`${t}/base`,$=s==="claude"?"claude":s==="codex"?"codex":s==="pi"?"pi":t==="kiro"?"kiro":"gemini";let y;try{const j=$==="kiro"?void 0:process.cwd();y=G({mode:$,projectDir:j})??void 0,y&&y.length===0&&(y=void 0)}catch{}const M=W();if(M.length>0){const v=K([...y??[],...M]),I=v.filter(C=>C.source==="connector").map(C=>C.name);I.length>0&&c.info("manager",`[${n.name}] injecting connector skills: [${I.join(", ")}]`),y=v.length>0?v:void 0}return{name:n.name,adapterType:s,aibot:{url:a,agentId:n.agent_id,apiKey:n.api_key,clientType:t,clientVersion:e,adapterHint:o.adapterHint??_?.adapterHint??h?.adapterHint??g?.adapterHint??w?.adapterHint??T?.adapterHint??k?.adapterHint??U,capabilities:i?.capabilities??b?.capabilities??h?.capabilities??g?.capabilities??w?.capabilities??T?.capabilities??k?.capabilities??_?.capabilities??["stream_chunk","local_action_v1","connector_upgrade"],localActions:re(i?.localActions??b?.localActions??m?.localActions??h?.localActions??g?.localActions??w?.localActions??T?.localActions??k?.localActions??_?.localActions??H?.localActions??P?.localActions??["exec_approve","exec_reject"],N,O,["connector_rollback","connector_upgrade_push",l]),skills:y},agent:{command:o.command,args:o.args,env:void 0},adapterOptions:p,acpAuthMethod:p.auth_method,acpInitialMode:p.initial_mode,acpMcpTools:p.acp_mcp_tools,promptTimeoutMs:n.prompt_timeout_ms,bindingsPath:oe(n.name),activeEventStorePath:ie(n.name),...o.enableSessionBinding||d?{enableSessionBinding:!0}:{},...o.autoInjectArgs?{autoInjectArgs:o.autoInjectArgs}:{},poolMaxSize:n.pool?.maxSize,poolIdleTimeoutMs:n.pool?.idleTimeoutMs,eventQueue:se(s,n.event_queue),logDir:x.log,providerBaseUrl:n.provider_base_url?.trim()||void 0,providerApiKey:n.provider_api_key?.trim()||void 0}}function ce(){const n=process.env.GRIX_AGENT_STARTUP_WAIT_MS,e=Number(n);return Number.isFinite(e)&&e>=500?Math.floor(e):ee}class ke{instances=[];configMap=new Map;upgradeChecker=null;globalConfigStore;configDir=x.config;installer=new Y;async start(e){const t=e??x.config;this.configDir=t,c.info("manager",`Loading configs from ${t}`),V(),D(),this.globalConfigStore=new z(A(x.data,"agent-global-configs.json")),this.globalConfigStore.load();const o=F(t).filter(l=>l.endsWith(".json")).sort();if(o.length===0)throw new Error(`No config files found in ${t}`);const a=[];let r=0;for(const l of o)try{const s=Q(A(t,l),"utf-8"),d=JSON.parse(s);if(Array.isArray(d.agents)){if(d.agents.length===0){c.error("manager",`No agents array found in ${l}`),r++;continue}for(const f of d.agents)try{const p=E(f);a.push({config:p,file:l}),c.info("manager",`Loaded ${p.name} (${p.adapterType??"acp"}) from ${l}`)}catch(p){const i=typeof f?.name=="string"?f.name:"<unknown>";c.error("manager",`Invalid agent config in ${l} (name=${i}): ${p}`),r++}}else c.error("manager",`Unrecognized config format in ${l}`)}catch(s){c.error("manager",`Failed to load ${l}: ${s}`)}let u=0;if(a.length>0){const l=ce();c.info("manager",`Starting ${a.length} agent(s), startup wait=${l}ms`);const s=()=>this.upgradeChecker?.triggerCheck(),d=i=>{this.instances=this.instances.filter(m=>m!==i)},f=a.map(({config:i})=>{const m=new S(i,this.globalConfigStore);return m.setUpgradeTrigger(s),this.instances.push(m),this.configMap.set(i.name,i),{config:i,instance:m,startPromise:m.start()}}),p=await Promise.all(f.map(async i=>{const m=await new Promise(_=>{let h=!1;const g=setTimeout(()=>{h||(h=!0,_({kind:"timeout"}))},l);i.startPromise.then(()=>{h||(h=!0,clearTimeout(g),_({kind:"started"}))}).catch(w=>{h||(h=!0,clearTimeout(g),_({kind:"failed",error:w}))})});return{task:i,outcome:m}}));for(const{task:i,outcome:m}of p)if(m.kind!=="started"){if(m.kind==="failed"){d(i.instance),c.error("manager",`Failed to start ${i.config.name}: ${m.error}`);continue}u++,c.warn("manager",`Startup pending for ${i.config.name}, continue retrying in background`),i.startPromise.then(()=>{c.info("manager",`Delayed start succeeded: ${i.config.name}`)}).catch(_=>{d(i.instance),c.error("manager",`Delayed start failed: ${i.config.name}: ${_}`)})}if(this.instances.length>0){const i=Math.max(0,this.instances.length-u);c.info("manager",`${i}/${a.length} agent(s) running now`)}u>0&&c.warn("manager",`${u} agent(s) still connecting in background`)}if(this.instances.length===0&&a.length>0)throw new Error("All agent configurations failed to start");if(a.length>0){const l=a[0].config;this.upgradeChecker=new R([{apiKey:l.aibot.apiKey,wsUrl:l.aibot.url}],()=>this.instances.some(s=>s.getStatus().busy)),await this.upgradeChecker.start()}}async stop(){c.info("manager","Stopping all agents..."),this.upgradeChecker?.stop(),await Promise.allSettled(this.instances.map(e=>e.stop())),await this.globalConfigStore?.flush(),this.instances=[],D(),c.info("manager","All stopped")}getAgentsStatus(){return this.instances.map(e=>e.getStatus())}async addAgent(e){const t=E(e);if(this.instances.some(a=>a.name===t.name))throw new Error(`Agent "${t.name}" already exists`);const o=new S(t,this.globalConfigStore);o.setUpgradeTrigger(()=>this.upgradeChecker?.triggerCheck()),await o.start(),this.instances.push(o),this.configMap.set(t.name,t),this.persistAgentsConfig(),c.info("manager",`Added agent: ${t.name}`)}async removeAgent(e){const t=this.instances.findIndex(a=>a.name===e);if(t===-1)throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});const o=this.instances[t];this.instances.splice(t,1),this.configMap.delete(e),await o.stop(),this.persistAgentsConfig(),c.info("manager",`Removed agent: ${e}`)}persistAgentsConfig(){const e=A(this.configDir,"agents.json");try{const t=[];for(const[,a]of this.configMap)t.push({name:a.name,ws_url:a.aibot.url,agent_id:a.aibot.agentId,api_key:a.aibot.apiKey,client_type:a.aibot.clientType});B(e,JSON.stringify({agents:t},null,4)+`
1
+ import{readFileSync as Q,readdirSync as F,writeFileSync as B}from"node:fs";import{join as A}from"node:path";import{AgentInstance as S}from"./bridge/bridge.js";import{GRIX_PATHS as x,log as c}from"./core/log/index.js";import{resolveClientVersion as L}from"./core/util/client-version.js";import{UpgradeChecker as R}from"./core/upgrade/upgrade-checker.js";import{AgentGlobalConfigStore as z}from"./core/persistence/agent-global-config-store.js";import{scanSkills as G,dedupeSkills as K}from"./adapter/claude/skill-scanner.js";import{scanDefaultSkills as W,logDefaultSkillsCheck as V,cleanupProjectedSkills as D}from"./default-skills/index.js";import{resolveCopilotCommand as q}from"./core/runtime/copilot-resolve.js";import{getCliVersion as J,resolveCliPath as X}from"./core/util/cli-probe.js";import{AgentInstaller as Y}from"./core/installer/installer.js";import{reportInstallFailure as Z}from"./core/observability/sentry.js";const ee=8e3;function te(){const n=q();return[{clientType:"openclaw",command:"openclaw"},{clientType:"claude",command:"claude"},{clientType:"codex",command:"codex"},{clientType:"gemini",command:"gemini"},{clientType:"qwen",command:"qwen"},{clientType:"hermes",command:"hermes"},{clientType:"reasonix",command:"reasonix"},{clientType:"codewhale",command:"codewhale"},{clientType:"opencode",command:"opencode"},{clientType:"cursor",command:"agent"},{clientType:"pi",command:"pi"},{clientType:"openhuman",command:"openhuman-core"},{clientType:"kiro",command:"kiro-cli"},{clientType:"copilot",command:n.command},{clientType:"agy",command:"agy"}]}async function ne(){return Promise.all(te().map(async n=>{const e=await X(n.command);if(!e)return{client_type:n.clientType,command:n.command,installed:!1,path:null,version:null};const t=await J(e,n.versionArgs??["--version"]);return{client_type:n.clientType,command:n.command,installed:!0,path:e,version:t.version,error:t.error}}))}function ae(n){switch(n){case"claude":return{adapterType:"claude",command:"claude"};case"codex":return{adapterType:"codex",command:"codex",options:{sandboxMode:"danger-full-access"}};case"gemini":return{adapterType:"acp",command:"gemini",autoInjectArgs:{acp:!0},enableSessionBinding:!0};case"qwen":return{adapterType:"acp",command:"qwen",adapterHint:"qwen/base",autoInjectArgs:{acp:!0},enableSessionBinding:!0};case"pi":return{adapterType:"pi",command:"pi"};case"cursor":return{adapterType:"cursor",command:"agent"};case"reasonix":return{adapterType:"acp",command:"reasonix",args:["acp"],enableSessionBinding:!0};case"codewhale":return{adapterType:"codewhale",command:"codewhale",enableSessionBinding:!0};case"openhuman":return{adapterType:"openhuman",command:"openhuman-core",enableSessionBinding:!0};case"kiro":return{adapterType:"acp",command:"kiro-cli",args:["acp"],enableSessionBinding:!0};case"opencode":return{adapterType:"opencode",command:"opencode",args:["serve"],enableSessionBinding:!0};case"copilot":{const e=q();return{adapterType:"acp",command:e.command,args:[...e.prefixArgs,"--acp"],enableSessionBinding:!0}}case"agy":return{adapterType:"agy",command:"agy",enableSessionBinding:!0};case"hermes":throw new Error('client_type "hermes" is not handled by grix-connector. Hermes runs as a separate project \u2014 see https://github.com/askie/grix-hermes-python');default:throw new Error(`Unsupported client_type: ${n}`)}}function oe(n){const e=String(n??"").trim().toLowerCase().replace(/[^a-z0-9._-]+/g,"-").replace(/-+/g,"-").replace(/^-|-$/g,"")||"default";return A(x.data,`session-bindings-${e}.json`)}function ie(n){const e=String(n??"").trim().toLowerCase().replace(/[^a-z0-9._-]+/g,"-").replace(/-+/g,"-").replace(/^-|-$/g,"")||"default";return A(x.data,`active-events-${e}.json`)}function re(...n){const e=[],t=new Set;for(const o of n)for(const a of o??[]){const r=String(a??"").trim(),u=r.toLowerCase();!r||t.has(u)||(t.add(u),e.push(r))}return e.length>0?e:void 0}function se(n,e){const t={claude:{maxConcurrent:1,maxQueued:5,queueTimeoutMs:0},codex:{maxConcurrent:1,maxQueued:5,queueTimeoutMs:0},cursor:{maxConcurrent:1,maxQueued:3,queueTimeoutMs:0},acp:{maxConcurrent:1,maxQueued:3,queueTimeoutMs:0},pi:{maxConcurrent:1,maxQueued:5,queueTimeoutMs:0},codewhale:{maxConcurrent:1,maxQueued:3,queueTimeoutMs:0},openhuman:{maxConcurrent:1,maxQueued:3,queueTimeoutMs:0},opencode:{maxConcurrent:1,maxQueued:3,queueTimeoutMs:0},agy:{maxConcurrent:1,maxQueued:3,queueTimeoutMs:0}},o=t[n]??t.acp;return{maxConcurrent:e?.max_concurrent??o.maxConcurrent??1,maxQueued:e?.max_queued??o.maxQueued??3,queueTimeoutMs:e?.queue_timeout_ms??o.queueTimeoutMs??0,cancelableQueued:!0,cancelableRunning:!0}}function E(n){const e=L(),t=String(n.client_type??"").trim().toLowerCase(),o=ae(t),a=String(n.ws_url??"").trim(),r="get_session_usage",u="get_rate_limits",l="get_agent_global_config";if(!n.name?.trim())throw new Error("agent name is required");if(!a)throw new Error(`agent ${n.name}: ws_url is required`);if(!n.agent_id?.trim())throw new Error(`agent ${n.name}: agent_id is required`);if(!n.api_key?.trim())throw new Error(`agent ${n.name}: api_key is required`);const s=o.adapterType,d=s==="acp",f=t==="qwen",p={...o.options??{}},i=s==="codex"?{capabilities:["local_action_v1","agent_invoke"],localActions:["session_control","get_context","set_model","set_mode","set_reasoning_effort","set_sandbox_mode","exec_approve","exec_reject","file_list","create_folder","turn_interrupt","permission_approve","permission_reject","thread_compact",r,u]}:null,m=s==="claude"?{localActions:["session_control","set_mode","set_model","claude_interaction_reply","exec_approve","exec_reject","file_list","create_folder","thread_compact",r,u]}:null,_=f?{capabilities:["stream_chunk","local_action_v1"],localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","file_list","create_folder",r],adapterHint:"qwen/base"}:null,h=s==="pi"?{adapterHint:"pi/base",capabilities:["local_action_v1"],localActions:["session_control","set_model","get_context","file_list","create_folder",r]}:null,g=s==="openhuman"?{adapterHint:"openhuman/base",capabilities:["local_action_v1"],localActions:["session_control","set_model","file_list","create_folder",r]}:null,w=s==="cursor"?{adapterHint:"cursor/base",capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","set_mode","get_context","file_list","create_folder",r,u]}:null,b=s==="codewhale"?{capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","file_list","create_folder",r]}:null,T=s==="opencode"?{adapterHint:"opencode/base",capabilities:["stream_chunk","local_action_v1"],localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","file_list","create_folder",r]}:null,k=s==="agy"?{adapterHint:"agy/base",capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","file_list","create_folder",r]}:null,P=d&&!f?{localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","file_list","create_folder",r]}:null,H=t==="kiro"?{localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","file_list","create_folder","thread_compact",r,u]}:null,N=s==="codex"||s==="claude"||t==="gemini"?["session_control","set_model","set_mode"]:void 0,O=[r,u];d&&p.raw_transport===void 0&&(p.raw_transport=t==="gemini");const U=`${t}/base`,$=s==="claude"?"claude":s==="codex"?"codex":s==="pi"?"pi":t==="kiro"?"kiro":"gemini";let y;try{const j=$==="kiro"?void 0:process.cwd();y=G({mode:$,projectDir:j})??void 0,y&&y.length===0&&(y=void 0)}catch{}const M=W();if(M.length>0){const v=K([...y??[],...M]),I=v.filter(C=>C.source==="connector").map(C=>C.name);I.length>0&&c.info("manager",`[${n.name}] injecting connector skills: [${I.join(", ")}]`),y=v.length>0?v:void 0}return{name:n.name,adapterType:s,aibot:{url:a,agentId:n.agent_id,apiKey:n.api_key,clientType:t,clientVersion:e,adapterHint:o.adapterHint??_?.adapterHint??h?.adapterHint??g?.adapterHint??w?.adapterHint??T?.adapterHint??k?.adapterHint??U,capabilities:i?.capabilities??b?.capabilities??h?.capabilities??g?.capabilities??w?.capabilities??T?.capabilities??k?.capabilities??_?.capabilities??["stream_chunk","local_action_v1","connector_upgrade"],localActions:re(i?.localActions??b?.localActions??m?.localActions??h?.localActions??g?.localActions??w?.localActions??T?.localActions??k?.localActions??_?.localActions??H?.localActions??P?.localActions??["exec_approve","exec_reject"],N,O,["connector_rollback","connector_upgrade_push",l]),skills:y},agent:{command:o.command,args:o.args,env:void 0},adapterOptions:p,acpAuthMethod:p.auth_method,acpInitialMode:p.initial_mode,acpMcpTools:p.acp_mcp_tools,promptTimeoutMs:n.prompt_timeout_ms,bindingsPath:oe(n.name),activeEventStorePath:ie(n.name),...o.enableSessionBinding||d?{enableSessionBinding:!0}:{},...o.autoInjectArgs?{autoInjectArgs:o.autoInjectArgs}:{},poolMaxSize:n.pool?.maxSize,poolIdleTimeoutMs:n.pool?.idleTimeoutMs,eventQueue:se(s,n.event_queue),logDir:x.log,providerBaseUrl:n.provider_base_url?.trim()||void 0,providerApiKey:n.provider_api_key?.trim()||void 0}}function ce(){const n=process.env.GRIX_AGENT_STARTUP_WAIT_MS,e=Number(n);return Number.isFinite(e)&&e>=500?Math.floor(e):ee}class ke{instances=[];configMap=new Map;upgradeChecker=null;globalConfigStore;configDir=x.config;installer=new Y;async start(e){const t=e??x.config;this.configDir=t,c.info("manager",`Loading configs from ${t}`),V(),D(),this.globalConfigStore=new z(A(x.data,"agent-global-configs.json")),this.globalConfigStore.load();const o=F(t).filter(l=>l.endsWith(".json")).sort();if(o.length===0)throw new Error(`No config files found in ${t}`);const a=[];let r=0;for(const l of o)try{const s=Q(A(t,l),"utf-8"),d=JSON.parse(s);if(Array.isArray(d.agents)){if(d.agents.length===0){c.error("manager",`No agents array found in ${l}`),r++;continue}for(const f of d.agents)try{const p=E(f);a.push({config:p,file:l}),c.info("manager",`Loaded ${p.name} (${p.adapterType??"acp"}) from ${l}`)}catch(p){const i=typeof f?.name=="string"?f.name:"<unknown>";c.error("manager",`Invalid agent config in ${l} (name=${i}): ${p}`),r++}}else c.error("manager",`Unrecognized config format in ${l}`)}catch(s){c.error("manager",`Failed to load ${l}: ${s}`)}let u=0;if(a.length>0){const l=ce();c.info("manager",`Starting ${a.length} agent(s), startup wait=${l}ms`);const s=()=>this.upgradeChecker?.triggerCheck(),d=i=>{this.instances=this.instances.filter(m=>m!==i)},f=a.map(({config:i})=>{const m=new S(i,this.globalConfigStore);return m.setUpgradeTrigger(s),this.instances.push(m),this.configMap.set(i.name,i),{config:i,instance:m,startPromise:m.start()}}),p=await Promise.all(f.map(async i=>{const m=await new Promise(_=>{let h=!1;const g=setTimeout(()=>{h||(h=!0,_({kind:"timeout"}))},l);i.startPromise.then(()=>{h||(h=!0,clearTimeout(g),_({kind:"started"}))}).catch(w=>{h||(h=!0,clearTimeout(g),_({kind:"failed",error:w}))})});return{task:i,outcome:m}}));for(const{task:i,outcome:m}of p)if(m.kind!=="started"){if(m.kind==="failed"){d(i.instance),c.error("manager",`Failed to start ${i.config.name}: ${m.error}`);continue}u++,c.warn("manager",`Startup pending for ${i.config.name}, continue retrying in background`),i.startPromise.then(()=>{c.info("manager",`Delayed start succeeded: ${i.config.name}`)}).catch(_=>{d(i.instance),c.error("manager",`Delayed start failed: ${i.config.name}: ${_}`)})}if(this.instances.length>0){const i=Math.max(0,this.instances.length-u);c.info("manager",`${i}/${a.length} agent(s) running now`)}u>0&&c.warn("manager",`${u} agent(s) still connecting in background`)}if(this.instances.length===0&&a.length>0)throw new Error("All agent configurations failed to start");if(a.length>0){const l=a[0].config;this.upgradeChecker=new R([{apiKey:l.aibot.apiKey,wsUrl:l.aibot.url}],()=>this.instances.some(s=>s.getStatus().busy)),await this.upgradeChecker.start()}}async stop(){c.info("manager","Stopping all agents..."),this.upgradeChecker?.stop(),await Promise.allSettled(this.instances.map(e=>e.stop())),await this.globalConfigStore?.flush(),this.instances=[],D(),c.info("manager","All stopped")}getAgentsStatus(){return this.instances.map(e=>e.getStatus())}async addAgent(e){const t=E(e);if(this.instances.some(a=>a.name===t.name))throw new Error(`Agent "${t.name}" already exists`);const o=new S(t,this.globalConfigStore);o.setUpgradeTrigger(()=>this.upgradeChecker?.triggerCheck()),await o.start(),this.instances.push(o),this.configMap.set(t.name,t),this.persistAgentsConfig(),c.info("manager",`Added agent: ${t.name}`)}async removeAgent(e){const t=this.instances.findIndex(a=>a.name===e);if(t===-1)throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});const o=this.instances[t];this.instances.splice(t,1),this.configMap.delete(e),await o.stop(),this.persistAgentsConfig(),c.info("manager",`Removed agent: ${e}`)}persistAgentsConfig(){const e=A(this.configDir,"agents.json");try{const t=[];for(const[,a]of this.configMap)t.push({name:a.name,ws_url:a.aibot.url,agent_id:a.aibot.agentId,api_key:a.aibot.apiKey,client_type:a.aibot.clientType});B(e,JSON.stringify({agents:t},null,4)+`
2
2
  `,"utf-8")}catch(t){c.error("manager",`Failed to persist agents config: ${t}`)}}async restartAgent(e){const t=this.instances.findIndex(u=>u.name===e);if(t===-1)throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});const o=this.configMap.get(e);if(!o)throw Object.assign(new Error(`Config for "${e}" not found`),{code:"NOT_FOUND"});await this.instances[t].stop();const r=new S(o,this.globalConfigStore);r.setUpgradeTrigger(()=>this.upgradeChecker?.triggerCheck()),await r.start(),this.instances[t]=r,c.info("manager",`Restarted agent: ${e}`)}async checkUpgrade(){return this.upgradeChecker?this.upgradeChecker.checkForUpdate():{available:!1}}triggerUpgrade(){this.upgradeChecker?.triggerCheck()}async probeAll(e={}){return le(this.instances,e)}async probeOne(e,t={}){return de(this.instances,e,t)}listInstallable(){return this.installer.listInstallable()}async installAgent(e){const t=await this.installer.install(e);return Z(t),t}getInstallProgress(e){return this.installer.getProgress(e)??null}}async function le(n,e){const t=e.concurrency??4,o=Date.now(),a=new Array(n.length);await new Promise(d=>{let f=0,p=0;const i=n.length;if(i===0){d();return}function m(g){const w=n[g];w.probe(e).then(b=>{a[g]=b,_()},b=>{a[g]={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:b?.message??String(b)}},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}},_()})}function _(){p++,f<i?m(f++):p===i&&d()}const h=Math.min(t,i);for(let g=0;g<h;g++)m(f++)});const r=a.filter(d=>d.status==="healthy").length,u=a.filter(d=>d.status==="degraded").length,l=a.filter(d=>d.status==="unavailable").length,s=await ne();return{ok:r===a.length&&a.length>0,total:a.length,healthy:r,degraded:u,unavailable:l,installed_clients:s,agents:a,probed_at:o,duration_ms:Date.now()-o}}async function de(n,e,t){const o=n.find(a=>a.name===e);if(!o)throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});return o.probe(t)}export{ke as Manager,ne as probeInstalledClientCommands,le as probeInstances,de as probeOneInstance};
@@ -1 +1 @@
1
- import*as i from"node:net";const e={bind:"127.0.0.1",port:0,endpoint:"/mcp",sessionTimeoutMs:18e5,invokeTimeoutMs:3e4};function n(o){const u={bind:o?.bind??e.bind,port:o?.port??e.port,endpoint:o?.endpoint??e.endpoint,sessionTimeoutMs:o?.sessionTimeoutMs??e.sessionTimeoutMs,invokeTimeoutMs:o?.invokeTimeoutMs??e.invokeTimeoutMs,allowedOrigins:o?.allowedOrigins,allowedHosts:o?.allowedHosts};return s(u.bind),u.port!==0&&t(u.port),r(u.sessionTimeoutMs),u}function s(o){if(!o||!i.isIPv4(o)&&!i.isIPv6(o))throw new Error(`\u914D\u7F6E\u6821\u9A8C\u5931\u8D25: bind \u5730\u5740 "${o}" \u4E0D\u662F\u5408\u6CD5\u7684 IPv4 \u6216 IPv6 \u5730\u5740`)}function t(o){if(!Number.isInteger(o)||o<1||o>65535)throw new Error(`\u914D\u7F6E\u6821\u9A8C\u5931\u8D25: port \u503C ${o} \u4E0D\u5728\u5408\u6CD5\u8303\u56F4 1-65535 \u5185\u6216\u4E0D\u662F\u6574\u6570`)}function r(o){if(!Number.isInteger(o)||o<1e3||o>864e5)throw new Error(`\u914D\u7F6E\u6821\u9A8C\u5931\u8D25: session_timeout_ms \u503C ${o} \u4E0D\u5728\u5408\u6CD5\u8303\u56F4 1000-86400000 \u5185`)}export{n as createDefaultGatewayConfig};
1
+ import*as n from"node:net";const i={bind:"127.0.0.1",port:0,endpoint:"/mcp",sessionTimeoutMs:18e5,invokeTimeoutMs:3e4};function s(u){const e={bind:u?.bind??i.bind,port:u?.port??i.port,endpoint:u?.endpoint??i.endpoint,sessionTimeoutMs:u?.sessionTimeoutMs??i.sessionTimeoutMs,invokeTimeoutMs:u?.invokeTimeoutMs??i.invokeTimeoutMs,allowedOrigins:u?.allowedOrigins,allowedHosts:u?.allowedHosts};return t(e.bind),e.port!==0&&o(e.port),r(e.sessionTimeoutMs),e}function t(u){if(!u||!n.isIPv4(u)&&!n.isIPv6(u))throw new Error(`\u914D\u7F6E\u6821\u9A8C\u5931\u8D25: bind \u5730\u5740 "${u}" \u4E0D\u662F\u5408\u6CD5\u7684 IPv4 \u6216 IPv6 \u5730\u5740`)}function o(u){if(!Number.isInteger(u)||u<1||u>65535)throw new Error(`\u914D\u7F6E\u6821\u9A8C\u5931\u8D25: port \u503C ${u} \u4E0D\u5728\u5408\u6CD5\u8303\u56F4 1-65535 \u5185\u6216\u4E0D\u662F\u6574\u6570`)}function r(u){if(!Number.isInteger(u)||u<1e3||u>864e5)throw new Error(`\u914D\u7F6E\u6821\u9A8C\u5931\u8D25: session_timeout_ms \u503C ${u} \u4E0D\u5728\u5408\u6CD5\u8303\u56F4 1000-86400000 \u5185`)}export{s as createDefaultGatewayConfig};
@@ -1 +1 @@
1
- const r=3e4;class c{connectionManager;onDisconnected;bindings=new Map;constructor(n,i){this.connectionManager=n,this.onDisconnected=i}async bind(n,i){if(this.bindings.has(n))throw new Error(`Session ${n} is already bound to a connection`);const e=await this.connectWithTimeout(i),t=[],s=e.onDisconnected(()=>{this.removeBinding(n),this.onDisconnected(n)});return t.push(s),this.bindings.set(n,{sessionId:n,handle:e,subscriptions:t}),e}getHandle(n){return this.bindings.get(n)?.handle}unbind(n){const i=this.bindings.get(n);if(i){this.bindings.delete(n);for(const e of i.subscriptions)e();i.handle.disconnect()}}unbindAll(){const n=[...this.bindings.keys()];for(const i of n)this.unbind(i)}connectWithTimeout(n){return new Promise((i,e)=>{let t=!1;const s=setTimeout(()=>{t||(t=!0,e(new Error("Connection bind timeout after 30000ms")))},3e4);this.connectionManager.connect({agentId:n.agentId,apiKey:n.apiKey,url:n.wsUrl,clientType:n.clientType,capabilities:["agent_invoke"],adapterHint:`${n.clientType}/base`},{maxRetries:0}).then(o=>{t?o.disconnect():(t=!0,clearTimeout(s),i(o))}).catch(o=>{t||(t=!0,clearTimeout(s),e(o))})})}removeBinding(n){const i=this.bindings.get(n);if(i){this.bindings.delete(n);for(const e of i.subscriptions)e()}}}export{c as ConnectionBindingImpl};
1
+ const a=3e4;class c{connectionManager;onDisconnected;bindings=new Map;constructor(n,i){this.connectionManager=n,this.onDisconnected=i}async bind(n,i){if(this.bindings.has(n))throw new Error(`Session ${n} is already bound to a connection`);const e=await this.connectWithTimeout(i),t=[],s=e.onDisconnected(()=>{this.removeBinding(n),this.onDisconnected(n)});return t.push(s),this.bindings.set(n,{sessionId:n,handle:e,subscriptions:t}),e}getHandle(n){return this.bindings.get(n)?.handle}unbind(n){const i=this.bindings.get(n);if(i){this.bindings.delete(n);for(const e of i.subscriptions)e();i.handle.disconnect()}}unbindAll(){const n=[...this.bindings.keys()];for(const i of n)this.unbind(i)}connectWithTimeout(n){return new Promise((i,e)=>{let t=!1;const s=setTimeout(()=>{t||(t=!0,e(new Error("Connection bind timeout after 30000ms")))},3e4);this.connectionManager.connect({agentId:n.agentId,apiKey:n.apiKey,url:n.wsUrl,clientType:n.clientType,capabilities:["agent_invoke"],adapterHint:`${n.clientType}/base`},{maxRetries:0}).then(o=>{t?o.disconnect():(t=!0,clearTimeout(s),i(o))}).catch(o=>{t||(t=!0,clearTimeout(s),e(o))})})}removeBinding(n){const i=this.bindings.get(n);if(i){this.bindings.delete(n);for(const e of i.subscriptions)e()}}}export{c as ConnectionBindingImpl};
@@ -1 +1 @@
1
- function a(o){const e=new Set([`http://127.0.0.1:${o.serverPort}`,`http://localhost:${o.serverPort}`,...o.allowedOrigins]),t=new Set([`127.0.0.1:${o.serverPort}`,`localhost:${o.serverPort}`,...o.allowedHosts]);return{validateRequest(s){const r=i(s,e);if(!r.ok)return r;const n=l(s,t);return n.ok?{ok:!0}:n}}}function i(o,e){const t=o.headers.origin;return t?e.has(t)?{ok:!0}:{ok:!1,statusCode:403,message:`Origin not allowed: ${t}`}:{ok:!0}}function l(o,e){const t=o.headers.host;return t?e.has(t)?{ok:!0}:{ok:!1,statusCode:403,message:`Host not allowed: ${t}`}:{ok:!1,statusCode:403,message:"Missing Host header"}}export{a as createSecurityPolicy};
1
+ function a(t){const o=new Set([`http://127.0.0.1:${t.serverPort}`,`http://localhost:${t.serverPort}`,...t.allowedOrigins]),e=new Set([`127.0.0.1:${t.serverPort}`,`localhost:${t.serverPort}`,...t.allowedHosts]);return{validateRequest(s){const r=i(s,o);if(!r.ok)return r;const n=l(s,e);return n.ok?{ok:!0}:n}}}function i(t,o){const e=t.headers.origin;return e?o.has(e)?{ok:!0}:{ok:!1,statusCode:403,message:`Origin not allowed: ${e}`}:{ok:!0}}function l(t,o){const e=t.headers.host;return e?o.has(e)?{ok:!0}:{ok:!1,statusCode:403,message:`Host not allowed: ${e}`}:{ok:!1,statusCode:403,message:"Missing Host header"}}export{a as createSecurityPolicy};
@@ -1 +1 @@
1
- import{toolCallToInvoke as i}from"../../core/mcp/tools.js";import{ToolRegistryImpl as l}from"./tool-registry.js";import{validateToolArgs as a}from"./tool-schemas.js";import{isEventTool as p,executeEventTool as d}from"./event-tool-executor.js";class y{registry;constructor(){this.registry=new l}async execute(t,e,r,n){if(!this.registry.hasTool(e))return this.errorResult(`\u672A\u77E5\u5DE5\u5177: ${e}`);const s=a(e,r);if(!s.valid)return this.errorResult(`\u53C2\u6570\u6821\u9A8C\u5931\u8D25: ${s.error}`);if(t.status!=="ready")return this.errorResult(`\u8FDE\u63A5\u4E0D\u53EF\u7528: \u5F53\u524D\u72B6\u6001\u4E3A ${t.status}`);if(p(e))return this.executeEventTool(t,e,r);const o=i(e,r);try{const u=await t.agentInvoke(o.action,o.params,n);return this.normalizeResult(u)}catch(u){const c=u instanceof Error?u.message:String(u);return c.toLowerCase().includes("timeout")?this.errorResult(`\u8C03\u7528\u8D85\u65F6: ${c}`):this.errorResult(`\u8C03\u7528\u5931\u8D25: ${c}`)}}normalizeResult(t){if(t==null||typeof t!="object")return this.successResult(t??null);const e=t,r=typeof e.code=="number"?e.code:0;if(r===0){const s="data"in e?e.data:null;return this.successResult(s??null)}const n=typeof e.msg=="string"?e.msg:"\u672A\u77E5\u9519\u8BEF";return this.errorResult(`\u4E0A\u6E38\u9519\u8BEF [code=${r}]: ${n}`)}successResult(t){return{content:[{type:"text",text:JSON.stringify(t)}],isError:!1}}errorResult(t){return{content:[{type:"text",text:t}],isError:!0}}async executeEventTool(t,e,r){return e==="grix_access_control"?this.executeAccessControl(t,r):d(t,e,r)}async executeAccessControl(t,e){const r=String(e.action??""),n={pair_approve:"pair_approve",pair_deny:"pair_deny",allow_sender:"sender_allow",remove_sender:"sender_remove",set_policy:"policy_set"}[r];if(!n)return this.errorResult(`\u672A\u77E5 access_control action: ${r}`);const s={};e.code!=null&&(s.code=e.code),e.sender_id!=null&&(s.sender_id=e.sender_id),e.policy!=null&&(s.policy=e.policy);try{const o=await t.agentInvoke("claude_access_control",{verb:n,payload:s},3e4);return this.successResult(o)}catch(o){const u=o instanceof Error?o.message:String(o);return this.errorResult(`access_control \u8C03\u7528\u5931\u8D25: ${u}`)}}}export{y as ToolExecutorImpl};
1
+ import{toolCallToInvoke as i}from"../../core/mcp/tools.js";import{ToolRegistryImpl as l}from"./tool-registry.js";import{validateToolArgs as a}from"./tool-schemas.js";import{isEventTool as p,executeEventTool as d}from"./event-tool-executor.js";class y{registry;constructor(){this.registry=new l}async execute(r,e,t,n){if(!this.registry.hasTool(e))return this.errorResult(`\u672A\u77E5\u5DE5\u5177: ${e}`);const s=a(e,t);if(!s.valid)return this.errorResult(`\u53C2\u6570\u6821\u9A8C\u5931\u8D25: ${s.error}`);if(r.status!=="ready")return this.errorResult(`\u8FDE\u63A5\u4E0D\u53EF\u7528: \u5F53\u524D\u72B6\u6001\u4E3A ${r.status}`);if(p(e))return this.executeEventTool(r,e,t);const o=i(e,t);try{const u=await r.agentInvoke(o.action,o.params,n);return this.normalizeResult(u)}catch(u){const c=u instanceof Error?u.message:String(u);return c.toLowerCase().includes("timeout")?this.errorResult(`\u8C03\u7528\u8D85\u65F6: ${c}`):this.errorResult(`\u8C03\u7528\u5931\u8D25: ${c}`)}}normalizeResult(r){if(r==null||typeof r!="object")return this.successResult(r??null);const e=r,t=typeof e.code=="number"?e.code:0;if(t===0){const s="data"in e?e.data:null;return this.successResult(s??null)}const n=typeof e.msg=="string"?e.msg:"\u672A\u77E5\u9519\u8BEF";return this.errorResult(`\u4E0A\u6E38\u9519\u8BEF [code=${t}]: ${n}`)}successResult(r){return{content:[{type:"text",text:JSON.stringify(r)}],isError:!1}}errorResult(r){return{content:[{type:"text",text:r}],isError:!0}}async executeEventTool(r,e,t){return e==="grix_access_control"?this.executeAccessControl(r,t):d(r,e,t)}async executeAccessControl(r,e){const t=String(e.action??""),n={pair_approve:"pair_approve",pair_deny:"pair_deny",allow_sender:"sender_allow",remove_sender:"sender_remove",set_policy:"policy_set"}[t];if(!n)return this.errorResult(`\u672A\u77E5 access_control action: ${t}`);const s={};e.code!=null&&(s.code=e.code),e.sender_id!=null&&(s.sender_id=e.sender_id),e.policy!=null&&(s.policy=e.policy);try{const o=await r.agentInvoke("claude_access_control",{verb:n,payload:s},3e4);return this.successResult(o)}catch(o){const u=o instanceof Error?o.message:String(o);return this.errorResult(`access_control \u8C03\u7528\u5931\u8D25: ${u}`)}}}export{y as ToolExecutorImpl};
@@ -1 +1 @@
1
- import{TOOLS as s,EVENT_TOOLS as t}from"../../core/mcp/tools.js";const e=new Set(["grix_query","grix_group","grix_message_send","grix_message_unsend","grix_admin"]),r=new Set(["grix_reply","grix_complete","grix_event_ack","grix_composing","grix_access_control","grix_status"]);class a{tools;toolMap;constructor(){this.tools=[...s.filter(o=>e.has(o.name)),...t.filter(o=>r.has(o.name))],this.toolMap=new Map(this.tools.map(o=>[o.name,o]))}getTools(){return this.tools}getTool(o){return this.toolMap.get(o)}hasTool(o){return this.toolMap.has(o)}}export{a as ToolRegistryImpl};
1
+ import{TOOLS as o,EVENT_TOOLS as s}from"../../core/mcp/tools.js";const e=new Set(["grix_query","grix_group","grix_message_send","grix_message_unsend","grix_admin"]),r=new Set(["grix_reply","grix_complete","grix_event_ack","grix_composing","grix_access_control","grix_status"]);class a{tools;toolMap;constructor(){this.tools=[...o.filter(t=>e.has(t.name)),...s.filter(t=>r.has(t.name))],this.toolMap=new Map(this.tools.map(t=>[t.name,t]))}getTools(){return this.tools}getTool(t){return this.toolMap.get(t)}hasTool(t){return this.toolMap.has(t)}}export{a as ToolRegistryImpl};