grix-connector 1.0.2 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/dist/adapter/acp/acp-adapter.js +10 -8
  2. package/dist/adapter/claude/claude-adapter.js +16 -15
  3. package/dist/adapter/claude/claude-bridge-server.js +1 -1
  4. package/dist/adapter/claude/mcp-http-launcher.js +2 -2
  5. package/dist/adapter/codewhale/codewhale-adapter.js +4 -3
  6. package/dist/adapter/codex/codex-bridge.js +6 -6
  7. package/dist/adapter/cursor/cursor-adapter.js +3 -2
  8. package/dist/adapter/opencode/opencode-adapter.js +7 -4
  9. package/dist/adapter/openhuman/openhuman-adapter.js +5 -2
  10. package/dist/adapter/pi/pi-adapter.js +8 -5
  11. package/dist/bridge/adapter-pool.js +1 -1
  12. package/dist/bridge/bridge.js +10 -6
  13. package/dist/bridge/deferred-events.js +1 -1
  14. package/dist/bridge/event-queue.js +1 -1
  15. package/dist/bridge/send-controller.js +4 -1
  16. package/dist/core/admin/admin-server.js +1 -0
  17. package/dist/core/admin/admin-token.js +1 -0
  18. package/dist/core/admin/index.js +1 -0
  19. package/dist/core/aibot/client.js +2 -1
  20. package/dist/core/aibot/connection-handle.js +1 -1
  21. package/dist/core/file-ops/handler.js +1 -1
  22. package/dist/core/mcp/event-tool-executor.js +1 -1
  23. package/dist/core/mcp/event-tool-port.js +0 -0
  24. package/dist/core/protocol/message-metadata.js +1 -1
  25. package/dist/core/protocol/message-reference.js +1 -1
  26. package/dist/core/protocol/payload-parser.js +6 -6
  27. package/dist/core/protocol/protocol-text.js +1 -1
  28. package/dist/core/provider-quota/kiro.js +1 -1
  29. package/dist/core/text-segmentation/safe-markdown-stream-segmenter.js +6 -6
  30. package/dist/core/upgrade/upgrade-checker.js +1 -1
  31. package/dist/grix.js +4 -3
  32. package/dist/log.js +2 -2
  33. package/dist/manager.js +2 -1
  34. package/dist/mcp/stream-http/gateway.js +1 -1
  35. package/dist/mcp/stream-http/security.js +1 -1
  36. package/dist/mcp/stream-http/tool-schemas.js +1 -1
  37. package/dist/protocol/acp-client.js +1 -1
  38. package/openclaw-plugin/index.js +15 -10
  39. package/package.json +4 -3
@@ -0,0 +1 @@
1
+ import{AdminServer as o}from"./admin-server.js";import{generateToken as i,writeTokenFile as t,readTokenFile as k}from"./admin-token.js";export{o as AdminServer,i as generateToken,k as readTokenFile,t as writeTokenFile};
@@ -1 +1,2 @@
1
- import{EventEmitter as f}from"node:events";import{randomUUID as p}from"node:crypto";import l from"node:os";import _ from"ws";import{log as u}from"../log/index.js";function k(m){return m.replace(/(?<=[\[:,\[]\s*)(\d{16,})(?=\s*[,}\]\n])/g,'"$1"')}function g(m){const e=[...m??["stream_chunk","local_action_v1","agent_invoke"]];return e.includes("event_result_ack")||e.push("event_result_ack"),e}const v="aibot-agent-api-v1",$=1;class d extends f{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;config;pendingInvokes=new Map;pendingRequests=new Map;outboundBuffer=[];constructor(e){super(),this.config={url:e.url,agentId:e.agentId,apiKey:e.apiKey,clientType:e.clientType,clientVersion:e.clientVersion??"",adapterHint:e.adapterHint??"",capabilities:g(e.capabilities),localActions:e.localActions??["exec_approve","exec_reject"],skills:e.skills}}get isConnected(){return this.connected}async connect(){return new Promise((e,t)=>{const i=new _(this.config.url);this.ws=i;const a=setTimeout(()=>{t(new Error("Auth timeout: no auth_ack received within 15s")),this.cleanupSocket()},15e3),r=++this.seq,o=setTimeout(()=>{this.pendingRequests.delete(r),t(new Error("Auth request timeout")),this.cleanupSocket()},15e3);this.pendingRequests.set(r,{expected:["auth_ack"],resolve:s=>{clearTimeout(a);const n=s.payload;n.code===0?(this.connected=!0,this.reconnectAttempts=0,n.heartbeat_sec&&(this.heartbeatSec=n.heartbeat_sec),this.startHeartbeat(),this.flushOutboundBuffer(),this.emit("auth",n),e(n)):t(new Error(`Auth failed: code=${n.code} msg=${n.msg}`))},reject:s=>{clearTimeout(a),t(s)},timer:o}),i.on("open",()=>{const s={agent_id:this.config.agentId,api_key:this.config.apiKey,client_type:this.config.clientType,protocol_version:v,contract_version:$,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:l.hostname(),platform:l.platform(),arch:l.arch(),os_release:l.release()},this.config.concurrency&&(s.concurrency=this.config.concurrency),this.sendPacket("auth",s,r)}),i.on("message",s=>{let n;try{n=JSON.parse(k(s.toString()))}catch{return}try{this.handlePacket(n)}catch(h){this.emitClientError(new Error(`handlePacket error: ${h}`))}}),i.on("close",(s,n)=>{this.connected=!1,this.stopHeartbeat(),this.rejectAllPendingRequests("websocket closed"),this.emit("close",s,n.toString()),s!==1e3&&this.attemptReconnect()}),i.on("error",s=>{this.emitClientError(s instanceof Error?s:new Error(String(s))),this.connected||t(s)})})}handlePacket(e){if(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_cancel":{this.emit("eventCancel",e.payload);break}case"queue_clear":{this.emit("queueClear",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;this.emitClientError(new Error(`Server error: code=${t.code} msg=${t.msg}`));break}case"agent_invoke_result":{this.handleInvokeResult(e.payload);break}case"send_ack":case"send_nack":case"local_action_ack":break;default:break}}sendEventAck(e){this.sendPacket("event_ack",e)}sendStreamChunk(e){this.sendPacket("client_stream_chunk",e)}sendMsg(e){this.sendPacket("send_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)}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,i=15e3){return new Promise((a,r)=>{const o=p(),s=Math.max(1e3,Math.min(i,6e4)),n=setTimeout(()=>{this.pendingInvokes.delete(o),r(new Error(`agent_invoke timeout: ${e}`))},s);this.pendingInvokes.set(o,{resolve:a,reject:r,timer:n}),this.sendPacket("agent_invoke",{invoke_id:o,action:e,params:t,timeout_ms:s})})}request(e,t,i){return new Promise((a,r)=>{const o=++this.seq,s=setTimeout(()=>{this.pendingRequests.delete(o),r(new Error(`request timeout: ${e} (expected ${i.expected.join("/")})`))},i.timeoutMs);this.pendingRequests.set(o,{expected:i.expected,resolve:a,reject:r,timer:s}),this.sendPacket(e,t,o)||(this.pendingRequests.delete(o),clearTimeout(s),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 deleteMessage(e,t,i=2e4){return this.request("delete_msg",{session_id:e,msg_id:t},{expected:["send_ack","send_nack","error"],timeoutMs:i})}async sendEventResultRequest(e,t=5e3){return this.request("event_result",e,{expected:["send_ack","send_nack","error"],timeoutMs:t})}disconnect(){this.connected=!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,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(i=>setTimeout(i,e+t)),!this.reconnecting)return;try{const i=await this.connect(),a=this.reconnectAttempts;this.reconnectAttempts=0,this.reconnecting=!1,u.info("aibot",`reconnect succeeded attempt=${a}`),this.emit("auth",i);return}catch(i){u.warn("aibot",`reconnect failed attempt=${this.reconnectAttempts} err=${i instanceof Error?i.message:i}`)}}}sendPacket(e,t,i){if(this.ws&&this.ws.readyState===_.OPEN){const a=this.ws.bufferedAmount>d.BACKPRESSURE_THRESHOLD;if(!a||!d.DROPPABLE_COMMANDS.has(e)){if(a&&d.DROPPABLE_COMMANDS.has(e))return!1;const r=i??++this.seq,o={cmd:e,seq:r,payload:t};try{const s=this.ws.readyState,n=this.ws.bufferedAmount;return this.ws.send(JSON.stringify(o),h=>{if(e==="event_result"){const c=t;h?u.warn("aibot",`event_result ws send callback failed event=${c.event_id??""} status=${c.status??""} seq=${r} readyState=${s} bufferedAmount=${n} err=${h.message}`):u.info("aibot",`event_result ws send callback ok event=${c.event_id??""} status=${c.status??""} seq=${r} readyState=${s} bufferedAmount=${n}`)}else if(e==="client_stream_chunk"){const c=t;h?u.warn("aibot",`stream_chunk ws send callback failed event=${c.event_id??""} session=${c.session_id??""} seq=${r} chunk_seq=${c.chunk_seq??""} is_finish=${c.is_finish??""} readyState=${s} bufferedAmount=${n} err=${h.message}`):u.info("aibot",`stream_chunk ws send ok event=${c.event_id??""} session=${c.session_id??""} seq=${r} chunk_seq=${c.chunk_seq??""} is_finish=${c.is_finish??""} readyState=${s} bufferedAmount=${n}`)}}),!0}catch(s){return this.emitClientError(new Error(`sendPacket failed: ${s}`)),!1}}}if(d.DROPPABLE_COMMANDS.has(e)||i!==void 0)return!1;if(this.outboundBuffer.length>=d.MAX_OUTBOUND_BUFFER_SIZE&&(this.outboundBuffer=this.outboundBuffer.filter(a=>d.BUFFER_OVERFLOW_RETAIN_COMMANDS.has(a.cmd)),this.outboundBuffer.length>=d.MAX_OUTBOUND_BUFFER_SIZE&&this.outboundBuffer.shift()),this.outboundBuffer.push({cmd:e,payload:t}),e==="client_stream_chunk"){const a=t;u.info("aibot",`stream_chunk buffered (ws not open) event=${a.event_id??""} session=${a.session_id??""} chunk_seq=${a.chunk_seq??""} is_finish=${a.is_finish??""} ws=${this.ws?`state=${this.ws.readyState}`:"null"}`)}return!1}async sendEventResultReliable(e){for(let r=1;r<=3;r++){const o=this.ws?.readyState??-1,s=this.ws?.bufferedAmount??0;u.info("aibot",`event_result send attempt event=${e.event_id} status=${e.status} attempt=${r}/3 readyState=${o} bufferedAmount=${s}`);try{const n=await this.sendEventResultRequest(e,5e3);if(n.cmd==="send_ack"){const c=n.payload;u.info("aibot",`event_result ack event=${e.event_id} status=${e.status} attempt=${r}/3 ack_event=${c.event_id??""} ack_status=${c.status??""}`);return}const h=n.payload;u.warn("aibot",`event_result rejected event=${e.event_id} status=${e.status} attempt=${r}/3 cmd=${n.cmd} code=${h.code??""} msg=${h.msg??""}`);return}catch(n){const h=n instanceof Error?n.message:String(n);if(u.warn("aibot",`event_result attempt failed event=${e.event_id} status=${e.status} attempt=${r}/3 err=${h}`),r===3){this.emitClientError(new Error(`event_result ack failed after 3 attempts: event=${e.event_id} status=${e.status}`));return}await new Promise(c=>setTimeout(c,750*r))}}}emitClientError(e){if(this.listenerCount("error")===0){u.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:i}of e){const a={cmd:t,seq:++this.seq,payload:i};try{this.ws.send(JSON.stringify(a))}catch{break}}}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{d as AibotClient};
1
+ import{EventEmitter as m}from"node:events";import{randomUUID as k}from"node:crypto";import l from"node:os";import _ from"ws";import{log as d}from"../log/index.js";function $(f){return f.replace(/(?<=[\[:,\[]\s*)(\d{16,})(?=\s*[,}\]\n])/g,'"$1"')}function v(f){const e=[...f??["stream_chunk","local_action_v1","agent_invoke"]];return e.includes("event_result_ack")||e.push("event_result_ack"),e}const g="aibot-agent-api-v1",p=1;class h extends m{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;config;pendingInvokes=new Map;pendingRequests=new Map;outboundBuffer=[];ackPolicy=null;constructor(e){super(),this.config={url:e.url,agentId:e.agentId,apiKey:e.apiKey,clientType:e.clientType,clientVersion:e.clientVersion??"",adapterHint:e.adapterHint??"",capabilities:v(e.capabilities),localActions:e.localActions??["exec_approve","exec_reject"],skills:e.skills}}get isConnected(){return this.connected}async connect(){return new Promise((e,t)=>{const i=new _(this.config.url);this.ws=i;const a=setTimeout(()=>{t(new Error("Auth timeout: no auth_ack received within 15s")),this.cleanupSocket()},15e3),r=++this.seq,u=setTimeout(()=>{this.pendingRequests.delete(r),t(new Error("Auth request timeout")),this.cleanupSocket()},15e3);this.pendingRequests.set(r,{expected:["auth_ack"],resolve:s=>{clearTimeout(a);const n=s.payload;n.code===0?(this.connected=!0,this.reconnectAttempts=0,n.heartbeat_sec&&(this.heartbeatSec=n.heartbeat_sec),n.ack_policy&&(this.ackPolicy=n.ack_policy,d.info("aibot",`ack_policy received: push_ack_timeout_ms=${n.ack_policy.push_ack_timeout_ms??"default"} max_retries=${n.ack_policy.max_retries??"default"} timeout_action=${n.ack_policy.timeout_action??"default"}`)),this.startHeartbeat(),this.flushOutboundBuffer(),this.emit("auth",n),e(n)):t(new Error(`Auth failed: code=${n.code} msg=${n.msg}`))},reject:s=>{clearTimeout(a),t(s)},timer:u}),i.on("open",()=>{const s={agent_id:this.config.agentId,api_key:this.config.apiKey,client_type:this.config.clientType,protocol_version:g,contract_version:p,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:l.hostname(),platform:l.platform(),arch:l.arch(),os_release:l.release()},this.config.concurrency&&(s.concurrency=this.config.concurrency),this.sendPacket("auth",s,r)}),i.on("message",s=>{let n;try{n=JSON.parse($(s.toString()))}catch{return}try{this.handlePacket(n)}catch(o){this.emitClientError(new Error(`handlePacket error: ${o}`))}}),i.on("close",(s,n)=>{this.connected=!1,this.stopHeartbeat(),this.rejectAllPendingRequests("websocket closed"),this.emit("close",s,n.toString()),s!==1e3&&this.attemptReconnect()}),i.on("error",s=>{this.emitClientError(s instanceof Error?s:new Error(String(s))),this.connected||t(s)})})}handlePacket(e){if(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"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,i=[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}${i?` ${i}`:""}`));break}case"agent_invoke_result":{this.handleInvokeResult(e.payload);break}case"send_ack":case"send_nack":case"local_action_ack":break;default:break}}sendEventAck(e){this.sendPacket("event_ack",e)||d.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||(d.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)||d.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)}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,i=15e3){return new Promise((a,r)=>{const u=k(),s=Math.max(1e3,Math.min(i,6e4)),n=setTimeout(()=>{this.pendingInvokes.delete(u),r(new Error(`agent_invoke timeout: ${e}`))},s);this.pendingInvokes.set(u,{resolve:a,reject:r,timer:n}),this.sendPacket("agent_invoke",{invoke_id:u,action:e,params:t,timeout_ms:s})})}request(e,t,i){return new Promise((a,r)=>{const u=++this.seq,s=setTimeout(()=>{this.pendingRequests.delete(u),r(new Error(`request timeout: ${e} (expected ${i.expected.join("/")})`))},i.timeoutMs);this.pendingRequests.set(u,{expected:i.expected,resolve:a,reject:r,timer:s}),this.sendPacket(e,t,u)||(this.pendingRequests.delete(u),clearTimeout(s),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,i=2e4){return this.request("delete_msg",{session_id:e,msg_id:t},{expected:["send_ack","send_nack","error"],timeoutMs:i})}async sendEventResultRequest(e,t=5e3){return this.request("event_result",e,{expected:["send_ack","send_nack","error"],timeoutMs:t})}disconnect(){this.connected=!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,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(i=>setTimeout(i,e+t)),!this.reconnecting)return;try{const i=await this.connect(),a=this.reconnectAttempts;this.reconnectAttempts=0,this.reconnecting=!1,d.info("aibot",`reconnect succeeded attempt=${a}`),this.emit("auth",i);return}catch(i){d.warn("aibot",`reconnect failed attempt=${this.reconnectAttempts} err=${i instanceof Error?i.message:i}`)}}}sendPacket(e,t,i){if(this.ws&&this.ws.readyState===_.OPEN){const a=this.ws.bufferedAmount>h.BACKPRESSURE_THRESHOLD;if(!a||!h.DROPPABLE_COMMANDS.has(e)){if(a&&h.DROPPABLE_COMMANDS.has(e))return!1;const r=i??++this.seq,u={cmd:e,seq:r,payload:t};try{const s=this.ws.readyState,n=this.ws.bufferedAmount;return this.ws.send(JSON.stringify(u),o=>{if(e==="event_result"){const c=t;o?d.warn("aibot",`event_result ws send callback failed event=${c.event_id??""} status=${c.status??""} seq=${r} readyState=${s} bufferedAmount=${n} err=${o.message}`):d.info("aibot",`event_result ws send callback ok event=${c.event_id??""} status=${c.status??""} seq=${r} readyState=${s} bufferedAmount=${n}`)}else if(e==="client_stream_chunk"){const c=t;o?d.warn("aibot",`stream_chunk ws send failed event=${c.event_id??""} session=${c.session_id??""} seq=${r} chunk_seq=${c.chunk_seq??""} is_finish=${c.is_finish??""} readyState=${s} bufferedAmount=${n} err=${o.message}`):d.info("aibot",`stream_chunk ws send ok event=${c.event_id??""} session=${c.session_id??""} seq=${r} chunk_seq=${c.chunk_seq??""} is_finish=${c.is_finish??""} readyState=${s} bufferedAmount=${n}`)}else if(e==="event_ack"){const c=t;o?d.warn("aibot",`event_ack ws send failed event=${c.event_id??""} seq=${r} readyState=${s} bufferedAmount=${n} err=${o.message}`):d.info("aibot",`event_ack ws send ok event=${c.event_id??""} seq=${r} readyState=${s} bufferedAmount=${n}`)}else if(e==="send_msg"){const c=t;o?d.warn("aibot",`send_msg ws send failed event=${c.event_id??""} session=${c.session_id??""} seq=${r} readyState=${s} bufferedAmount=${n} err=${o.message}`):d.info("aibot",`send_msg ws send ok event=${c.event_id??""} session=${c.session_id??""} seq=${r} readyState=${s} bufferedAmount=${n}`)}else if(o){const c=t;d.warn("aibot",`${e} ws send failed seq=${r} session=${c.session_id??""} event=${c.event_id??""} client_msg_id=${c.client_msg_id??""} readyState=${s} bufferedAmount=${n} err=${o.message}`)}}),!0}catch(s){return this.emitClientError(new Error(`sendPacket failed: ${s}`)),!1}}}if(h.DROPPABLE_COMMANDS.has(e)||i!==void 0)return!1;if(this.outboundBuffer.length>=h.MAX_OUTBOUND_BUFFER_SIZE&&(this.outboundBuffer=this.outboundBuffer.filter(a=>h.BUFFER_OVERFLOW_RETAIN_COMMANDS.has(a.cmd)),this.outboundBuffer.length>=h.MAX_OUTBOUND_BUFFER_SIZE&&this.outboundBuffer.shift()),this.outboundBuffer.push({cmd:e,payload:t}),e==="client_stream_chunk"){const a=t;d.info("aibot",`stream_chunk buffered (ws not open) event=${a.event_id??""} session=${a.session_id??""} chunk_seq=${a.chunk_seq??""} is_finish=${a.is_finish??""} ws=${this.ws?`state=${this.ws.readyState}`:"null"}`)}return!1}async sendEventResultReliable(e){const t=this.ackPolicy?.max_retries??3,i=this.ackPolicy?.push_ack_timeout_ms??5e3,a=750;for(let r=1;r<=t;r++){const u=this.ws?.readyState??-1,s=this.ws?.bufferedAmount??0;d.info("aibot",`event_result send attempt event=${e.event_id} status=${e.status} attempt=${r}/${t} readyState=${u} bufferedAmount=${s}`);try{const n=await this.sendEventResultRequest(e,i);if(n.cmd==="send_ack"){const c=n.payload;d.info("aibot",`event_result ack event=${e.event_id} status=${e.status} attempt=${r}/${t} ack_event=${c.event_id??""} ack_status=${c.status??""}`);return}const o=n.payload;if(d.warn("aibot",`event_result rejected event=${e.event_id} status=${e.status} attempt=${r}/${t} cmd=${n.cmd} code=${o.code??""} msg=${o.msg??""}${o.ref_cmd?` ref_cmd=${o.ref_cmd}`:""}${o.ref_id?` ref_id=${o.ref_id}`:""}`),o.code===4003){d.warn("aibot",`event_result stopping retries: 4003 ownership denied event=${e.event_id}`);return}return}catch(n){const o=n instanceof Error?n.message:String(n);if(d.warn("aibot",`event_result attempt failed event=${e.event_id} status=${e.status} attempt=${r}/${t} err=${o}`),r===t){this.emitClientError(new Error(`event_result ack failed after ${t} attempts: event=${e.event_id} status=${e.status}`));return}await new Promise(c=>setTimeout(c,a*r))}}}emitClientError(e){if(this.listenerCount("error")===0){d.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:i}of e){const a={cmd:t,seq:++this.seq,payload:i};try{this.ws.send(JSON.stringify(a))}catch{break}}}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 +1 @@
1
- class c{client;_status="ready";connectedAt;listeners=[];constructor(e){this.client=e,this.connectedAt=Date.now(),this.addInternalListener("close",()=>{this._status!=="closing"&&(this._status="reconnecting")}),this.addInternalListener("auth",()=>{this._status="ready"}),this.addInternalListener("disconnected",()=>{this._status!=="closing"&&this._status!=="closed"&&(this._status="reconnecting")})}get status(){return this._status}getStatusSnapshot(){return{status:this._status,connectedAt:this.connectedAt,reconnectAttempts:0}}sendEventAck(e){this.client.sendEventAck(e)}sendStreamChunk(e){this.client.sendStreamChunk(e)}sendMsg(e){this.client.sendMsg(e)}sendEventResult(e){this.client.sendEventResult(e)}sendLocalActionResult(e){this.client.sendLocalActionResult(e)}sendEventStopAck(e){this.client.sendEventStopAck(e)}sendEventStopResult(e){this.client.sendEventStopResult(e)}sendSessionActivitySet(e){this.client.sendSessionActivitySet(e)}sendCodexEvent(e){this.client.sendCodexEvent(e)}sendUpdateBindingCard(e){this.client.sendUpdateBindingCard(e)}sendPing(){this.client.sendPing()}sendEventState(e){this.client.sendEventState(e)}sendEventCancelResult(e){this.client.sendEventCancelResult(e)}sendQueueClearResult(e){this.client.sendQueueClearResult(e)}sendQueueSnapshot(e){this.client.sendQueueSnapshot(e)}agentInvoke(e,t,s){return this.client.agentInvoke(e,t,s)}sendStreamChunkRequest(e,t){return this.client.sendStreamChunkRequest(e,t)}sendText(e,t){return this.client.sendText(e,t)}sendMedia(e,t){return this.client.sendMedia(e,t)}deleteMessage(e,t,s){return this.client.deleteMessage(e,t,s)}sendEventResultRequest(e,t){return this.client.sendEventResultRequest(e,t)}sendCodexEventReliable(e,t){return this.client.request("codex_event",e,{expected:["send_ack","send_nack","error"],timeoutMs:t??2e4})}onEvent(e){return this.subscribe("event",e)}onLocalAction(e){return this.subscribe("localAction",e)}onStop(e){return this.subscribe("stop",e)}onRevoke(e){return this.subscribe("revoke",e)}onEventCancel(e){return this.subscribe("eventCancel",e)}onQueueClear(e){return this.subscribe("queueClear",e)}onKicked(e){return this.subscribe("kicked",e)}onError(e){return this.subscribe("error",e)}onClose(e){return this.subscribe("close",e)}onDisconnected(e){return this.subscribe("disconnected",e)}disconnect(){this._status="closing",this.removeAllListeners(),this.client.disconnect(),this._status="closed"}subscribe(e,t){return this.client.on(e,t),this.listeners.push({event:e,handler:t}),()=>{this.client.removeListener(e,t);const s=this.listeners.findIndex(n=>n.event===e&&n.handler===t);s>=0&&this.listeners.splice(s,1)}}addInternalListener(e,t){this.client.on(e,t),this.listeners.push({event:e,handler:t})}removeAllListeners(){for(const{event:e,handler:t}of this.listeners)this.client.removeListener(e,t);this.listeners.length=0}}export{c as AibotConnectionHandleImpl};
1
+ class r{client;_status="ready";connectedAt;listeners=[];constructor(e){this.client=e,this.connectedAt=Date.now(),this.addInternalListener("close",()=>{this._status!=="closing"&&(this._status="reconnecting")}),this.addInternalListener("auth",()=>{this._status="ready"}),this.addInternalListener("disconnected",()=>{this._status!=="closing"&&this._status!=="closed"&&(this._status="reconnecting")})}get status(){return this._status}getStatusSnapshot(){return{status:this._status,connectedAt:this.connectedAt,reconnectAttempts:0}}sendEventAck(e){this.client.sendEventAck(e)}sendStreamChunk(e){this.client.sendStreamChunk(e)}sendMsg(e){this.client.sendMsg(e)}editMsg(e){this.client.editMsg(e)}sendEventResult(e){this.client.sendEventResult(e)}sendLocalActionResult(e){this.client.sendLocalActionResult(e)}sendEventStopAck(e){this.client.sendEventStopAck(e)}sendEventStopResult(e){this.client.sendEventStopResult(e)}sendSessionActivitySet(e){this.client.sendSessionActivitySet(e)}sendCodexEvent(e){this.client.sendCodexEvent(e)}sendUpdateBindingCard(e){this.client.sendUpdateBindingCard(e)}sendPing(){this.client.sendPing()}sendEventState(e){this.client.sendEventState(e)}sendEventCancelResult(e){this.client.sendEventCancelResult(e)}sendQueueClearResult(e){this.client.sendQueueClearResult(e)}sendQueueSnapshot(e){this.client.sendQueueSnapshot(e)}agentInvoke(e,t,s){return this.client.agentInvoke(e,t,s)}sendStreamChunkRequest(e,t){return this.client.sendStreamChunkRequest(e,t)}sendText(e,t){return this.client.sendText(e,t)}sendMedia(e,t){return this.client.sendMedia(e,t)}editMessage(e,t){return this.client.editMessage(e,t)}deleteMessage(e,t,s){return this.client.deleteMessage(e,t,s)}sendEventResultRequest(e,t){return this.client.sendEventResultRequest(e,t)}sendCodexEventReliable(e,t){return this.client.request("codex_event",e,{expected:["send_ack","send_nack","error"],timeoutMs:t??2e4})}onEvent(e){return this.subscribe("event",e)}onLocalAction(e){return this.subscribe("localAction",e)}onStop(e){return this.subscribe("stop",e)}onRevoke(e){return this.subscribe("revoke",e)}onEdit(e){return this.subscribe("edit",e)}onEventCancel(e){return this.subscribe("eventCancel",e)}onQueueClear(e){return this.subscribe("queueClear",e)}onKicked(e){return this.subscribe("kicked",e)}onError(e){return this.subscribe("error",e)}onClose(e){return this.subscribe("close",e)}onDisconnected(e){return this.subscribe("disconnected",e)}disconnect(){this._status="closing",this.removeAllListeners(),this.client.disconnect(),this._status="closed"}subscribe(e,t){return this.client.on(e,t),this.listeners.push({event:e,handler:t}),()=>{this.client.removeListener(e,t);const s=this.listeners.findIndex(n=>n.event===e&&n.handler===t);s>=0&&this.listeners.splice(s,1)}}addInternalListener(e,t){this.client.on(e,t),this.listeners.push({event:e,handler:t})}removeAllListeners(){for(const{event:e,handler:t}of this.listeners)this.client.removeListener(e,t);this.listeners.length=0}}export{r as AibotConnectionHandleImpl};
@@ -1 +1 @@
1
- import{mkdir as m}from"node:fs/promises";import{join as c,basename as l}from"node:path";import{homedir as u}from"node:os";import{listFiles as _}from"./list-files.js";function d(){return u()}async function f(a,s){const o=a.params??{},t=o.parent_id?.trim(),i=o.show_hidden??!1;let e;t?e=t:e=s.resolveCwd()||d();try{return{status:"ok",result:{files:await _(e,i),current_path:e}}}catch(r){return r?.code==="ENOENT"?{status:"failed",error_code:"path_not_found",error_msg:`Directory not found: ${e}`}:r?.code==="ENOTDIR"?{status:"failed",error_code:"not_a_directory",error_msg:`Not a directory: ${e}`}:{status:"failed",error_code:"list_failed",error_msg:String(r.message||r)}}}async function p(a,s){const o=a.params??{},t=o.name?.trim(),i=o.parent_id?.trim();if(!t)return{status:"failed",error_code:"name_required",error_msg:"Folder name is required"};if(/[/\\]/.test(t))return{status:"failed",error_code:"invalid_name",error_msg:"Folder name must not contain path separators"};const e=i||s.resolveCwd()||d(),r=c(e,t);try{return await m(r),{status:"ok",result:{id:r,name:l(r),is_directory:!0}}}catch(n){return n?.code==="EEXIST"?{status:"failed",error_code:"already_exists",error_msg:`Folder already exists: ${r}`}:{status:"failed",error_code:"create_failed",error_msg:String(n.message||n)}}}export{p as handleCreateFolderAction,f as handleFileListAction};
1
+ import{mkdir as m}from"node:fs/promises";import{join as c,basename as l}from"node:path";import{homedir as _}from"node:os";import{listFiles as u}from"./list-files.js";function d(){return _()}async function f(a,s){const o=a.params??{},t=o.parent_id?.trim(),i=o.show_hidden??!1;let e;t?e=t:e=s.resolveCwd()||d();try{return{status:"ok",result:{files:await u(e,i),current_path:e}}}catch(r){return r?.code==="ENOENT"?{status:"failed",error_code:"path_not_found",error_msg:`Directory not found: ${e}`}:r?.code==="ENOTDIR"?{status:"failed",error_code:"not_a_directory",error_msg:`Not a directory: ${e}`}:{status:"failed",error_code:"list_failed",error_msg:String(r.message||r)}}}async function p(a,s){const o=a.params??{},t=o.name?.trim(),i=o.parent_id?.trim();if(!t)return{status:"failed",error_code:"name_required",error_msg:"Folder name is required"};if(/[/\\]/.test(t))return{status:"failed",error_code:"invalid_name",error_msg:"Folder name must not contain path separators"};const e=i||s.resolveCwd()||d(),r=c(e,t);try{return await m(r),{status:"ok",result:{id:r,name:l(r),is_directory:!0}}}catch(n){return n?.code==="EEXIST"?{status:"failed",error_code:"already_exists",error_msg:`Folder already exists: ${r}`}:{status:"failed",error_code:"create_failed",error_msg:String(n.message||n)}}}export{p as handleCreateFolderAction,f as handleFileListAction};
@@ -1 +1 @@
1
- import{splitTextForAibotProtocol as l}from"../protocol/protocol-text.js";import{PHASE2_TOOL_NAMES as f,ACCESS_CONTROL_ACTION_MAP as a}from"./tools.js";const v=f;function A(n){return v.has(n)}function E(n,e,t){switch(e){case"grix_reply":return p(n,t);case"grix_complete":return S(n,t);case"grix_event_ack":return m(n,t);case"grix_composing":return g(n,t);case"grix_access_control":return x(n,t);case"grix_status":return h(n);default:return c(`\u672A\u77E5\u4E8B\u4EF6\u5DE5\u5177: ${e}`)}}function p(n,e){const t=String(e.event_id??""),i=String(e.session_id??""),s=String(e.text??""),u=e.quoted_message_id;if(!i)return c("\u7F3A\u5C11\u5FC5\u586B\u53C2\u6570: session_id");if(!s)return c("\u7F3A\u5C11\u5FC5\u586B\u53C2\u6570: text");const r=l(s),_=`reply_${t||"proactive"}_${Date.now()}`;if(!t){for(let o=0;o<r.length;o++)n.sendMsg({session_id:i,client_msg_id:`${_}_${o+1}`,msg_type:1,content:r[o],quoted_message_id:o===0?u:void 0});return d({ok:!0,direct:!0,chunks:r.length,client_msg_id:_})}for(let o=0;o<r.length;o++)n.sendStreamChunk({event_id:t||void 0,session_id:i,delta_content:r[o],chunk_seq:o+1,is_finish:!1,client_msg_id:_,quoted_message_id:o===0?u:void 0});return n.sendStreamChunk({event_id:t||void 0,session_id:i,delta_content:"",chunk_seq:r.length+1,is_finish:!0,client_msg_id:_}),d({ok:!0,chunks:r.length,client_msg_id:_})}function S(n,e){const t=String(e.event_id??""),i=String(e.status??""),s=e.msg;return t?["responded","canceled","failed"].includes(i)?(n.sendEventResult({event_id:t,status:i,msg:s}),d({ok:!0,event_id:t,status:i})):c("status \u5FC5\u987B\u4E3A responded\u3001canceled \u6216 failed"):c("\u7F3A\u5C11\u5FC5\u586B\u53C2\u6570: event_id")}function m(n,e){const t=String(e.event_id??""),i=e.session_id;return t?(n.sendEventAck({event_id:t,session_id:i,received_at:Date.now()}),d({ok:!0,event_id:t})):c("\u7F3A\u5C11\u5FC5\u586B\u53C2\u6570: event_id")}function g(n,e){const t=String(e.session_id??""),i=e.active===!0,s=e.event_id;return t?(n.sendSessionActivitySet({session_id:t,kind:"composing",active:i,ref_event_id:s,ttl_ms:i?3e4:void 0}),d({ok:!0,session_id:t,active:i})):c("\u7F3A\u5C11\u5FC5\u586B\u53C2\u6570: session_id")}function x(n,e){const t=String(e.action??""),i=a[t];if(!i)return c(`\u672A\u77E5 access_control action: ${t}`);const s={};return e.code!=null&&(s.code=e.code),e.sender_id!=null&&(s.sender_id=e.sender_id),e.policy!=null&&(s.policy=e.policy),{content:[{type:"text",text:"__ASYNC_ACCESS_CONTROL__"}],_async:{verb:i,payload:s}}}function h(n){const e=n.status,t=n.getStatusSnapshot();return d({connected:e==="ready",status:e,connected_at:t.connectedAt})}function d(n){return{content:[{type:"text",text:JSON.stringify(n)}],isError:!1}}function c(n){return{content:[{type:"text",text:n}],isError:!0}}export{v as EVENT_TOOL_NAMES,E as executeEventTool,A as isEventTool};
1
+ import{splitTextForAibotProtocol as f}from"../protocol/protocol-text.js";import{PHASE2_TOOL_NAMES as a,ACCESS_CONTROL_ACTION_MAP as v}from"./tools.js";const p=a;function E(n){return p.has(n)}function C(n,e,t){switch(e){case"grix_reply":return S(n,t);case"grix_complete":return m(n,t);case"grix_event_ack":return x(n,t);case"grix_composing":return g(n,t);case"grix_access_control":return h(n,t);case"grix_status":return y(n);default:return c(`\u672A\u77E5\u4E8B\u4EF6\u5DE5\u5177: ${e}`)}}function S(n,e){const t=String(e.event_id??""),s=String(e.session_id??""),o=String(e.text??""),u=e.quoted_message_id;if(!s)return c("\u7F3A\u5C11\u5FC5\u586B\u53C2\u6570: session_id");if(!o)return c("\u7F3A\u5C11\u5FC5\u586B\u53C2\u6570: text");const r=f(o),_=`reply_${t||"proactive"}_${Date.now()}`;if(!t){for(let i=0;i<r.length;i++)n.sendMsg({session_id:s,client_msg_id:`${_}_${i+1}`,msg_type:1,content:r[i],quoted_message_id:i===0?u:void 0});return d({ok:!0,direct:!0,chunks:r.length,client_msg_id:_})}for(let i=0;i<r.length;i++){const l=i===r.length-1;n.sendStreamChunk({event_id:t||void 0,session_id:s,delta_content:r[i],chunk_seq:i+1,is_finish:l,client_msg_id:_,quoted_message_id:i===0?u:void 0})}return d({ok:!0,chunks:r.length,client_msg_id:_})}function m(n,e){const t=String(e.event_id??""),s=String(e.status??""),o=e.msg;return t?["responded","canceled","failed"].includes(s)?(n.sendEventResult({event_id:t,status:s,msg:o}),d({ok:!0,event_id:t,status:s})):c("status \u5FC5\u987B\u4E3A responded\u3001canceled \u6216 failed"):c("\u7F3A\u5C11\u5FC5\u586B\u53C2\u6570: event_id")}function x(n,e){const t=String(e.event_id??""),s=e.session_id;return t?(n.sendEventAck({event_id:t,session_id:s,received_at:Date.now()}),d({ok:!0,event_id:t})):c("\u7F3A\u5C11\u5FC5\u586B\u53C2\u6570: event_id")}function g(n,e){const t=String(e.session_id??""),s=e.active===!0,o=e.event_id;return t?(n.sendSessionActivitySet({session_id:t,kind:"composing",active:s,ref_event_id:o,ttl_ms:s?3e4:void 0}),d({ok:!0,session_id:t,active:s})):c("\u7F3A\u5C11\u5FC5\u586B\u53C2\u6570: session_id")}function h(n,e){const t=String(e.action??""),s=v[t];if(!s)return c(`\u672A\u77E5 access_control action: ${t}`);const o={};return e.code!=null&&(o.code=e.code),e.sender_id!=null&&(o.sender_id=e.sender_id),e.policy!=null&&(o.policy=e.policy),{content:[{type:"text",text:"__ASYNC_ACCESS_CONTROL__"}],_async:{verb:s,payload:o}}}function y(n){const e=n.status,t=n.getStatusSnapshot();return d({connected:e==="ready",status:e,connected_at:t.connectedAt})}function d(n){return{content:[{type:"text",text:JSON.stringify(n)}],isError:!1}}function c(n){return{content:[{type:"text",text:n}],isError:!0}}export{p as EVENT_TOOL_NAMES,C as executeEventTool,E as isEventTool};
File without changes
@@ -1,2 +1,2 @@
1
- function i(t,s){const e=[];return s.quotedMessageId?.trim()&&e.push(`[[quoted_message_id:${s.quotedMessageId.trim()}]]`),e.push(t),s.messageId?.trim()&&e.push(`[[message_id:${s.messageId.trim()}]]`),e.join(`
1
+ function i(t,s){const e=[];return s.quotedMessageId?.trim()&&e.push(`[[quoted_message_id:${s.quotedMessageId.trim()}]]`),s.messageId?.trim()&&e.push(`[[message_id:${s.messageId.trim()}]]`),e.push(t),e.join(`
2
2
  `)}export{i as injectMessageMetadata};
@@ -1,2 +1,2 @@
1
- import{normalizeString as o}from"../util/normalize-string.js";const a=/^\[\[quoted_message_id:([^\]\r\n]+)\]\](?:\r?\n)?/u;function g(r,{messageId:e="",quotedMessageId:t=""}={}){const u=String(r??""),d=o(e),n=o(t),s=[];return n&&s.push(`[[quoted_message_id:${n}]]`),u&&s.push(u),d&&s.push(`[[message_id:${d}]]`),s.join(`
1
+ import{normalizeString as o}from"../util/normalize-string.js";const a=/^\[\[quoted_message_id:([^\]\r\n]+)\]\](?:\r?\n)?/u;function g(r,{messageId:e="",quotedMessageId:t=""}={}){const u=String(r??""),d=o(e),n=o(t),s=[];return n&&s.push(`[[quoted_message_id:${n}]]`),d&&s.push(`[[message_id:${d}]]`),u&&s.push(u),s.join(`
2
2
  `)}function i(r){const e=String(r??""),t=a.exec(e);return t?{text:e.slice(t[0].length),quotedMessageId:o(t[1])}:{text:e,quotedMessageId:""}}function f({replyTo:r="",text:e="",inboundQuotedMessageId:t="",inboundMessageId:u=""}={}){const d=o(r);if(d)return{text:String(e??""),quotedMessageId:d};const n=i(e);if(n.quotedMessageId)return n;const s=o(t)||o(u);return{text:n.text,quotedMessageId:s}}export{i as extractOutboundQuotedMessageReference,g as formatInboundMessageReferenceText,f as resolveOutboundQuotedMessageId};
@@ -1,11 +1,11 @@
1
- import{isAbsolute as d}from"node:path";const l="[\u5F15\u7528\u6D88\u606F]",p=/^(?:(?:[ \t]*>[^\r\n]*(?:\r?\n|$))+)(?:[ \t]*\r?\n)*[ \t]*-{3,}[ \t]*(?:\r?\n|$)(?:[ \t]*\r?\n)*/;function E(t,e){const n=g(t),r=_(n,e),o=y(t,n),i=w(n),s=I(n,"additional_directories","additionalDirectories"),c=m(n,"mode_id","modeId"),f=m(n,"model_id","modelId"),a=C(n,"timeout_ms","timeoutMs");return{cwd:r,prompt:o,mcpServers:i,additionalDirectories:s??[],modeId:c,modelId:f,timeoutMs:a}}function g(t){const e=u(t.extra),n=[t,u(t.acp),u(t.gemini_acp)];return e&&(n.push(e),n.push(u(e.acp)),n.push(u(e.gemini_acp))),n.filter(r=>r!==null)}function _(t,e){const r=m(t,"cwd","workdir","working_directory")?.trim()||e?.trim()||"";if(!r)return process.cwd();if(!d(r))throw new Error(`CWD must be an absolute path: ${r}`);return r}function y(t,e){const n=v(e,"prompt");if(n&&n.length>0){const f=n.map(a=>a?.type==="text"?a.text:"").filter(Boolean);if(f.length>0)return f.join(`
2
- `)}const r=t.context_messages??[],o=t.quoted_message_id,i=t.msg_id;let s=m(e,"text")??t.content?.trim()??"";s=x(s,r,o);const c=A(r,i);return!c&&!s?"":c?s?`Conversation context:
1
+ import{isAbsolute as y}from"node:path";const m="[\u5F15\u7528\u6D88\u606F]",g=/^(?:(?:[ \t]*>[^\r\n]*(?:\r?\n|$))+)(?:[ \t]*\r?\n)*[ \t]*-{3,}[ \t]*(?:\r?\n|$)(?:[ \t]*\r?\n)*/;function M(t,n){const r=_(t.extra_json),o=h(t.context_messages_json),e=A(t,r),i=b(e,n),s=x(t,e,o),c=D(e),u=O(e,"additional_directories","additionalDirectories"),f=a(e,"mode_id","modeId"),p=a(e,"model_id","modelId"),d=k(e,"timeout_ms","timeoutMs");return{cwd:i,prompt:s,mcpServers:c,additionalDirectories:u??[],modeId:f,modelId:p,timeoutMs:d}}function A(t,n){const r=[t,l(t.acp),l(t.gemini_acp)];return n&&(r.push(n),r.push(l(n.acp)),r.push(l(n.gemini_acp))),r.filter(o=>o!==null)}function _(t){if(t==null)return null;if(typeof t=="object"&&!Array.isArray(t))return t;if(typeof t!="string")return null;const n=t.trim();if(!n)return null;try{const r=JSON.parse(n);if(r&&typeof r=="object"&&!Array.isArray(r))return r}catch{}return null}function h(t){if(t==null)return[];if(Array.isArray(t))return t;if(typeof t!="string")return[];const n=t.trim();if(!n)return[];try{const r=JSON.parse(n);if(Array.isArray(r))return r}catch{}return[]}function b(t,n){const o=a(t,"cwd","workdir","working_directory")?.trim()||n?.trim()||"";if(!o)return process.cwd();if(!y(o))throw new Error(`CWD must be an absolute path: ${o}`);return o}function x(t,n,r){const o=E(n,"prompt");if(o&&o.length>0){const u=o.map(f=>f?.type==="text"?f.text:"").filter(Boolean);if(u.length>0)return u.join(`
2
+ `)}const e=t.quoted_message_id,i=t.msg_id;let s=a(n,"text")??t.content?.trim()??"";s=I(s,r,e);const c=j(r,i);return!c&&!s?"":c?s?`Conversation context:
3
3
  `+c+`
4
4
 
5
5
  Latest user message:
6
6
  `+s:`Conversation context:
7
- `+c:s}function A(t,e){if(!t||t.length===0)return"";const n=[];for(const r of t){if(e&&r.msg_id===e)continue;const o=r.content?.trim();if(!o)continue;const i=h(o);if(i)n.push(`Quoted message:
8
- `+i);else{const s=r.sender_type===2?"Assistant message":"User message";n.push(`${s}:
9
- ${o}`)}}return n.join(`
7
+ `+c:s}function j(t,n){if(!t||t.length===0)return"";const r=[];for(const o of t){if(n&&o.msg_id===n)continue;const e=o.content?.trim();if(!e)continue;const i=C(e);if(i)r.push(`Quoted message:
8
+ `+i);else{const s=o.sender_type===2?"Assistant message":"User message";r.push(`${s}:
9
+ ${e}`)}}return r.join(`
10
10
 
11
- `)}function h(t){return t.startsWith(l)?t.slice(l.length).replace(/^\s*\n?/,"").trim()||l:null}function x(t,e,n){return!n||!e?.some(r=>r.msg_id===n&&r.content?.trim())?t:t.replace(p,"")}function w(t){for(const e of t){const n=e.mcp_servers??e.mcpServers,r=b(n);if(r&&r.every(o=>!!o&&typeof o=="object"&&!Array.isArray(o)))return r.map(o=>({name:o.name??"unknown",command:o.command??"",args:o.args,env:o.env}))}return[]}function m(t,...e){for(const n of t)for(const r of e){const o=n[r];if(typeof o=="string"&&o.trim())return o.trim()}return null}function v(t,e){for(const n of t){const r=n[e];if(Array.isArray(r)&&r.length>0)return r}return null}function I(t,...e){for(const n of t)for(const r of e){const o=n[r];if(!Array.isArray(o))continue;const i=o.filter(s=>typeof s=="string");if(i.length>0)return i}return null}function u(t){return!t||typeof t!="object"||Array.isArray(t)?null:t}function b(t){return Array.isArray(t)?t:null}function C(t,...e){for(const n of t)for(const r of e){const o=n[r];if(typeof o=="number"&&Number.isFinite(o)&&o>0)return Math.floor(o)}return null}export{E as extractAcpTurnInput};
11
+ `)}function C(t){return t.startsWith(m)?t.slice(m.length).replace(/^\s*\n?/,"").trim()||m:null}function I(t,n,r){return!r||!n?.some(o=>o.msg_id===r&&o.content?.trim())?t:t.replace(g,"")}function D(t){for(const n of t){const r=n.mcp_servers??n.mcpServers,o=S(r);if(o&&o.every(e=>!!e&&typeof e=="object"&&!Array.isArray(e)))return o.map(e=>({name:e.name??"unknown",command:e.command??"",args:e.args,env:e.env}))}return[]}function a(t,...n){for(const r of t)for(const o of n){const e=r[o];if(typeof e=="string"&&e.trim())return e.trim()}return null}function E(t,n){for(const r of t){const o=r[n];if(Array.isArray(o)&&o.length>0)return o}return null}function O(t,...n){for(const r of t)for(const o of n){const e=r[o];if(!Array.isArray(e))continue;const i=e.filter(s=>typeof s=="string");if(i.length>0)return i}return null}function l(t){return!t||typeof t!="object"||Array.isArray(t)?null:t}function S(t){return Array.isArray(t)?t:null}function k(t,...n){for(const r of t)for(const o of n){const e=r[o];if(typeof e=="number"&&Number.isFinite(e)&&e>0)return Math.floor(e)}return null}export{M as extractAcpTurnInput};
@@ -1 +1 @@
1
- const A=2e3,I=12288,E=1200,M=48;function i(t,r){const n=Number(t);return!Number.isFinite(n)||n<=0?r:Math.floor(n)}function U(t,r=1200){return Math.min(2e3,i(t,r))}function f(t){return Math.min(2e3,i(t,48))}function B(t,r){const n=String(t??"");if(!n)return[];const s=U(r,1200),T=[];let e="",o=0,_=0;for(const u of n){const O=Buffer.byteLength(u,"utf8"),c=o+1,L=_+O;e&&(c>s||L>12288)&&(T.push(e),e="",o=0,_=0),e+=u,o+=1,_+=O}return e&&T.push(e),T}export{E as DEFAULT_OUTBOUND_TEXT_CHUNK_LIMIT,U as resolveOutboundTextChunkLimit,f as resolveStreamTextChunkLimit,B as splitTextForAibotProtocol};
1
+ const A=16384,I=12288,E=1200,M=48;function i(t,r){const n=Number(t);return!Number.isFinite(n)||n<=0?r:Math.floor(n)}function U(t,r=1200){return Math.min(16384,i(t,r))}function f(t){return Math.min(16384,i(t,48))}function B(t,r){const n=String(t??"");if(!n)return[];const s=U(r,1200),o=[];let e="",T=0,_=0;for(const u of n){const O=Buffer.byteLength(u,"utf8"),c=T+1,L=_+O;e&&(c>s||L>12288)&&(o.push(e),e="",T=0,_=0),e+=u,T+=1,_+=O}return e&&o.push(e),o}export{A as AIBOT_PROTOCOL_MAX_RUNES,E as DEFAULT_OUTBOUND_TEXT_CHUNK_LIMIT,U as resolveOutboundTextChunkLimit,f as resolveStreamTextChunkLimit,B as splitTextForAibotProtocol};
@@ -1 +1 @@
1
- import{readFile as l}from"node:fs/promises";import{homedir as a}from"node:os";import{join as s}from"node:path";import{spawn as u}from"node:child_process";const m=s(a(),".aws/sso/cache/kiro-auth-token-cli.json"),p="https://codewhisperer.us-east-1.amazonaws.com",d="AmazonCodeWhispererService.GetUsageLimits",w=1e4;async function c(){const t=await l(m,"utf-8");return JSON.parse(t)}function f(t){return Date.now()>new Date(t.expiresAt).getTime()}async function T(){return new Promise((t,o)=>{const e=s(a(),".local/bin/kiro-cli"),i=u(e,["whoami"],{stdio:["ignore","pipe","pipe"],timeout:15e3});let r="";i.stdout.on("data",n=>{r+=n.toString()}),i.stderr.on("data",()=>{}),i.on("close",n=>{n===0?t():o(new Error(`kiro-cli whoami exited with code ${n}: ${r.trim()}`))}),i.on("error",o)})}async function g(t,o){const e=new AbortController,i=setTimeout(()=>e.abort(),w);try{const r=await fetch(p,{method:"POST",headers:{"Content-Type":"application/x-amz-json-1.0","X-Amz-Target":d,Authorization:`Bearer ${t}`,Accept:"application/json"},body:JSON.stringify({profileArn:o}),signal:e.signal});if(!r.ok){const n=await r.text();throw new Error(`GetUsageLimits ${r.status}: ${n}`)}return await r.json()}finally{clearTimeout(i)}}async function b(){try{let t=await c();f(t)&&(await T(),t=await c());const o=await g(t.accessToken,t.profileArn),e=o.usageBreakdownList?.find(r=>r.resourceType==="CREDIT");let i=null;if(e){const r=e.currentUsageWithPrecision??e.currentUsage,n=e.usageLimitWithPrecision??e.usageLimit;i={remaining:Math.max(0,n-r),total:n,used:r,unit:e.unit||e.displayName||"Credits"}}return{provider:"kiro",providerLabel:"Kiro",planName:o.subscriptionInfo?.subscriptionTitle??null,tiers:[],balance:i,success:!0,error:null}}catch(t){const o=t instanceof Error?t.message:String(t);return{provider:"kiro",providerLabel:"Kiro",planName:null,tiers:[],balance:null,success:!1,error:o}}}export{b as queryKiroQuota};
1
+ import{readFile as u}from"node:fs/promises";import{homedir as a}from"node:os";import{join as c}from"node:path";import{spawn as m}from"node:child_process";const p=c(a(),".aws/sso/cache/kiro-auth-token-cli.json"),d="https://codewhisperer.us-east-1.amazonaws.com",w="AmazonCodeWhispererService.GetUsageLimits",f=1e4;async function l(){const e=await u(p,"utf-8");return JSON.parse(e)}function g(e){return Date.now()>new Date(e.expiresAt).getTime()}async function T(){return new Promise((e,i)=>{const t=c(a(),".local/bin/kiro-cli"),o=m(t,["whoami"],{stdio:["ignore","pipe","pipe"],timeout:15e3});let r="";o.stdout.on("data",n=>{r+=n.toString()}),o.stderr.on("data",()=>{}),o.on("close",n=>{n===0?e():i(new Error(`kiro-cli whoami exited with code ${n}: ${r.trim()}`))}),o.on("error",i)})}async function h(e,i){const t=new AbortController,o=setTimeout(()=>t.abort(),f);try{const r=await fetch(d,{method:"POST",headers:{"Content-Type":"application/x-amz-json-1.0","X-Amz-Target":w,Authorization:`Bearer ${e}`,Accept:"application/json"},body:JSON.stringify({profileArn:i}),signal:t.signal});if(!r.ok){const n=await r.text();throw new Error(`GetUsageLimits ${r.status}: ${n}`)}return await r.json()}finally{clearTimeout(o)}}async function x(){try{let e=await l();g(e)&&(await T(),e=await l());const i=await h(e.accessToken,e.profileArn),t=i.usageBreakdownList?.find(r=>r.resourceType==="CREDIT");let o=null;if(t){const r=t.currentUsageWithPrecision??t.currentUsage,n=t.usageLimitWithPrecision??t.usageLimit,s=t.nextDateReset<1e12?t.nextDateReset*1e3:t.nextDateReset;o={remaining:Math.max(0,n-r),total:n,used:r,unit:t.unit||t.displayName||"Credits",resetsAt:Number.isFinite(s)&&s>0?new Date(s).toISOString():null}}return{provider:"kiro",providerLabel:"Kiro",planName:i.subscriptionInfo?.subscriptionTitle??null,tiers:[],balance:o,success:!0,error:null}}catch(e){const i=e instanceof Error?e.message:String(e);return{provider:"kiro",providerLabel:"Kiro",planName:null,tiers:[],balance:null,success:!1,error:i}}}export{x as queryKiroQuota};
@@ -1,6 +1,6 @@
1
- const k=3e3,p=4500;class L{enabled;targetChars;hardLimitChars;segmentChars=0;state={inFence:!1,fenceMarker:"```"};constructor(s){this.enabled=s?.enabled??!0,this.targetChars=Math.max(256,Number(s?.targetChars??3e3)),this.hardLimitChars=Math.max(this.targetChars+128,Number(s?.hardLimitChars??4500))}push(s){if(!s)return[];if(!this.enabled)return this.segmentChars+=s.length,this.state=o(s,this.state),[{text:s,closeAfter:!1}];const e=[];let t=s;for(;t.length>0;){const n=this.targetChars-this.segmentChars;if(n>0&&t.length<=n){this.segmentChars+=t.length,this.state=o(t,this.state),e.push({text:t,closeAfter:!1});break}const c=M(t,this.state),i=Math.max(64,Math.floor(this.targetChars*.75)-this.segmentChars),r=Math.max(128,this.hardLimitChars-this.segmentChars),a=A(c,i,r);if(a>0&&a<t.length){const l=t.slice(0,a),C=o(l,this.state);this.segmentChars+=l.length,e.push({text:l,closeAfter:!0,reason:"threshold"}),t=t.slice(a),this.segmentChars=0,this.state=C;continue}if(this.segmentChars+t.length<this.hardLimitChars){this.segmentChars+=t.length,this.state=o(t,this.state),e.push({text:t,closeAfter:!1});break}const u=Math.max(128,this.hardLimitChars-this.segmentChars),f=Math.min(t.length,u);if(f>=t.length){this.segmentChars+=t.length,this.state=o(t,this.state),e.push({text:t,closeAfter:!1});break}let g=t.slice(0,f),m=t.slice(f);if(this.state.inFence){const l=this.state.fenceMarker||"```";g=`${g}
2
- ${l}`,m=`${l}
3
- ${m}`}e.push({text:g,closeAfter:!0,reason:"hard_limit"}),t=m,this.segmentChars=0,this.state={inFence:!1,fenceMarker:this.state.fenceMarker||"```"}}return d(e)}finish(){return[]}reset(){this.segmentChars=0,this.state={inFence:!1,fenceMarker:"```"}}}function d(h){const s=[];for(const e of h){if(!e.text)continue;const t=s[s.length-1];if(t&&!t.closeAfter&&!e.closeAfter&&!t.reason&&!e.reason){t.text+=e.text;continue}s.push(e)}return s}function A(h,s,e){let t=-1;for(const n of h)n>=s&&n<=e&&(t=n);return t}function M(h,s){const e=[];let t={...s};const n=h.split(`
4
- `);let c=0;for(let i=0;i<n.length;i++){const r=n[i]??"",a=i<n.length-1?`${r}
5
- `:r;if(t=o(a,t),c+=a.length,!t.inFence){if(r.trim()===""&&i<n.length-1){e.push(c);continue}/[。!?.!?]\s*$/.test(r)&&i<n.length-1&&e.push(c)}}return e}function o(h,s){let e=s.inFence,t=s.fenceMarker;const n=h.split(`
6
- `);for(const c of n){const r=(c??"").trimStart();if(r.startsWith("```")||r.startsWith("~~~")){const a=r.startsWith("~~~")?"~~~":"```";e?a===t&&(e=!1):(e=!0,t=a)}}return{inFence:e,fenceMarker:t}}export{L as SafeMarkdownStreamSegmenter};
1
+ const p=3e3,L=4500;class x{enabled;targetChars;hardLimitChars;segmentChars=0;state={inFence:!1,fenceMarker:"```"};constructor(s){this.enabled=s?.enabled??!0,this.targetChars=Math.max(256,Number(s?.targetChars??3e3)),this.hardLimitChars=Math.max(this.targetChars+128,Number(s?.hardLimitChars??4500))}push(s){if(!s)return[];if(!this.enabled)return this.segmentChars+=s.length,this.state=o(s,this.state),[{text:s,closeAfter:!1}];const e=[];let t=s;for(;t.length>0;){const r=this.targetChars-this.segmentChars,h=this.segmentChars>=this.hardLimitChars;if(r>0&&t.length<=r){this.segmentChars+=t.length,this.state=o(t,this.state),e.push({text:t,closeAfter:!1});break}const i=k(t,this.state),n=Math.max(64,Math.floor(this.targetChars*.75)-this.segmentChars),c=Math.max(128,this.hardLimitChars-this.segmentChars),f=M(i,n,c);if(f>0&&f<t.length){const l=t.slice(0,f),d=o(l,this.state);this.segmentChars+=l.length,e.push({text:l,closeAfter:!0,reason:"threshold"}),t=t.slice(f),this.segmentChars=0,this.state=d;continue}if(!h&&this.segmentChars+t.length<this.hardLimitChars){this.segmentChars+=t.length,this.state=o(t,this.state),e.push({text:t,closeAfter:!1});break}const C=Math.max(128,this.hardLimitChars-this.segmentChars),m=Math.min(t.length,C);if(m>=t.length){this.segmentChars+=t.length,this.state=o(t,this.state),this.state.inFence?e.push({text:t,closeAfter:!1}):(e.push({text:t,closeAfter:!0,reason:"hard_limit"}),this.segmentChars=0);break}let g=t.slice(0,m),u=t.slice(m);if(this.state.inFence){const l=this.state.fenceMarker||"```";g=`${g}
2
+ ${l}`,u=`${l}
3
+ ${u}`}e.push({text:g,closeAfter:!0,reason:"hard_limit"}),t=u,this.segmentChars=0,this.state={inFence:!1,fenceMarker:this.state.fenceMarker||"```"}}return A(e)}finish(){return[]}reset(){this.segmentChars=0,this.state={inFence:!1,fenceMarker:"```"}}}function A(a){const s=[];for(const e of a){if(!e.text)continue;const t=s[s.length-1];if(t&&!t.closeAfter&&!e.closeAfter&&!t.reason&&!e.reason){t.text+=e.text;continue}s.push(e)}return s}function M(a,s,e){let t=-1;for(const r of a)r>=s&&r<=e&&(t=r);return t}function k(a,s){const e=[];let t={...s};const r=a.split(`
4
+ `);let h=0;for(let i=0;i<r.length;i++){const n=r[i]??"",c=i<r.length-1?`${n}
5
+ `:n;if(t=o(c,t),h+=c.length,!t.inFence){if(n.trim()===""&&i<r.length-1){e.push(h);continue}/[。!?.!?]\s*$/.test(n)&&i<r.length-1&&e.push(h)}}return e}function o(a,s){let e=s.inFence,t=s.fenceMarker;const r=a.split(`
6
+ `);for(const h of r){const n=(h??"").trimStart();if(n.startsWith("```")||n.startsWith("~~~")){const c=n.startsWith("~~~")?"~~~":"```";e?c===t&&(e=!1):(e=!0,t=c)}}return{inFence:e,fenceMarker:t}}export{x as SafeMarkdownStreamSegmenter};
@@ -1 +1 @@
1
- import{existsSync as m,mkdirSync as C,readFileSync as I,renameSync as U,writeFileSync as P}from"node:fs";import{join as h,dirname as $}from"node:path";import{spawn as b}from"node:child_process";import{log as n}from"../log/index.js";import{GRIX_PATHS as _}from"../log/index.js";import{resolveClientVersion as p}from"../util/client-version.js";import{UpgradeError as E,collectEnvInfo as L,getUpgradeLogTail as M,npmInstall as O,pendingExists as v,preflightCheck as D,readPending as N,removePending as u,upgradeLog as o,verifyInstalledVersion as R,writePending as F}from"./npm-upgrader.js";const G=360*60*1e3,x=300*1e3,y=1800*1e3,V=2,j=3,H="grix-connector",w=1e4;function S(s){return s.replace(/^wss:/,"https:").replace(/^ws:/,"http:")}function T(){return h(_.data,"upgrade-state.json")}function A(){const s=T();if(!m(s))return{daily_attempts:{},version_attempts:{}};try{return JSON.parse(I(s,"utf-8"))}catch{return{daily_attempts:{},version_attempts:{}}}}function K(s){const t=T();C($(t),{recursive:!0});const e=t+".tmp";P(e,JSON.stringify(s),"utf-8"),U(e,t)}function k(){return new Date().toISOString().slice(0,10)}class Q{agentConfigs;timer=null;initialTimer=null;running=!1;stopped=!1;constructor(t){this.agentConfigs=t}async start(){await this.handlePendingOnStartup(),this.initialTimer=setTimeout(()=>{this.stopped||(this.runCheck(),!this.stopped&&(this.timer=setInterval(()=>this.runCheck(),G)))},x)}stop(){this.stopped=!0,this.initialTimer&&(clearTimeout(this.initialTimer),this.initialTimer=null),this.timer&&(clearInterval(this.timer),this.timer=null)}triggerCheck(){this.stopped||this.runCheck()}async handlePendingOnStartup(){if(!v())return;const t=N();if(!t){u();return}const e=p();e===t.target_version?(o(`daemon startup: version matches target ${t.target_version}, upgrade succeeded`),await this.reportUpgrade({from_version:t.from_version,to_version:t.target_version,status:"success"})):(o(`daemon startup: version ${e} != target ${t.target_version}, rolled back`),await this.reportUpgrade({from_version:t.from_version,to_version:t.target_version,status:"rolled_back",error_code:"STARTUP_CRASH",crash_count:t.crash_count})),u()}async runCheck(){if(!this.running){this.running=!0;try{await this.check()}catch(t){n.error("upgrade",`Check failed: ${t instanceof Error?t.message:t}`)}finally{this.running=!1}}}async check(){if(v())return;const t=this.agentConfigs[0];if(!t)return;const e=await this.queryUpgrade(t);if(!e.available||!e.release)return;const r=e.release.version,a=p();if(!e.release.force&&!this.checkRateLimit(r))return;const c=D();if(!c.ok){await this.reportUpgrade({from_version:a,to_version:r,status:"failed",error_code:c.errorCode,error_msg:c.errorMsg});return}o(`upgrade start: ${a} -> ${r}`);const d=Date.now();try{F(a,r),await O(H,r),R(r),this.startGuardian(),o("npm install verified, reporting installed"),await this.reportUpgrade({from_version:a,to_version:r,status:"installed",duration_ms:Date.now()-d}),o("shutting down for restart"),process.kill(process.pid,"SIGTERM")}catch(l){const f=l instanceof E?l.code:"NPM_INSTALL_FAILED",g=l instanceof Error?l.message:String(l);o(`upgrade failed: ${f} ${g}`),u(),this.recordFailure(r),await this.reportUpgrade({from_version:a,to_version:r,status:"failed",error_code:f,error_msg:g,duration_ms:Date.now()-d,upgrade_log:M()})}}checkRateLimit(t){const e=A();if(e.last_failure_at){const c=Date.now()-new Date(e.last_failure_at).getTime();if(c<y)return n.info("upgrade",`In cooldown, ${(y-c)/6e4}m remaining`),!1}const r=e.version_attempts[t]??0;if(r>=j)return n.info("upgrade",`Version ${t} already tried ${r} times, skipping`),!1;const a=k(),i=e.daily_attempts[a]??0;return i>=V?(n.info("upgrade",`Already ${i} attempts today, skipping`),!1):!0}recordFailure(t){const e=A(),r=k();e.last_failure_at=new Date().toISOString(),e.last_failure_version=t,e.daily_attempts[r]=(e.daily_attempts[r]??0)+1,e.version_attempts[t]=(e.version_attempts[t]??0)+1,K(e)}async queryUpgrade(t){const r=`${S(t.wsUrl)}/v1/agent-api/upgrade/check?`+new URLSearchParams({client_type:"grix-connector",client_version:p(),channel:t.channel??"stable",platform:process.platform,arch:process.arch}).toString();try{const a=await fetch(r,{headers:{Authorization:`Bearer ${t.apiKey}`},signal:AbortSignal.timeout(w)});if(!a.ok)return n.warn("upgrade",`Check API returned ${a.status}`),{available:!1};const i=await a.json();return i.code!==0?{available:!1}:i.data}catch(a){return n.warn("upgrade",`Check API error: ${a instanceof Error?a.message:a}`),{available:!1}}}async reportUpgrade(t){const e=this.agentConfigs[0];if(!e)return;const a=`${S(e.wsUrl)}/v1/agent-api/upgrade/report`,i=t.npm_version?t:{...t,...L()};try{await fetch(a,{method:"POST",headers:{Authorization:`Bearer ${e.apiKey}`,"Content-Type":"application/json"},body:JSON.stringify(i),signal:AbortSignal.timeout(w)})}catch{}}startGuardian(){const t=h(_.base,"bin","upgrade-guardian.sh");if(process.platform==="win32"||!m(t)){n.info("upgrade","Guardian not available on this platform, skipping");return}try{const e=b(t,[],{detached:!0,stdio:"ignore"});e.unref(),o(`guardian started (pid ${e.pid})`)}catch(e){n.warn("upgrade",`Failed to start guardian: ${e instanceof Error?e.message:e}`)}}}export{Q as UpgradeChecker};
1
+ import{existsSync as h,mkdirSync as U,readFileSync as b,renameSync as I,writeFileSync as P}from"node:fs";import{join as m,dirname as $}from"node:path";import{spawn as E}from"node:child_process";import{log as n}from"../log/index.js";import{GRIX_PATHS as _}from"../log/index.js";import{resolveClientVersion as u}from"../util/client-version.js";import{UpgradeError as M,collectEnvInfo as D,getUpgradeLogTail as L,npmInstall as O,pendingExists as y,preflightCheck as N,readPending as R,removePending as f,upgradeLog as a,verifyInstalledVersion as F,writePending as x}from"./npm-upgrader.js";const B=360*60*1e3,G=300*1e3,v=1800*1e3,V=2,j=3,H="grix-connector",w=1e4;function S(o){return o.replace(/^wss:/,"https:").replace(/^ws:/,"http:")}function k(){return m(_.data,"upgrade-state.json")}function T(){const o=k();if(!h(o))return{daily_attempts:{},version_attempts:{}};try{return JSON.parse(b(o,"utf-8"))}catch{return{daily_attempts:{},version_attempts:{}}}}function K(o){const t=k();U($(t),{recursive:!0});const e=t+".tmp";P(e,JSON.stringify(o),"utf-8"),I(e,t)}function A(){return new Date().toISOString().slice(0,10)}class Z{agentConfigs;isBusy;timer=null;initialTimer=null;running=!1;stopped=!1;constructor(t,e){this.agentConfigs=t,this.isBusy=e}async start(){await this.handlePendingOnStartup(),this.initialTimer=setTimeout(()=>{this.stopped||(this.runCheck(),!this.stopped&&(this.timer=setInterval(()=>this.runCheck(),B)))},G)}stop(){this.stopped=!0,this.initialTimer&&(clearTimeout(this.initialTimer),this.initialTimer=null),this.timer&&(clearInterval(this.timer),this.timer=null)}triggerCheck(){this.stopped||this.runCheck()}async checkForUpdate(){const t=this.agentConfigs[0];return t?this.queryUpgrade(t):{available:!1}}async handlePendingOnStartup(){if(!y())return;const t=R();if(!t){f();return}const e=u();e===t.target_version?(a(`daemon startup: version matches target ${t.target_version}, upgrade succeeded`),await this.reportUpgrade({from_version:t.from_version,to_version:t.target_version,status:"success"})):(a(`daemon startup: version ${e} != target ${t.target_version}, rolled back`),await this.reportUpgrade({from_version:t.from_version,to_version:t.target_version,status:"rolled_back",error_code:"STARTUP_CRASH",crash_count:t.crash_count})),f()}async runCheck(){if(!this.running){this.running=!0;try{await this.check()}catch(t){n.error("upgrade",`Check failed: ${t instanceof Error?t.message:t}`)}finally{this.running=!1}}}async check(){if(y())return;const t=this.agentConfigs[0];if(!t)return;const e=await this.queryUpgrade(t);if(!e.available||!e.release)return;const r=e.release.version,i=u();if(!e.release.force&&!this.checkRateLimit(r))return;const l=N();if(!l.ok){await this.reportUpgrade({from_version:i,to_version:r,status:"failed",error_code:l.errorCode,error_msg:l.errorMsg});return}a(`upgrade start: ${i} -> ${r}`);const g=Date.now();try{if(x(i,r),await O(H,r),F(r),this.startGuardian(),a("npm install verified, reporting installed"),await this.reportUpgrade({from_version:i,to_version:r,status:"installed",duration_ms:Date.now()-g}),a("shutting down for restart"),this.isBusy?.()){a("active tasks detected, waiting for completion before restart");const c=3600*1e3,d=5e3,p=Date.now()+c;for(;this.isBusy()&&Date.now()<p&&!this.stopped;)await new Promise(C=>setTimeout(C,d));if(this.stopped){a("upgrade aborted: checker stopped during wait");return}this.isBusy()?a("active tasks still running after 1h max wait, forcing restart"):a("all tasks completed, proceeding with restart")}process.kill(process.pid,"SIGTERM")}catch(c){const d=c instanceof M?c.code:"NPM_INSTALL_FAILED",p=c instanceof Error?c.message:String(c);a(`upgrade failed: ${d} ${p}`),f(),this.recordFailure(r),await this.reportUpgrade({from_version:i,to_version:r,status:"failed",error_code:d,error_msg:p,duration_ms:Date.now()-g,upgrade_log:L()})}}checkRateLimit(t){const e=T();if(e.last_failure_at){const l=Date.now()-new Date(e.last_failure_at).getTime();if(l<v)return n.info("upgrade",`In cooldown, ${(v-l)/6e4}m remaining`),!1}const r=e.version_attempts[t]??0;if(r>=j)return n.info("upgrade",`Version ${t} already tried ${r} times, skipping`),!1;const i=A(),s=e.daily_attempts[i]??0;return s>=V?(n.info("upgrade",`Already ${s} attempts today, skipping`),!1):!0}recordFailure(t){const e=T(),r=A();e.last_failure_at=new Date().toISOString(),e.last_failure_version=t,e.daily_attempts[r]=(e.daily_attempts[r]??0)+1,e.version_attempts[t]=(e.version_attempts[t]??0)+1,K(e)}async queryUpgrade(t){const r=`${S(t.wsUrl)}/v1/agent-api/upgrade/check?`+new URLSearchParams({client_type:"grix-connector",client_version:u(),channel:t.channel??"stable",platform:process.platform,arch:process.arch}).toString();try{const i=await fetch(r,{headers:{Authorization:`Bearer ${t.apiKey}`},signal:AbortSignal.timeout(w)});if(!i.ok)return n.warn("upgrade",`Check API returned ${i.status}`),{available:!1};const s=await i.json();return s.code!==0?{available:!1}:s.data}catch(i){return n.warn("upgrade",`Check API error: ${i instanceof Error?i.message:i}`),{available:!1}}}async reportUpgrade(t){const e=this.agentConfigs[0];if(!e)return;const i=`${S(e.wsUrl)}/v1/agent-api/upgrade/report`,s=t.npm_version?t:{...t,...D()};try{await fetch(i,{method:"POST",headers:{Authorization:`Bearer ${e.apiKey}`,"Content-Type":"application/json"},body:JSON.stringify(s),signal:AbortSignal.timeout(w)})}catch{}}startGuardian(){const t=m(_.base,"bin","upgrade-guardian.sh");if(process.platform==="win32"||!h(t)){n.info("upgrade","Guardian not available on this platform, skipping");return}try{const e=E(t,[],{detached:!0,stdio:"ignore"});e.unref(),a(`guardian started (pid ${e.pid})`)}catch(e){n.warn("upgrade",`Failed to start guardian: ${e instanceof Error?e.message:e}`)}}}export{Z as UpgradeChecker};
package/dist/grix.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import g from"node:path";import{writeFileSync as P}from"node:fs";import{Manager as R}from"./manager.js";import{ensureGrixDirs as $,initLogger as y,log as i}from"./core/log/index.js";import{HealthServer as T}from"./core/runtime/index.js";import{writePidFile as F,removePidFile as d}from"./core/runtime/index.js";import{resolveRuntimePaths as w}from"./core/config/index.js";import{ServiceManager as I}from"./service/service-manager.js";import{acquireDaemonLock as N,releaseDaemonLock as u}from"./runtime/daemon-lock.js";import{writeDaemonStatus as D,removeDaemonStatus as O}from"./runtime/service-state.js";const n=process.argv.slice(2),E=[],a={};for(let t=0;t<n.length;t++)n[t].startsWith("--")&&n[t+1]&&!n[t+1].startsWith("--")?(a[n[t].slice(2)]=n[t+1],t++):n[t].startsWith("--")?a[n[t].slice(2)]="true":E.push(n[t]);a.help&&(console.log(`grix-connector \u2014 Unified AI Agent Bridge
2
+ import p from"node:path";import{writeFileSync as E}from"node:fs";import{Manager as $}from"./manager.js";import{ensureGrixDirs as y,initLogger as N,log as a}from"./core/log/index.js";import{HealthServer as O}from"./core/runtime/index.js";import{writePidFile as U,removePidFile as f}from"./core/runtime/index.js";import{resolveRuntimePaths as x}from"./core/config/index.js";import{ServiceManager as L}from"./service/service-manager.js";import{acquireDaemonLock as C,releaseDaemonLock as g}from"./runtime/daemon-lock.js";import{writeDaemonStatus as v,removeDaemonStatus as _}from"./runtime/service-state.js";import{AdminServer as j,generateToken as H,writeTokenFile as G}from"./core/admin/index.js";const s=process.argv.slice(2),k=[],n={};for(let t=0;t<s.length;t++)s[t].startsWith("--")&&s[t+1]&&!s[t+1].startsWith("--")?(n[s[t].slice(2)]=s[t+1],t++):s[t].startsWith("--")?n[s[t].slice(2)]="true":k.push(s[t]);n.help&&(console.log(`grix-connector \u2014 Unified AI Agent Bridge
3
3
 
4
4
  Usage: grix-connector <command> [options]
5
5
 
@@ -13,6 +13,7 @@ Options:
13
13
  --config-dir <path> Config directory (default: ~/.grix/config)
14
14
  --profile <name> Profile name for config subdirectory
15
15
  --health-port <port> Health check port (default: 19579)
16
+ --admin-port <port> Admin API port (default: 19580)
16
17
  --help Show this help message
17
18
 
18
19
  Platform services:
@@ -24,5 +25,5 @@ Examples:
24
25
  grix-connector start # Start as system service
25
26
  grix-connector status # Check service status
26
27
  grix-connector restart # Restart the service
27
- `),process.exit(0));const m=E[0],x=["start","stop","restart","status"];if(m&&x.includes(m)){const t=w(),l=a["config-dir"]??(a.profile?g.join(t.configDir,a.profile):void 0),c=g.resolve(process.argv[1]||`${t.rootDir}/dist/grix.js`),e=new I({cliPath:c,nodePath:process.execPath});try{let r;switch(m){case"start":(await e.status({rootDir:t.rootDir})).installed?r=await e.start({rootDir:t.rootDir}):r=await e.install({rootDir:t.rootDir,configDir:l});break;case"stop":r=await e.stop({rootDir:t.rootDir});break;case"restart":(await e.status({rootDir:t.rootDir})).installed?r=await e.restart({rootDir:t.rootDir}):r=await e.install({rootDir:t.rootDir,configDir:l});break;case"status":r=await e.status({rootDir:t.rootDir});break}console.log(JSON.stringify(r,null,2)),process.exit(0)}catch(r){console.error(`${m} failed: ${r instanceof Error?r.message:r}`),process.exit(1)}}else m&&(console.error(`Unknown command: ${m}
28
- Valid commands: ${x.join(", ")}`),process.exit(1));const s=w(),A=a["config-dir"]??(a.profile?`${s.configDir}/${a.profile}`:void 0),S=new R,p=new T;let v=!1;async function f(t){if(v)return;v=!0,i.info("main",`Received ${t}, shutting down...`),p.markShuttingDown();const l=setTimeout(()=>{i.error("main","Shutdown timed out, forcing exit"),u(s.daemonLockFile).catch(()=>{}),d(),process.exit(2)},1e4);try{await S.stop(),await p.stop(),await u(s.daemonLockFile),await O(s.daemonStatusFile).catch(()=>{}),clearTimeout(l),d(),i.info("main","Shutdown complete"),process.exit(0)}catch(c){i.error("main",`Shutdown error: ${c}`),u(s.daemonLockFile).catch(()=>{}),d(),process.exit(2)}}async function L(){$(),y();try{await N(s.daemonLockFile,s.rootDir)}catch(o){console.error(o instanceof Error?o.message:o),process.exit(1)}F(),i.info("main",`grix-connector starting (PID ${process.pid})`),await D(s.daemonStatusFile,{state:"starting",pid:process.pid,updated_at:Date.now()});const t=parseInt(a["health-port"]??process.env.GRIX_HEALTH_PORT??"19579",10);await p.start(t);const l=g.join(s.dataDir,"health-port");P(l,String(t),"utf-8"),process.on("SIGINT",()=>f("SIGINT")),process.on("SIGTERM",()=>f("SIGTERM"));let c="",e=0,r;process.on("uncaughtException",o=>{const h=o instanceof Error?o.stack??o.message:String(o);h===c?(e++,(e<=3||e%100===0)&&i.error("main",`Uncaught exception (x${e}): ${h}`)):(e>3&&i.error("main",`Previous exception repeated ${e} times total`),c=h,e=1,i.error("main",`Uncaught exception: ${o instanceof Error?o.stack:o}`),r||(r=setTimeout(()=>{e>3&&i.error("main",`Previous exception repeated ${e} times total`),c="",e=0,r=void 0},1e4).unref())),!k(o)&&f("uncaughtException")}),process.on("unhandledRejection",o=>{i.error("main",`Unhandled rejection: ${o}`),!k(o)&&f("unhandledRejection")}),p.setStatusProvider(()=>S.getAgentsStatus()),await S.start(A),await D(s.daemonStatusFile,{state:"running",pid:process.pid,updated_at:Date.now()}),process.send&&process.send("ready"),i.info("main","grix-connector ready")}L().catch(t=>{i.error("main",`Fatal: ${t}`),u(s.daemonLockFile).catch(()=>{}),d(),process.exit(1)});const C=new Set(["ECONNRESET","ECONNREFUSED","ETIMEDOUT","EPIPE","EAI_AGAIN","ENOTFOUND","EHOSTUNREACH","ENETUNREACH","EIO"]);function k(t){return t instanceof Error&&"code"in t?C.has(t.code):!1}
28
+ `),process.exit(0));const l=k[0],P=["start","stop","restart","status"];if(l&&P.includes(l)){const t=x(),m=n["config-dir"]??(n.profile?p.join(t.configDir,n.profile):void 0),d=p.resolve(process.argv[1]||`${t.rootDir}/dist/grix.js`),r=new L({cliPath:d,nodePath:process.execPath});try{let o;switch(l){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(`${l} failed: ${o instanceof Error?o.message:o}`),process.exit(1)}}else l&&(console.error(`Unknown command: ${l}
29
+ Valid commands: ${P.join(", ")}`),process.exit(1));const i=x(),b=n["config-dir"]??(n.profile?`${i.configDir}/${n.profile}`:void 0),c=new $,h=new O,A=H(),S=new j(A);let T=!1;async function w(t){if(T)return;T=!0,a.info("main",`Received ${t}, shutting down...`),h.markShuttingDown();const m=setTimeout(()=>{a.error("main","Shutdown timed out, forcing exit"),g(i.daemonLockFile).catch(()=>{}),f(),process.exit(2)},1e4);try{await c.stop(),await S.stop(),await h.stop(),await g(i.daemonLockFile),await _(i.daemonStatusFile).catch(()=>{}),clearTimeout(m),f(),a.info("main","Shutdown complete"),process.exit(0)}catch(d){a.error("main",`Shutdown error: ${d}`),g(i.daemonLockFile).catch(()=>{}),f(),process.exit(2)}}async function M(){y(),N();try{await C(i.daemonLockFile,i.rootDir)}catch(e){console.error(e instanceof Error?e.message:e),process.exit(1)}U(),a.info("main",`grix-connector starting (PID ${process.pid})`),await v(i.daemonStatusFile,{state:"starting",pid:process.pid,updated_at:Date.now()});const t=parseInt(n["health-port"]??process.env.GRIX_HEALTH_PORT??"19579",10);await h.start(t);const m=p.join(i.dataDir,"health-port");E(m,String(t),"utf-8"),process.on("SIGINT",()=>w("SIGINT")),process.on("SIGTERM",()=>w("SIGTERM"));let d="",r=0,o;process.on("uncaughtException",e=>{const D=e instanceof Error?e.stack??e.message:String(e);D===d?(r++,(r<=3||r%100===0)&&a.error("main",`Uncaught exception (x${r}): ${D}`)):(r>3&&a.error("main",`Previous exception repeated ${r} times total`),d=D,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`),d="",r=0,o=void 0},1e4).unref())),!R(e)&&w("uncaughtException")}),process.on("unhandledRejection",e=>{a.error("main",`Unhandled rejection: ${e}`),!R(e)&&w("unhandledRejection")}),h.setStatusProvider(()=>c.getAgentsStatus()),await c.start(b);const u=parseInt(n["admin-port"]??process.env.GRIX_ADMIN_PORT??"19580",10);S.setAgentHandler({list:()=>c.getAgentsStatus(),add:e=>c.addAgent(e),remove:e=>c.removeAgent(e),restart:e=>c.restartAgent(e)}),S.setUpgradeHandler({check:()=>c.checkUpgrade(),trigger:()=>c.triggerUpgrade()}),await S.start(u);const I=p.join(i.dataDir,"admin-token"),F=p.join(i.dataDir,"admin-port");G(I,A),E(F,String(u),"utf-8"),await v(i.daemonStatusFile,{state:"running",pid:process.pid,updated_at:Date.now()}),process.send&&process.send("ready"),a.info("main","grix-connector ready")}M().catch(t=>{a.error("main",`Fatal: ${t}`),g(i.daemonLockFile).catch(()=>{}),f(),process.exit(1)});const W=new Set(["ECONNRESET","ECONNREFUSED","ETIMEDOUT","EPIPE","EAI_AGAIN","ENOTFOUND","EHOSTUNREACH","ENETUNREACH","EIO"]);function R(t){return t instanceof Error&&"code"in t?W.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 i}from"node:path";import{homedir as m}from"node:os";const e=i(m(),".grix"),s={base:e,config:i(e,"config"),log:i(e,"log"),data:i(e,"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),n=i(s.log,`grix-acp-${o}.log`);a=g(n,{flags:"a"})}function c(){return new Date().toISOString().slice(11,19)}const u={info(o,n,...r){const t=`${c()} [${o}] ${n}${r.length?" "+r.map(String).join(" "):""}`;console.log(t),a?.write(t+`
2
- `)},error(o,n,...r){const t=`${c()} [${o}] ERROR ${n}${r.length?" "+r.map(String).join(" "):""}`;console.error(t),a?.write(t+`
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 +1,2 @@
1
- import{readFileSync as M,readdirSync as q}from"node:fs";import{join as A}from"node:path";import{AgentInstance as j}from"./bridge/bridge.js";import{GRIX_PATHS as x,log as l}from"./core/log/index.js";import{resolveClientVersion as I}from"./core/util/client-version.js";import{UpgradeChecker as D}from"./core/upgrade/upgrade-checker.js";import{AgentGlobalConfigStore as E}from"./core/persistence/agent-global-config-store.js";import{scanSkills as H}from"./adapter/claude/skill-scanner.js";const P=8e3;function Q(e){switch(e){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};default:throw new Error(`Unsupported client_type: ${e}`)}}function L(e){const t=String(e??"").trim().toLowerCase().replace(/[^a-z0-9._-]+/g,"-").replace(/-+/g,"-").replace(/^-|-$/g,"")||"default";return A(x.data,`session-bindings-${t}.json`)}function B(e){const t=String(e??"").trim().toLowerCase().replace(/[^a-z0-9._-]+/g,"-").replace(/-+/g,"-").replace(/^-|-$/g,"")||"default";return A(x.data,`active-events-${t}.json`)}function N(...e){const t=[],o=new Set;for(const i of e)for(const d of i??[]){const r=String(d??"").trim(),p=r.toLowerCase();!r||o.has(p)||(o.add(p),t.push(r))}return t.length>0?t:void 0}function U(e,t){const o={claude:{maxConcurrent:1,maxQueued:5,queueTimeoutMs:3e5},codex:{maxConcurrent:1,maxQueued:5,queueTimeoutMs:3e5},cursor:{maxConcurrent:1,maxQueued:3,queueTimeoutMs:18e4},acp:{maxConcurrent:1,maxQueued:3,queueTimeoutMs:3e5},pi:{maxConcurrent:1,maxQueued:5,queueTimeoutMs:3e5},codewhale:{maxConcurrent:1,maxQueued:3,queueTimeoutMs:3e5},openhuman:{maxConcurrent:1,maxQueued:3,queueTimeoutMs:3e5},opencode:{maxConcurrent:1,maxQueued:3,queueTimeoutMs:3e5}},i=o[e]??o.acp;return{maxConcurrent:t?.max_concurrent??i.maxConcurrent??1,maxQueued:t?.max_queued??i.maxQueued??3,queueTimeoutMs:t?.queue_timeout_ms??i.queueTimeoutMs??3e5,cancelableQueued:!0,cancelableRunning:!0}}function R(e){const t=I(),o=String(e.client_type??"").trim().toLowerCase(),i=Q(o),d=String(e.ws_url??"").trim(),r="get_session_usage",p="get_rate_limits",s="get_agent_global_config";if(!e.name?.trim())throw new Error("agent name is required");if(!d)throw new Error(`agent ${e.name}: ws_url is required`);if(!e.agent_id?.trim())throw new Error(`agent ${e.name}: agent_id is required`);if(!e.api_key?.trim())throw new Error(`agent ${e.name}: api_key is required`);const a=i.adapterType,m=a==="acp",f=o==="qwen",c={...i.options??{}},n=a==="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,p]}:null,u=a==="claude"?{localActions:["session_control","set_mode","set_model","claude_interaction_reply","exec_approve","exec_reject","file_list","create_folder","thread_compact",r,p]}: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,g=a==="pi"?{adapterHint:"pi/base",capabilities:["local_action_v1"],localActions:["session_control","set_model","get_context","file_list","create_folder",r]}:null,h=a==="openhuman"?{adapterHint:"openhuman/base",capabilities:["local_action_v1"],localActions:["session_control","set_model","file_list","create_folder",r]}:null,b=a==="cursor"?{adapterHint:"cursor/base",capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","set_mode","get_context","file_list","create_folder",r,p]}:null,S=a==="codewhale"?{capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","file_list","create_folder",r]}:null,T=a==="opencode"?{adapterHint:"opencode/base",capabilities:["stream_chunk","local_action_v1"],localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","file_list","create_folder",r]}:null,v=m&&!f?{localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","file_list","create_folder",r]}:null,k=a==="codex"||a==="claude"||o==="gemini"?["session_control","set_model","set_mode"]:void 0,C=[r,p];m&&c.raw_transport===void 0&&(c.raw_transport=o==="gemini"),o==="kiro"&&c.acp_mcp_tools===void 0&&(c.acp_mcp_tools=!1);const $=`${o}/base`,y=a==="claude"?"claude":a==="codex"?"codex":a==="pi"?"pi":o==="kiro"?"kiro":"gemini";let w;try{w=H({mode:y,projectDir:process.cwd()})??void 0,w&&w.length===0&&(w=void 0)}catch{}return{name:e.name,adapterType:a,aibot:{url:d,agentId:e.agent_id,apiKey:e.api_key,clientType:o,clientVersion:t,adapterHint:i.adapterHint??_?.adapterHint??g?.adapterHint??h?.adapterHint??b?.adapterHint??T?.adapterHint??$,capabilities:n?.capabilities??S?.capabilities??g?.capabilities??h?.capabilities??b?.capabilities??T?.capabilities??_?.capabilities??["stream_chunk","local_action_v1","connector_upgrade"],localActions:N(n?.localActions??S?.localActions??u?.localActions??g?.localActions??h?.localActions??b?.localActions??T?.localActions??_?.localActions??v?.localActions??["exec_approve","exec_reject"],k,C,["connector_rollback","connector_upgrade_push",s]),skills:w},agent:{command:i.command,args:i.args,env:void 0},adapterOptions:c,acpAuthMethod:c.auth_method,acpInitialMode:c.initial_mode,acpMcpTools:c.acp_mcp_tools,promptTimeoutMs:e.prompt_timeout_ms,bindingsPath:L(e.name),activeEventStorePath:B(e.name),...i.enableSessionBinding||m?{enableSessionBinding:!0}:{},...i.autoInjectArgs?{autoInjectArgs:i.autoInjectArgs}:{},poolMaxSize:e.pool?.maxSize,poolIdleTimeoutMs:e.pool?.idleTimeoutMs,eventQueue:U(a,e.event_queue),logDir:x.log,providerBaseUrl:e.provider_base_url?.trim()||void 0,providerApiKey:e.provider_api_key?.trim()||void 0}}function z(){const e=process.env.GRIX_AGENT_STARTUP_WAIT_MS,t=Number(e);return Number.isFinite(t)&&t>=500?Math.floor(t):P}class Y{instances=[];upgradeChecker=null;globalConfigStore;async start(t){const o=t??x.config;l.info("manager",`Loading configs from ${o}`),this.globalConfigStore=new E(A(x.data,"agent-global-configs.json")),this.globalConfigStore.load();const i=q(o).filter(s=>s.endsWith(".json")).sort();if(i.length===0)throw new Error(`No config files found in ${o}`);const d=[];let r=0;for(const s of i)try{const a=M(A(o,s),"utf-8"),m=JSON.parse(a);if(Array.isArray(m.agents)){if(m.agents.length===0){l.error("manager",`No agents array found in ${s}`),r++;continue}for(const f of m.agents)try{const c=R(f);d.push({config:c,file:s}),l.info("manager",`Loaded ${c.name} (${c.adapterType??"acp"}) from ${s}`)}catch(c){const n=typeof f?.name=="string"?f.name:"<unknown>";l.error("manager",`Invalid agent config in ${s} (name=${n}): ${c}`),r++}}else l.error("manager",`Unrecognized config format in ${s}`)}catch(a){l.error("manager",`Failed to load ${s}: ${a}`)}let p=0;if(d.length>0){const s=z();l.info("manager",`Starting ${d.length} agent(s), startup wait=${s}ms`);const a=()=>this.upgradeChecker?.triggerCheck(),m=n=>{this.instances=this.instances.filter(u=>u!==n)},f=d.map(({config:n})=>{const u=new j(n,this.globalConfigStore);return u.setUpgradeTrigger(a),this.instances.push(u),{config:n,instance:u,startPromise:u.start()}}),c=await Promise.all(f.map(async n=>{const u=await new Promise(_=>{let g=!1;const h=setTimeout(()=>{g||(g=!0,_({kind:"timeout"}))},s);n.startPromise.then(()=>{g||(g=!0,clearTimeout(h),_({kind:"started"}))}).catch(b=>{g||(g=!0,clearTimeout(h),_({kind:"failed",error:b}))})});return{task:n,outcome:u}}));for(const{task:n,outcome:u}of c)if(u.kind!=="started"){if(u.kind==="failed"){m(n.instance),l.error("manager",`Failed to start ${n.config.name}: ${u.error}`);continue}p++,l.warn("manager",`Startup pending for ${n.config.name}, continue retrying in background`),n.startPromise.then(()=>{l.info("manager",`Delayed start succeeded: ${n.config.name}`)}).catch(_=>{m(n.instance),l.error("manager",`Delayed start failed: ${n.config.name}: ${_}`)})}if(this.instances.length>0){const n=Math.max(0,this.instances.length-p);l.info("manager",`${n}/${d.length} agent(s) running now`)}p>0&&l.warn("manager",`${p} agent(s) still connecting in background`)}if(this.instances.length===0&&d.length>0)throw new Error("All agent configurations failed to start");if(d.length>0){const s=d[0].config;this.upgradeChecker=new D([{apiKey:s.aibot.apiKey,wsUrl:s.aibot.url}]),await this.upgradeChecker.start()}}async stop(){l.info("manager","Stopping all agents..."),this.upgradeChecker?.stop(),await Promise.allSettled(this.instances.map(t=>t.stop())),await this.globalConfigStore?.flush(),this.instances=[],l.info("manager","All stopped")}getAgentsStatus(){return this.instances.map(t=>t.getStatus())}}export{Y as Manager};
1
+ import{readFileSync as q,readdirSync as I,writeFileSync as D}from"node:fs";import{join as x}from"node:path";import{AgentInstance as k}from"./bridge/bridge.js";import{GRIX_PATHS as w,log as c}from"./core/log/index.js";import{resolveClientVersion as E}from"./core/util/client-version.js";import{UpgradeChecker as U}from"./core/upgrade/upgrade-checker.js";import{AgentGlobalConfigStore as N}from"./core/persistence/agent-global-config-store.js";import{scanSkills as H}from"./adapter/claude/skill-scanner.js";const P=8e3;function Q(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};default:throw new Error(`Unsupported client_type: ${n}`)}}function L(n){const t=String(n??"").trim().toLowerCase().replace(/[^a-z0-9._-]+/g,"-").replace(/-+/g,"-").replace(/^-|-$/g,"")||"default";return x(w.data,`session-bindings-${t}.json`)}function F(n){const t=String(n??"").trim().toLowerCase().replace(/[^a-z0-9._-]+/g,"-").replace(/-+/g,"-").replace(/^-|-$/g,"")||"default";return x(w.data,`active-events-${t}.json`)}function O(...n){const t=[],e=new Set;for(const a of n)for(const o of a??[]){const s=String(o??"").trim(),d=s.toLowerCase();!s||e.has(d)||(e.add(d),t.push(s))}return t.length>0?t:void 0}function B(n,t){const e={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}},a=e[n]??e.acp;return{maxConcurrent:t?.max_concurrent??a.maxConcurrent??1,maxQueued:t?.max_queued??a.maxQueued??3,queueTimeoutMs:t?.queue_timeout_ms??a.queueTimeoutMs??0,cancelableQueued:!0,cancelableRunning:!0}}function S(n){const t=E(),e=String(n.client_type??"").trim().toLowerCase(),a=Q(e),o=String(n.ws_url??"").trim(),s="get_session_usage",d="get_rate_limits",l="get_agent_global_config";if(!n.name?.trim())throw new Error("agent name is required");if(!o)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 r=a.adapterType,g=r==="acp",_=e==="qwen",u={...a.options??{}},i=r==="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",s,d]}:null,p=r==="claude"?{localActions:["session_control","set_mode","set_model","claude_interaction_reply","exec_approve","exec_reject","file_list","create_folder","thread_compact",s,d]}: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",s],adapterHint:"qwen/base"}:null,m=r==="pi"?{adapterHint:"pi/base",capabilities:["local_action_v1"],localActions:["session_control","set_model","get_context","file_list","create_folder",s]}:null,h=r==="openhuman"?{adapterHint:"openhuman/base",capabilities:["local_action_v1"],localActions:["session_control","set_model","file_list","create_folder",s]}:null,b=r==="cursor"?{adapterHint:"cursor/base",capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","set_mode","get_context","file_list","create_folder",s,d]}:null,C=r==="codewhale"?{capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","file_list","create_folder",s]}:null,T=r==="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",s]}:null,v=g&&!_?{localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","file_list","create_folder",s]}:null,$=r==="codex"||r==="claude"||e==="gemini"?["session_control","set_model","set_mode"]:void 0,y=[s,d];g&&u.raw_transport===void 0&&(u.raw_transport=e==="gemini"),e==="kiro"&&u.acp_mcp_tools===void 0&&(u.acp_mcp_tools=!1);const M=`${e}/base`,j=r==="claude"?"claude":r==="codex"?"codex":r==="pi"?"pi":e==="kiro"?"kiro":"gemini";let A;try{A=H({mode:j,projectDir:process.cwd()})??void 0,A&&A.length===0&&(A=void 0)}catch{}return{name:n.name,adapterType:r,aibot:{url:o,agentId:n.agent_id,apiKey:n.api_key,clientType:e,clientVersion:t,adapterHint:a.adapterHint??f?.adapterHint??m?.adapterHint??h?.adapterHint??b?.adapterHint??T?.adapterHint??M,capabilities:i?.capabilities??C?.capabilities??m?.capabilities??h?.capabilities??b?.capabilities??T?.capabilities??f?.capabilities??["stream_chunk","local_action_v1","connector_upgrade"],localActions:O(i?.localActions??C?.localActions??p?.localActions??m?.localActions??h?.localActions??b?.localActions??T?.localActions??f?.localActions??v?.localActions??["exec_approve","exec_reject"],$,y,["connector_rollback","connector_upgrade_push",l]),skills:A},agent:{command:a.command,args:a.args,env:void 0},adapterOptions:u,acpAuthMethod:u.auth_method,acpInitialMode:u.initial_mode,acpMcpTools:u.acp_mcp_tools,promptTimeoutMs:n.prompt_timeout_ms,bindingsPath:L(n.name),activeEventStorePath:F(n.name),...a.enableSessionBinding||g?{enableSessionBinding:!0}:{},...a.autoInjectArgs?{autoInjectArgs:a.autoInjectArgs}:{},poolMaxSize:n.pool?.maxSize,poolIdleTimeoutMs:n.pool?.idleTimeoutMs,eventQueue:B(r,n.event_queue),logDir:w.log,providerBaseUrl:n.provider_base_url?.trim()||void 0,providerApiKey:n.provider_api_key?.trim()||void 0}}function R(){const n=process.env.GRIX_AGENT_STARTUP_WAIT_MS,t=Number(n);return Number.isFinite(t)&&t>=500?Math.floor(t):P}class Z{instances=[];configMap=new Map;upgradeChecker=null;globalConfigStore;configDir=w.config;async start(t){const e=t??w.config;this.configDir=e,c.info("manager",`Loading configs from ${e}`),this.globalConfigStore=new N(x(w.data,"agent-global-configs.json")),this.globalConfigStore.load();const a=I(e).filter(l=>l.endsWith(".json")).sort();if(a.length===0)throw new Error(`No config files found in ${e}`);const o=[];let s=0;for(const l of a)try{const r=q(x(e,l),"utf-8"),g=JSON.parse(r);if(Array.isArray(g.agents)){if(g.agents.length===0){c.error("manager",`No agents array found in ${l}`),s++;continue}for(const _ of g.agents)try{const u=S(_);o.push({config:u,file:l}),c.info("manager",`Loaded ${u.name} (${u.adapterType??"acp"}) from ${l}`)}catch(u){const i=typeof _?.name=="string"?_.name:"<unknown>";c.error("manager",`Invalid agent config in ${l} (name=${i}): ${u}`),s++}}else c.error("manager",`Unrecognized config format in ${l}`)}catch(r){c.error("manager",`Failed to load ${l}: ${r}`)}let d=0;if(o.length>0){const l=R();c.info("manager",`Starting ${o.length} agent(s), startup wait=${l}ms`);const r=()=>this.upgradeChecker?.triggerCheck(),g=i=>{this.instances=this.instances.filter(p=>p!==i)},_=o.map(({config:i})=>{const p=new k(i,this.globalConfigStore);return p.setUpgradeTrigger(r),this.instances.push(p),this.configMap.set(i.name,i),{config:i,instance:p,startPromise:p.start()}}),u=await Promise.all(_.map(async i=>{const p=await new Promise(f=>{let m=!1;const h=setTimeout(()=>{m||(m=!0,f({kind:"timeout"}))},l);i.startPromise.then(()=>{m||(m=!0,clearTimeout(h),f({kind:"started"}))}).catch(b=>{m||(m=!0,clearTimeout(h),f({kind:"failed",error:b}))})});return{task:i,outcome:p}}));for(const{task:i,outcome:p}of u)if(p.kind!=="started"){if(p.kind==="failed"){g(i.instance),c.error("manager",`Failed to start ${i.config.name}: ${p.error}`);continue}d++,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(f=>{g(i.instance),c.error("manager",`Delayed start failed: ${i.config.name}: ${f}`)})}if(this.instances.length>0){const i=Math.max(0,this.instances.length-d);c.info("manager",`${i}/${o.length} agent(s) running now`)}d>0&&c.warn("manager",`${d} agent(s) still connecting in background`)}if(this.instances.length===0&&o.length>0)throw new Error("All agent configurations failed to start");if(o.length>0){const l=o[0].config;this.upgradeChecker=new U([{apiKey:l.aibot.apiKey,wsUrl:l.aibot.url}],()=>this.instances.some(r=>r.getStatus().busy)),await this.upgradeChecker.start()}}async stop(){c.info("manager","Stopping all agents..."),this.upgradeChecker?.stop(),await Promise.allSettled(this.instances.map(t=>t.stop())),await this.globalConfigStore?.flush(),this.instances=[],c.info("manager","All stopped")}getAgentsStatus(){return this.instances.map(t=>t.getStatus())}async addAgent(t){const e=S(t);if(this.instances.some(o=>o.name===e.name))throw new Error(`Agent "${e.name}" already exists`);const a=new k(e,this.globalConfigStore);a.setUpgradeTrigger(()=>this.upgradeChecker?.triggerCheck()),await a.start(),this.instances.push(a),this.configMap.set(e.name,e),this.persistAgentsConfig(),c.info("manager",`Added agent: ${e.name}`)}async removeAgent(t){const e=this.instances.findIndex(o=>o.name===t);if(e===-1)throw Object.assign(new Error(`Agent "${t}" not found`),{code:"NOT_FOUND"});const a=this.instances[e];this.instances.splice(e,1),this.configMap.delete(t),await a.stop(),this.persistAgentsConfig(),c.info("manager",`Removed agent: ${t}`)}persistAgentsConfig(){const t=x(this.configDir,"agents.json");try{const e=[];for(const[,o]of this.configMap)e.push({name:o.name,ws_url:o.aibot.url,agent_id:o.aibot.agentId,api_key:o.aibot.apiKey,client_type:o.aibot.clientType});D(t,JSON.stringify({agents:e},null,4)+`
2
+ `,"utf-8")}catch(e){c.error("manager",`Failed to persist agents config: ${e}`)}}async restartAgent(t){const e=this.instances.findIndex(d=>d.name===t);if(e===-1)throw Object.assign(new Error(`Agent "${t}" not found`),{code:"NOT_FOUND"});const a=this.configMap.get(t);if(!a)throw Object.assign(new Error(`Config for "${t}" not found`),{code:"NOT_FOUND"});await this.instances[e].stop();const s=new k(a,this.globalConfigStore);s.setUpgradeTrigger(()=>this.upgradeChecker?.triggerCheck()),await s.start(),this.instances[e]=s,c.info("manager",`Restarted agent: ${t}`)}async checkUpgrade(){return this.upgradeChecker?this.upgradeChecker.checkForUpdate():{available:!1}}triggerUpgrade(){this.upgradeChecker?.triggerCheck()}}export{Z as Manager};
@@ -1 +1 @@
1
- import{createServer as N}from"node:http";import{Server as O}from"@modelcontextprotocol/sdk/server/index.js";import{StreamableHTTPServerTransport as j}from"@modelcontextprotocol/sdk/server/streamableHttp.js";import{ListToolsRequestSchema as R,CallToolRequestSchema as E}from"@modelcontextprotocol/sdk/types.js";import{createSecurityPolicy as h}from"./security.js";import{SessionManagerImpl as A}from"./session-manager.js";import{ToolRegistryImpl as C}from"./tool-registry.js";import{ToolExecutorImpl as J}from"./tool-executor.js";function q(n,r){let a=null,p=!1,m,c;const S=new C,v=new J;let s=null;return{async start(){m=h({serverPort:n.port,allowedOrigins:n.allowedOrigins??[],allowedHosts:n.allowedHosts??[]}),c=new A({maxSessions:1,sessionTimeoutMs:n.sessionTimeoutMs,onSessionExpired:e=>{w(e)}}),a=N((e,t)=>{g(e,t)}),await new Promise((e,t)=>{a.on("error",t),a.listen(n.port,n.bind,()=>{a.removeListener("error",t),p=!0;const i=a.address();i&&typeof i=="object"&&(n.port=i.port,m=h({serverPort:n.port,allowedOrigins:n.allowedOrigins??[],allowedHosts:n.allowedHosts??[]})),e()})})},async stop(){if(p=!1,a&&(await new Promise(e=>{a.close(()=>e())}),a=null),s){try{await s.transport.close()}catch{}try{await s.mcpServer.close()}catch{}s=null}c&&(await c.closeAll(),c.dispose())},getStatus(){return{listening:p,url:`http://${n.bind}:${n.port}${n.endpoint}`,activeSessions:s?1:0}},pushEvent(e){s&&y(s.mcpServer,"notifications/message",{event_id:e.event_id,session_id:e.session_id,sender_id:e.sender_id??"",content:e.content,msg_type:e.msg_type??1,msg_id:e.msg_id??"",session_type:e.session_type??1,quoted_message_id:e.quoted_message_id??"",attachments:e.attachments??[],context_messages:e.context_messages??[]},s.transport)},pushStop(e){s&&y(s.mcpServer,"notifications/event_stop",{event_id:e.event_id,stop_id:e.stop_id,session_id:e.session_id,reason:e.reason??""},s.transport)},pushRevoke(e){s&&y(s.mcpServer,"notifications/event_revoke",{event_id:e.event_id,session_id:e.session_id,reason:e.reason??""},s.transport)},pushLocalAction(e){s&&y(s.mcpServer,"notifications/local_action",e,s.transport)}};async function g(e,t){if(new URL(e.url??"/",`http://${e.headers.host??"localhost"}`).pathname!==n.endpoint){t.writeHead(404,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Not Found"}));return}const i=m.validateRequest(e);if(!i.ok){t.writeHead(i.statusCode??403,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:i.message}));return}const o=e.method?.toUpperCase();if(o==="GET"){const d=f(e);if(!d||!s||s.sessionId!==d){t.writeHead(400,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Bad Request: missing or invalid session"}));return}c.touchActivity(d),await s.transport.handleRequest(e,t);return}if(o==="DELETE"){await x(e,t);return}if(o==="POST"){await _(e,t);return}t.writeHead(405,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Method Not Allowed"}))}async function _(e,t){const i=await b(e);let o;try{o=JSON.parse(i)}catch{t.writeHead(400,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Invalid JSON body"}));return}const d=I(o),l=e.headers.accept??"";if(!l.includes("application/json")||!l.includes("text/event-stream")){t.writeHead(406,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Not Acceptable: Accept header must include both application/json and text/event-stream"}));return}if(d)await T(e,t,o);else{const u=f(e);if(!u){t.writeHead(400,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Bad Request: missing Mcp-Session-Id header"}));return}if(!s||s.sessionId!==u){t.writeHead(404,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Not Found: session does not exist or has expired"}));return}c.touchActivity(u),await s.transport.handleRequest(e,t,o)}}async function T(e,t,i){if(s){t.writeHead(503,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Service Unavailable: gateway already has an active session"}));return}const o=c.createSession().sessionId,d=new j({sessionIdGenerator:()=>o,enableJsonResponse:!1,onsessioninitialized:()=>{},onsessionclosed:()=>{w(o)}}),l=new O({name:"grix-mcp-server",version:"1.0.0"},{capabilities:{tools:{}}});H(l,o),await l.connect(d),s={transport:d,mcpServer:l,sessionId:o},await d.handleRequest(e,t,i);try{c.markReady(o)}catch{}}async function x(e,t){const i=f(e);if(!i){t.writeHead(400,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Bad Request: missing Mcp-Session-Id header"}));return}if(!s||s.sessionId!==i){t.writeHead(404,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Not Found: session does not exist or has expired"}));return}await s.transport.handleRequest(e,t)}function H(e,t){const i=S.getTools();e.setRequestHandler(R,async()=>({tools:i.map(o=>({name:o.name,description:o.description,inputSchema:o.inputSchema}))})),e.setRequestHandler(E,async o=>{const{name:d,arguments:l}=o.params,u=c.getSession(t);return!u||u.state!=="ready"?{content:[{type:"text",text:`Session \u72B6\u6001\u4E0D\u53EF\u7528: ${u?.state??"unknown"}`}],isError:!0}:r.status!=="ready"?{content:[{type:"text",text:`\u8FDE\u63A5\u4E0D\u53EF\u7528: \u5F53\u524D\u72B6\u6001\u4E3A ${r.status}`}],isError:!0}:(c.touchActivity(t),v.execute(r,d,l??{},n.invokeTimeoutMs))})}async function w(e){if(s?.sessionId===e){try{await s.mcpServer.close()}catch{}s=null}try{await c.closeSession(e)}catch{}}}function y(n,r,a,p){return p?p.send({jsonrpc:"2.0",method:r,params:a}):Promise.resolve()}function f(n){const r=n.headers["mcp-session-id"];return Array.isArray(r)?r[0]:r||void 0}function I(n){return n&&typeof n=="object"&&"method"in n?n.method==="initialize":Array.isArray(n)?n.some(r=>r&&typeof r=="object"&&r.method==="initialize"):!1}function b(n){return new Promise((r,a)=>{const p=[];n.on("data",m=>p.push(m)),n.on("end",()=>r(Buffer.concat(p).toString("utf-8"))),n.on("error",a)})}export{q as createMcpGateway};
1
+ import{createServer as N}from"node:http";import{Server as O}from"@modelcontextprotocol/sdk/server/index.js";import{StreamableHTTPServerTransport as j}from"@modelcontextprotocol/sdk/server/streamableHttp.js";import{ListToolsRequestSchema as R,CallToolRequestSchema as E}from"@modelcontextprotocol/sdk/types.js";import{createSecurityPolicy as h}from"./security.js";import{SessionManagerImpl as q}from"./session-manager.js";import{ToolRegistryImpl as A}from"./tool-registry.js";import{ToolExecutorImpl as C}from"./tool-executor.js";function J(n,r){let a=null,p=!1,m,c;const S=new A,v=new C;let s=null;return{async start(){m=h({serverPort:n.port,allowedOrigins:n.allowedOrigins??[],allowedHosts:n.allowedHosts??[]}),c=new q({maxSessions:1,sessionTimeoutMs:n.sessionTimeoutMs,onSessionExpired:e=>{w(e)}}),a=N((e,t)=>{g(e,t)}),await new Promise((e,t)=>{a.on("error",t),a.listen(n.port,n.bind,()=>{a.removeListener("error",t),p=!0;const i=a.address();i&&typeof i=="object"&&(n.port=i.port,m=h({serverPort:n.port,allowedOrigins:n.allowedOrigins??[],allowedHosts:n.allowedHosts??[]})),e()})})},async stop(){if(p=!1,a&&(await new Promise(e=>{a.close(()=>e())}),a=null),s){try{await s.transport.close()}catch{}try{await s.mcpServer.close()}catch{}s=null}c&&(await c.closeAll(),c.dispose())},getStatus(){return{listening:p,url:`http://${n.bind}:${n.port}${n.endpoint}`,activeSessions:s?1:0}},pushEvent(e){s&&y(s.mcpServer,"notifications/message",{event_id:e.event_id,session_id:e.session_id,sender_id:e.sender_id??"",content:e.content,msg_type:e.msg_type??1,msg_id:e.msg_id??"",session_type:e.session_type??1,quoted_message_id:e.quoted_message_id??"",attachments:e.attachments??[],context_messages:e.context_messages??[]},s.transport)},pushStop(e){s&&y(s.mcpServer,"notifications/event_stop",{event_id:e.event_id,stop_id:e.stop_id,session_id:e.session_id,reason:e.reason??""},s.transport)},pushRevoke(e){s&&y(s.mcpServer,"notifications/event_revoke",{event_id:e.event_id,session_id:e.session_id,reason:e.reason??""},s.transport)},pushLocalAction(e){s&&y(s.mcpServer,"notifications/local_action",e,s.transport)}};async function g(e,t){if(new URL(e.url??"/",`http://${e.headers.host??"localhost"}`).pathname!==n.endpoint){t.writeHead(404,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Not Found"}));return}const i=m.validateRequest(e);if(!i.ok){t.writeHead(i.statusCode??403,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:i.message}));return}const o=e.method?.toUpperCase();if(o==="GET"){const d=f(e);if(!d||!s||s.sessionId!==d){t.writeHead(400,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Bad Request: missing or invalid session"}));return}c.touchActivity(d),await s.transport.handleRequest(e,t);return}if(o==="DELETE"){await x(e,t);return}if(o==="POST"){await _(e,t);return}t.writeHead(405,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Method Not Allowed"}))}async function _(e,t){const i=await b(e);let o;try{o=JSON.parse(i)}catch{t.writeHead(400,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Invalid JSON body"}));return}const d=I(o),l=e.headers.accept??"";if(!l.includes("application/json")||!l.includes("text/event-stream")){t.writeHead(406,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Not Acceptable: Accept header must include both application/json and text/event-stream"}));return}if(d)await T(e,t,o);else{const u=f(e);if(!u){t.writeHead(400,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Bad Request: missing Mcp-Session-Id header"}));return}if(!s||s.sessionId!==u){t.writeHead(404,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Not Found: session does not exist or has expired"}));return}c.touchActivity(u),await s.transport.handleRequest(e,t,o)}}async function T(e,t,i){if(s){t.writeHead(503,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Service Unavailable: gateway already has an active session"}));return}const o=c.createSession().sessionId,d=new j({sessionIdGenerator:()=>o,enableJsonResponse:!1,onsessioninitialized:()=>{},onsessionclosed:()=>{w(o)}}),l=new O({name:"grix-mcp-server",version:"1.0.0"},{capabilities:{tools:{}}});H(l,o),await l.connect(d),s={transport:d,mcpServer:l,sessionId:o},await d.handleRequest(e,t,i);try{c.markReady(o)}catch{}}async function x(e,t){const i=f(e);if(!i){t.writeHead(400,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Bad Request: missing Mcp-Session-Id header"}));return}if(!s||s.sessionId!==i){t.writeHead(404,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Not Found: session does not exist or has expired"}));return}await s.transport.handleRequest(e,t)}function H(e,t){const i=S.getTools();e.setRequestHandler(R,async()=>({tools:i.map(o=>({name:o.name,description:o.description,inputSchema:o.inputSchema}))})),e.setRequestHandler(E,async o=>{const{name:d,arguments:l}=o.params,u=c.getSession(t);return!u||u.state!=="ready"?{content:[{type:"text",text:`Session \u72B6\u6001\u4E0D\u53EF\u7528: ${u?.state??"unknown"}`}],isError:!0}:r.status!=="ready"?{content:[{type:"text",text:`\u8FDE\u63A5\u4E0D\u53EF\u7528: \u5F53\u524D\u72B6\u6001\u4E3A ${r.status}`}],isError:!0}:(c.touchActivity(t),v.execute(r,d,l??{},n.invokeTimeoutMs))})}async function w(e){if(s?.sessionId===e){try{await s.mcpServer.close()}catch{}s=null}try{await c.closeSession(e)}catch{}}}function y(n,r,a,p){return p?p.send({jsonrpc:"2.0",method:r,params:a}):Promise.resolve()}function f(n){const r=n.headers["mcp-session-id"];return Array.isArray(r)?r[0]:r||void 0}function I(n){return n&&typeof n=="object"&&"method"in n?n.method==="initialize":Array.isArray(n)?n.some(r=>r&&typeof r=="object"&&r.method==="initialize"):!1}function b(n){return new Promise((r,a)=>{const p=[];n.on("data",m=>p.push(m)),n.on("end",()=>r(Buffer.concat(p).toString("utf-8"))),n.on("error",a)})}export{J as createMcpGateway};
@@ -1 +1 @@
1
- function a(e){const t=new Set([`http://127.0.0.1:${e.serverPort}`,`http://localhost:${e.serverPort}`,...e.allowedOrigins]),o=new Set([`127.0.0.1:${e.serverPort}`,`localhost:${e.serverPort}`,...e.allowedHosts]);return{validateRequest(s){const r=i(s,t);if(!r.ok)return r;const n=l(s,o);return n.ok?{ok:!0}:n}}}function i(e,t){const o=e.headers.origin;return o?t.has(o)?{ok:!0}:{ok:!1,statusCode:403,message:`Origin not allowed: ${o}`}:{ok:!0}}function l(e,t){const o=e.headers.host;return o?t.has(o)?{ok:!0}:{ok:!1,statusCode:403,message:`Host not allowed: ${o}`}:{ok:!1,statusCode:403,message:"Missing Host header"}}export{a as createSecurityPolicy};
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
- const o={required:["action"],properties:{action:{type:"string",enum:["contact_search","session_search","message_history","message_search"]},id:{type:"string"},keyword:{type:"string",maxLength:200},limit:{type:"integer",minimum:1,maximum:100},offset:{type:"integer",minimum:0},sessionId:{type:"string"},beforeId:{type:"string"}}},a={required:["action"],properties:{action:{type:"string",enum:["create","detail","leave","add_members","remove_members","update_member_role","update_all_members_muted","update_member_speaking","dissolve"]},sessionId:{type:"string"},name:{type:"string",maxLength:128},memberIds:{type:"array",items:{type:"string"},maxItems:100},memberTypes:{type:"array",items:{type:"integer",enum:[1,2]}},memberId:{type:"string"},role:{type:"integer",enum:[1,2]},memberType:{type:"integer"},allMembersMuted:{type:"boolean"},isSpeakMuted:{type:"boolean"},canSpeakWhenAllMuted:{type:"boolean"}}},p={required:["sessionId","content"],properties:{sessionId:{type:"string"},content:{type:"string",maxLength:1e4},msgType:{type:"integer"},quotedMessageId:{type:"string"},threadId:{type:"string"}}},m={required:["sessionId","msgId"],properties:{sessionId:{type:"string"},msgId:{type:"string"}}},g={required:["action"],properties:{action:{type:"string",enum:["create_agent","list_categories","create_category","update_category","assign_category","rotate_api_key"]},agentName:{type:"string"},introduction:{type:"string"},isMain:{type:"boolean"},agentId:{type:"string"},categoryId:{type:"string"},name:{type:"string"},parentId:{type:"string"},sortOrder:{type:"integer"}}},y={required:["session_id","text"],properties:{event_id:{type:"string"},session_id:{type:"string"},text:{type:"string",maxLength:5e4},quoted_message_id:{type:"string"},is_final:{type:"boolean"}}},d={required:["event_id","status"],properties:{event_id:{type:"string"},status:{type:"string",enum:["responded","canceled","failed"]},msg:{type:"string",maxLength:500}}},c={required:["event_id"],properties:{event_id:{type:"string"},session_id:{type:"string"}}},_={required:["session_id","active"],properties:{session_id:{type:"string"},active:{type:"boolean"},event_id:{type:"string"}}},l={required:["action"],properties:{action:{type:"string",enum:["pair_approve","pair_deny","allow_sender","remove_sender","set_policy"]},code:{type:"string"},sender_id:{type:"string"},policy:{type:"string",enum:["allowlist","open","disabled"]}}},f={required:[],properties:{}},C={grix_query:o,grix_group:a,grix_message_send:p,grix_message_unsend:m,grix_admin:g,grix_reply:y,grix_complete:d,grix_event_ack:c,grix_composing:_,grix_access_control:l,grix_status:f};function B(r,t){const e=C[r];if(!e)return{valid:!1,error:`\u672A\u77E5\u5DE5\u5177: ${r}`};for(const u of e.required)if(t[u]===void 0||t[u]===null)return{valid:!1,error:`\u7F3A\u5C11\u5FC5\u586B\u53C2\u6570: ${u}`};for(const[u,n]of Object.entries(t)){if(n==null)continue;const i=e.properties[u];if(!i)continue;const s=$(u,n,i);if(s)return{valid:!1,error:s}}return{valid:!0}}function $(r,t,e){switch(e.type){case"string":if(typeof t!="string")return`\u53C2\u6570 ${r} \u7C7B\u578B\u9519\u8BEF: \u671F\u671B string\uFF0C\u5B9E\u9645 ${typeof t}`;if(e.maxLength!==void 0&&t.length>e.maxLength)return`\u53C2\u6570 ${r} \u8D85\u8FC7\u6700\u5927\u957F\u5EA6 ${e.maxLength}\uFF0C\u5B9E\u9645 ${t.length}`;if(e.enum&&!e.enum.includes(t))return`\u53C2\u6570 ${r} \u503C "${t}" \u4E0D\u5728\u5141\u8BB8\u8303\u56F4 [${e.enum.join(", ")}]`;break;case"integer":if(typeof t!="number"||!Number.isInteger(t))return`\u53C2\u6570 ${r} \u7C7B\u578B\u9519\u8BEF: \u671F\u671B integer\uFF0C\u5B9E\u9645 ${typeof t=="number"?"\u6D6E\u70B9\u6570":typeof t}`;if(e.minimum!==void 0&&t<e.minimum)return`\u53C2\u6570 ${r} \u503C ${t} \u5C0F\u4E8E\u6700\u5C0F\u503C ${e.minimum}`;if(e.maximum!==void 0&&t>e.maximum)return`\u53C2\u6570 ${r} \u503C ${t} \u5927\u4E8E\u6700\u5927\u503C ${e.maximum}`;if(e.enum&&!e.enum.includes(t))return`\u53C2\u6570 ${r} \u503C ${t} \u4E0D\u5728\u5141\u8BB8\u8303\u56F4 [${e.enum.join(", ")}]`;break;case"boolean":if(typeof t!="boolean")return`\u53C2\u6570 ${r} \u7C7B\u578B\u9519\u8BEF: \u671F\u671B boolean\uFF0C\u5B9E\u9645 ${typeof t}`;break;case"array":if(!Array.isArray(t))return`\u53C2\u6570 ${r} \u7C7B\u578B\u9519\u8BEF: \u671F\u671B array\uFF0C\u5B9E\u9645 ${typeof t}`;if(e.maxItems!==void 0&&t.length>e.maxItems)return`\u53C2\u6570 ${r} \u8D85\u8FC7\u6700\u5927\u5143\u7D20\u6570 ${e.maxItems}\uFF0C\u5B9E\u9645 ${t.length}`;if(e.items)for(let u=0;u<t.length;u++){const n=t[u];if(e.items.type==="string"&&typeof n!="string")return`\u53C2\u6570 ${r}[${u}] \u7C7B\u578B\u9519\u8BEF: \u671F\u671B string\uFF0C\u5B9E\u9645 ${typeof n}`;if(e.items.type==="integer"){if(typeof n!="number"||!Number.isInteger(n))return`\u53C2\u6570 ${r}[${u}] \u7C7B\u578B\u9519\u8BEF: \u671F\u671B integer\uFF0C\u5B9E\u9645 ${typeof n}`;if(e.items.enum&&!e.items.enum.includes(n))return`\u53C2\u6570 ${r}[${u}] \u503C ${n} \u4E0D\u5728\u5141\u8BB8\u8303\u56F4 [${e.items.enum.join(", ")}]`}}break}}export{B as validateToolArgs};
1
+ const o={required:["action"],properties:{action:{type:"string",enum:["contact_search","session_search","message_history","message_search"]},id:{type:"string"},keyword:{type:"string",maxLength:200},limit:{type:"integer",minimum:1,maximum:100},offset:{type:"integer",minimum:0},sessionId:{type:"string"},beforeId:{type:"string"}}},a={required:["action"],properties:{action:{type:"string",enum:["create","detail","leave","add_members","remove_members","update_member_role","update_all_members_muted","update_member_speaking","dissolve"]},sessionId:{type:"string"},name:{type:"string",maxLength:128},memberIds:{type:"array",items:{type:"string"},maxItems:100},memberTypes:{type:"array",items:{type:"integer",enum:[1,2]}},memberId:{type:"string"},role:{type:"integer",enum:[1,2]},memberType:{type:"integer"},allMembersMuted:{type:"boolean"},isSpeakMuted:{type:"boolean"},canSpeakWhenAllMuted:{type:"boolean"}}},p={required:["sessionId","content"],properties:{sessionId:{type:"string"},content:{type:"string",maxLength:1e4},msgType:{type:"integer"},quotedMessageId:{type:"string"},threadId:{type:"string"}}},m={required:["sessionId","msgId"],properties:{sessionId:{type:"string"},msgId:{type:"string"}}},g={required:["action"],properties:{action:{type:"string",enum:["create_agent","list_categories","create_category","update_category","assign_category","rotate_api_key"]},agentName:{type:"string"},introduction:{type:"string"},isMain:{type:"boolean"},agentId:{type:"string"},categoryId:{type:"string"},name:{type:"string"},parentId:{type:"string"},sortOrder:{type:"integer"}}},y={required:["session_id","text"],properties:{event_id:{type:"string"},session_id:{type:"string"},text:{type:"string",maxLength:5e4},quoted_message_id:{type:"string"},is_final:{type:"boolean"}}},d={required:["event_id","status"],properties:{event_id:{type:"string"},status:{type:"string",enum:["responded","canceled","failed"]},msg:{type:"string",maxLength:500}}},c={required:["event_id"],properties:{event_id:{type:"string"},session_id:{type:"string"}}},l={required:["session_id","active"],properties:{session_id:{type:"string"},active:{type:"boolean"},event_id:{type:"string"}}},_={required:["action"],properties:{action:{type:"string",enum:["pair_approve","pair_deny","allow_sender","remove_sender","set_policy"]},code:{type:"string"},sender_id:{type:"string"},policy:{type:"string",enum:["allowlist","open","disabled"]}}},f={required:[],properties:{}},C={grix_query:o,grix_group:a,grix_message_send:p,grix_message_unsend:m,grix_admin:g,grix_reply:y,grix_complete:d,grix_event_ack:c,grix_composing:l,grix_access_control:_,grix_status:f};function B(r,t){const e=C[r];if(!e)return{valid:!1,error:`\u672A\u77E5\u5DE5\u5177: ${r}`};for(const i of e.required)if(t[i]===void 0||t[i]===null)return{valid:!1,error:`\u7F3A\u5C11\u5FC5\u586B\u53C2\u6570: ${i}`};for(const[i,u]of Object.entries(t)){if(u==null)continue;const n=e.properties[i];if(!n)continue;const s=$(i,u,n);if(s)return{valid:!1,error:s}}return{valid:!0}}function $(r,t,e){switch(e.type){case"string":if(typeof t!="string")return`\u53C2\u6570 ${r} \u7C7B\u578B\u9519\u8BEF: \u671F\u671B string\uFF0C\u5B9E\u9645 ${typeof t}`;if(e.maxLength!==void 0&&t.length>e.maxLength)return`\u53C2\u6570 ${r} \u8D85\u8FC7\u6700\u5927\u957F\u5EA6 ${e.maxLength}\uFF0C\u5B9E\u9645 ${t.length}`;if(e.enum&&!e.enum.includes(t))return`\u53C2\u6570 ${r} \u503C "${t}" \u4E0D\u5728\u5141\u8BB8\u8303\u56F4 [${e.enum.join(", ")}]`;break;case"integer":if(typeof t!="number"||!Number.isInteger(t))return`\u53C2\u6570 ${r} \u7C7B\u578B\u9519\u8BEF: \u671F\u671B integer\uFF0C\u5B9E\u9645 ${typeof t=="number"?"\u6D6E\u70B9\u6570":typeof t}`;if(e.minimum!==void 0&&t<e.minimum)return`\u53C2\u6570 ${r} \u503C ${t} \u5C0F\u4E8E\u6700\u5C0F\u503C ${e.minimum}`;if(e.maximum!==void 0&&t>e.maximum)return`\u53C2\u6570 ${r} \u503C ${t} \u5927\u4E8E\u6700\u5927\u503C ${e.maximum}`;if(e.enum&&!e.enum.includes(t))return`\u53C2\u6570 ${r} \u503C ${t} \u4E0D\u5728\u5141\u8BB8\u8303\u56F4 [${e.enum.join(", ")}]`;break;case"boolean":if(typeof t!="boolean")return`\u53C2\u6570 ${r} \u7C7B\u578B\u9519\u8BEF: \u671F\u671B boolean\uFF0C\u5B9E\u9645 ${typeof t}`;break;case"array":if(!Array.isArray(t))return`\u53C2\u6570 ${r} \u7C7B\u578B\u9519\u8BEF: \u671F\u671B array\uFF0C\u5B9E\u9645 ${typeof t}`;if(e.maxItems!==void 0&&t.length>e.maxItems)return`\u53C2\u6570 ${r} \u8D85\u8FC7\u6700\u5927\u5143\u7D20\u6570 ${e.maxItems}\uFF0C\u5B9E\u9645 ${t.length}`;if(e.items)for(let i=0;i<t.length;i++){const u=t[i];if(e.items.type==="string"&&typeof u!="string")return`\u53C2\u6570 ${r}[${i}] \u7C7B\u578B\u9519\u8BEF: \u671F\u671B string\uFF0C\u5B9E\u9645 ${typeof u}`;if(e.items.type==="integer"){if(typeof u!="number"||!Number.isInteger(u))return`\u53C2\u6570 ${r}[${i}] \u7C7B\u578B\u9519\u8BEF: \u671F\u671B integer\uFF0C\u5B9E\u9645 ${typeof u}`;if(e.items.enum&&!e.items.enum.includes(u))return`\u53C2\u6570 ${r}[${i}] \u503C ${u} \u4E0D\u5728\u5141\u8BB8\u8303\u56F4 [${e.items.enum.join(", ")}]`}}break}}export{B as validateToolArgs};
@@ -1 +1 @@
1
- import{EventEmitter as w}from"events";import{log as a}from"../core/log/index.js";import{AgentEventType as m}from"../types/events.js";import{mapSessionUpdate as M}from"./event-mapper.js";class c extends Error{authMethods;constructor(e){super("ACP authentication required"),this.name="AcpAuthRequiredError",this.authMethods=e}}function u(n){if(!n||typeof n!="object")return!1;const e=n;return e.code===-32e3?!0:e.code===-32603&&e.data?.details?/\b(401|token\s*expired|access\s*token)\b/i.test(e.data.details):!1}function h(n){return!n||typeof n!="object"?[{id:"oauth"}]:n.data?.authMethods??[{id:"oauth"}]}class S extends w{acpSessionId="";alive=!1;pendingPermissions=new Map;availableModes=[];availableModels=[];currentMode="";currentModel="";listSupported=!1;loadSupported=!1;transport;constructor(){super(),this.transport=null}get sessionId(){return this.acpSessionId}get isAlive(){return this.alive}get modes(){return[...this.availableModes]}get mode(){return this.currentMode}get models(){return[...this.availableModels]}get model(){return this.currentModel}get sessionOptions(){return{modes:[...this.availableModes],currentModeId:this.currentMode,models:[...this.availableModels],currentModelId:this.currentModel}}async connect(e){this.transport=e.transport,this.transport.on("close",()=>{this.alive&&(this.alive=!1,this.emit("session-lost"))}),this.transport.setHandlers((t,i)=>{t==="session/update"&&this.handleSessionUpdate(i)},(t,i,r)=>{t==="session/request_permission"?this.handlePermissionRequest(i,r):t.startsWith("cursor/")?this.transport.respondSuccess(i,{}).catch(()=>{}):this.transport.respondError(i,-32601,"method not implemented").catch(()=>{})});const s=await this.initialize();if(this.listSupported=!!s.agentCapabilities?.sessionCapabilities?.list,this.loadSupported=!!s.agentCapabilities?.loadSession,e.authMethod)try{await this.transport.call("authenticate",{methodId:e.authMethod})}catch(t){throw u(t)?new c(h(t)):t}if(e.sessionId&&e.sessionId!=="__continue__"){if(!this.loadSupported)throw new Error(`session/load not supported by agent, cannot resume session ${e.sessionId}`);try{if(await this.loadSession(e.sessionId,e)){this.alive=!0;return}}catch(t){throw u(t)?new c(h(t)):t}throw new Error(`session/load failed for session ${e.sessionId}`)}try{await this.newSession(e)}catch(t){throw u(t)?new c(h(t)):t}e.initialMode&&this.setLiveMode(e.initialMode).catch(()=>{}),this.alive=!0}async initialize(){return await this.transport.call("initialize",{protocolVersion:1,clientCapabilities:{fs:{readTextFile:!1,writeTextFile:!1},terminal:!1},clientInfo:{name:"grix-connector-acp",version:"0.2.0"}})}async newSession(e){const s=(e.mcpServers??[]).map(i=>{const r={name:i.name,command:i.command};return i.args&&(r.args=i.args),i.env&&Object.keys(i.env).length>0&&(r.env=Object.entries(i.env).map(([l,d])=>({name:l,value:d}))),r}),o={cwd:e.cwd||process.cwd(),mcpServers:s};e.additionalDirectories&&e.additionalDirectories.length>0&&(o.additionalDirectories=e.additionalDirectories);const t=await this.transport.call("session/new",o);if(!t?.sessionId)throw new Error("session/new returned empty sessionId");this.acpSessionId=t.sessionId,this.absorbModes(t.modes),this.absorbModels(t.models)}async loadSession(e,s){const o=(s.mcpServers??[]).map(r=>({name:r.name,command:r.command,...r.args?{args:r.args}:{},...r.env?{env:r.env}:{}})),t={sessionId:e,cwd:s.cwd||process.cwd(),mcpServers:o};s.additionalDirectories&&s.additionalDirectories.length>0&&(t.additionalDirectories=s.additionalDirectories);const i=await this.transport.call("session/load",t);return i?.sessionId?(this.acpSessionId=i.sessionId,this.absorbModes(i.modes),this.absorbModels(i.models),!0):i&&(i.modes||i.models)?(this.acpSessionId=e,this.absorbModes(i.modes),this.absorbModels(i.models),!0):!1}absorbModes(e){e?.availableModes?.length&&(this.availableModes=[...e.availableModes],e.currentModeId&&(this.currentMode=e.currentModeId))}absorbModels(e){e?.availableModels?.length&&(this.availableModels=[...e.availableModels],e.currentModelId&&(this.currentModel=e.currentModelId))}async send(e,s,o){if(!this.alive)throw new Error("session not active");if(!this.acpSessionId)throw new Error("no agent session id");const t=[{type:"text",text:e}];if(s)for(const i of s)t.push({type:"image",data:i.data.toString("base64"),mimeType:i.mimeType});await this.transport.call("session/prompt",{sessionId:this.acpSessionId,prompt:t,...this.currentModel?{modelId:this.currentModel}:{}}),this.emit("event",{type:m.Result,sessionId:this.acpSessionId,done:!0})}async cancel(){if(this.acpSessionId)try{await this.transport.notify("session/cancel",{sessionId:this.acpSessionId})}catch{}}async authenticate(e){await this.transport.call("authenticate",{methodId:e})}async respondPermission(e,s){const o=this.pendingPermissions.get(e);if(!o)throw new Error(`unknown permission request: ${e}`);this.pendingPermissions.delete(e);const t=this.pickPermissionOptionId(s.behavior,o.options),i=this.buildPermissionResult(s.behavior,t);await this.transport.respondSuccess(o.rpcId,i)}async ping(e=1e4){if(!this.alive)return!1;try{const s=new AbortController,o=setTimeout(()=>s.abort(),e);return await this.transport.call("session/list",{},s.signal),clearTimeout(o),!0}catch(s){return s?.code===-32601}}async setLiveMode(e){if(!this.acpSessionId)return a.warn("acp-client",`setLiveMode("${e}") skipped: no active session`),!1;const s=this.matchAvailableMode(e);if(!s)return a.warn("acp-client",`setLiveMode("${e}") failed: mode not found in available [${this.availableModes.map(o=>o.id).join(",")}]`),!1;try{const o=new AbortController,t=setTimeout(()=>o.abort(),8e3);return await this.transport.call("session/set_mode",{sessionId:this.acpSessionId,modeId:s},o.signal),clearTimeout(t),this.currentMode=s,!0}catch(o){return a.warn("acp-client",`setLiveMode("${s}") RPC failed: ${o instanceof Error?o.message:o}`),!1}}async setModel(e){if(!this.acpSessionId)return a.warn("acp-client",`setModel("${e}") skipped: no active session`),!1;try{const s=new AbortController,o=setTimeout(()=>s.abort(),8e3);return await this.transport.call("session/set_model",{sessionId:this.acpSessionId,modelId:e},s.signal),clearTimeout(o),this.currentModel=e,!0}catch(s){return a.warn("acp-client",`setModel("${e}") RPC failed: ${s instanceof Error?s.message:s}`),!1}}handleSessionUpdate(e){const s=M(this.acpSessionId,e);for(const o of s)o.sessionId||(o.sessionId=this.acpSessionId),this.emit("event",o)}handlePermissionRequest(e,s){const o=s,t=o?.toolCall??{},i=String(e),r=Array.isArray(o?.options)?o.options:[];this.pendingPermissions.set(i,{rpcId:e,options:r});const l=t.title||t.kind||"permission",d=t.toolCallId||i,p=t.title||"",f={type:m.PermissionRequest,requestId:i,toolName:l,toolInput:p||d,sessionId:this.acpSessionId,permissionRequest:{requestId:i,toolCallId:d,toolName:l,toolTitle:p,options:r,rawParams:s}};this.emit("event",f)}matchAvailableMode(e){const s=e.toLowerCase();for(const o of this.availableModes)if(o.id.toLowerCase()===s||o.name.toLowerCase()===s)return o.id;return""}pickPermissionOptionId(e,s){if(s.length===0)return"";if(e==="deny"){for(const t of s)if(t.kind==="reject_once"||t.kind==="reject_always")return t.optionId;for(const t of s)if(t.kind.toLowerCase().includes("reject")||t.kind.toLowerCase().includes("deny"))return t.optionId;for(const t of s)if(t.name.toLowerCase().includes("reject")||t.name.toLowerCase().includes("deny"))return t.optionId;return s[s.length-1].optionId}const o=e==="allow-always"?"allow_always":e==="allow-once"?"allow_once":null;if(o){for(const t of s)if(t.kind===o)return t.optionId}for(const t of s)if(t.kind.toLowerCase().includes("allow"))return t.optionId;for(const t of s)if(t.name.toLowerCase().includes("allow"))return t.optionId;return s[0].optionId}buildPermissionResult(e,s){return e==="deny"?s?{outcome:{outcome:"selected",optionId:s}}:{outcome:{outcome:"cancelled"}}:s?{outcome:{outcome:"selected",optionId:s}}:{outcome:{outcome:"cancelled"}}}}export{c as AcpAuthRequiredError,S as AcpClient,u as isAuthRequiredError};
1
+ import{EventEmitter as w}from"events";import{log as a}from"../core/log/index.js";import{AgentEventType as m}from"../types/events.js";import{mapSessionUpdate as M}from"./event-mapper.js";class c extends Error{authMethods;constructor(e){super("ACP authentication required"),this.name="AcpAuthRequiredError",this.authMethods=e}}function u(n){if(!n||typeof n!="object")return!1;const e=n;return e.code===-32e3?!0:e.code===-32603&&e.data?.details?/\b(401|token\s*expired|access\s*token)\b/i.test(e.data.details):!1}function h(n){return!n||typeof n!="object"?[{id:"oauth"}]:n.data?.authMethods??[{id:"oauth"}]}class S extends w{acpSessionId="";alive=!1;pendingPermissions=new Map;availableModes=[];availableModels=[];currentMode="";currentModel="";listSupported=!1;loadSupported=!1;transport;constructor(){super(),this.transport=null}get sessionId(){return this.acpSessionId}get isAlive(){return this.alive}get modes(){return[...this.availableModes]}get mode(){return this.currentMode}get models(){return[...this.availableModels]}get model(){return this.currentModel}get sessionOptions(){return{modes:[...this.availableModes],currentModeId:this.currentMode,models:[...this.availableModels],currentModelId:this.currentModel}}async connect(e){this.transport=e.transport,this.transport.on("close",()=>{this.alive&&(this.alive=!1,this.emit("session-lost"))}),this.transport.setHandlers((t,o)=>{t==="session/update"&&this.handleSessionUpdate(o)},(t,o,r)=>{t==="session/request_permission"?this.handlePermissionRequest(o,r):t.startsWith("cursor/")?this.transport.respondSuccess(o,{}).catch(()=>{}):this.transport.respondError(o,-32601,"method not implemented").catch(()=>{})});const s=await this.initialize();if(this.listSupported=!!s.agentCapabilities?.sessionCapabilities?.list,this.loadSupported=!!s.agentCapabilities?.loadSession,e.authMethod)try{await this.transport.call("authenticate",{methodId:e.authMethod})}catch(t){throw u(t)?new c(h(t)):t}if(e.sessionId&&e.sessionId!=="__continue__"){if(!this.loadSupported)throw new Error(`session/load not supported by agent, cannot resume session ${e.sessionId}`);try{if(await this.loadSession(e.sessionId,e)){this.alive=!0;return}}catch(t){throw u(t)?new c(h(t)):t}throw new Error(`session/load failed for session ${e.sessionId}`)}try{await this.newSession(e)}catch(t){throw u(t)?new c(h(t)):t}e.initialMode&&this.setLiveMode(e.initialMode).catch(()=>{}),this.alive=!0}async initialize(){return await this.transport.call("initialize",{protocolVersion:1,clientCapabilities:{fs:{readTextFile:!1,writeTextFile:!1},terminal:!1},clientInfo:{name:"grix-connector-acp",version:"0.2.0"}})}async newSession(e){const s=(e.mcpServers??[]).map(o=>{const r={name:o.name,command:o.command};return o.args&&(r.args=o.args),o.env&&Object.keys(o.env).length>0&&(r.env=Object.entries(o.env).map(([l,d])=>({name:l,value:d}))),r}),i={cwd:e.cwd||process.cwd(),mcpServers:s};e.additionalDirectories&&e.additionalDirectories.length>0&&(i.additionalDirectories=e.additionalDirectories);const t=await this.transport.call("session/new",i);if(!t?.sessionId)throw new Error("session/new returned empty sessionId");this.acpSessionId=t.sessionId,this.absorbModes(t.modes),this.absorbModels(t.models)}async loadSession(e,s){const i=(s.mcpServers??[]).map(r=>({name:r.name,command:r.command,...r.args?{args:r.args}:{},...r.env?{env:r.env}:{}})),t={sessionId:e,cwd:s.cwd||process.cwd(),mcpServers:i};s.additionalDirectories&&s.additionalDirectories.length>0&&(t.additionalDirectories=s.additionalDirectories);const o=await this.transport.call("session/load",t);return o?.sessionId?(this.acpSessionId=o.sessionId,this.absorbModes(o.modes),this.absorbModels(o.models),!0):o&&(o.modes||o.models)?(this.acpSessionId=e,this.absorbModes(o.modes),this.absorbModels(o.models),!0):!1}absorbModes(e){e?.availableModes?.length&&(this.availableModes=[...e.availableModes],e.currentModeId&&(this.currentMode=e.currentModeId))}absorbModels(e){e?.availableModels?.length&&(this.availableModels=[...e.availableModels],e.currentModelId&&(this.currentModel=e.currentModelId))}async send(e,s,i){if(!this.alive)throw new Error("session not active");if(!this.acpSessionId)throw new Error("no agent session id");const t=[{type:"text",text:e}];if(s)for(const o of s)t.push({type:"image",data:o.data.toString("base64"),mimeType:o.mimeType});await this.transport.call("session/prompt",{sessionId:this.acpSessionId,prompt:t,...this.currentModel?{modelId:this.currentModel}:{}}),this.emit("event",{type:m.Result,sessionId:this.acpSessionId,done:!0})}async cancel(){if(this.acpSessionId)try{await this.transport.notify("session/cancel",{sessionId:this.acpSessionId})}catch{}}async authenticate(e){await this.transport.call("authenticate",{methodId:e})}async respondPermission(e,s){const i=this.pendingPermissions.get(e);if(!i)throw new Error(`unknown permission request: ${e}`);this.pendingPermissions.delete(e);const t=this.pickPermissionOptionId(s.behavior,i.options),o=this.buildPermissionResult(s.behavior,t);await this.transport.respondSuccess(i.rpcId,o)}async ping(e=1e4){if(!this.alive)return!1;try{const s=new AbortController,i=setTimeout(()=>s.abort(),e);return await this.transport.call("session/list",{},s.signal),clearTimeout(i),!0}catch(s){return s?.code===-32601}}async setLiveMode(e){if(!this.acpSessionId)return a.warn("acp-client",`setLiveMode("${e}") skipped: no active session`),!1;const s=this.matchAvailableMode(e);if(!s)return a.warn("acp-client",`setLiveMode("${e}") failed: mode not found in available [${this.availableModes.map(i=>i.id).join(",")}]`),!1;try{const i=new AbortController,t=setTimeout(()=>i.abort(),8e3);return await this.transport.call("session/set_mode",{sessionId:this.acpSessionId,modeId:s},i.signal),clearTimeout(t),this.currentMode=s,!0}catch(i){return a.warn("acp-client",`setLiveMode("${s}") RPC failed: ${i instanceof Error?i.message:i}`),!1}}async setModel(e){if(!this.acpSessionId)return a.warn("acp-client",`setModel("${e}") skipped: no active session`),!1;try{const s=new AbortController,i=setTimeout(()=>s.abort(),8e3);return await this.transport.call("session/set_model",{sessionId:this.acpSessionId,modelId:e},s.signal),clearTimeout(i),this.currentModel=e,!0}catch(s){return a.warn("acp-client",`setModel("${e}") RPC failed: ${s instanceof Error?s.message:s}`),!1}}handleSessionUpdate(e){this.emit("activity");const s=M(this.acpSessionId,e);for(const i of s)i.sessionId||(i.sessionId=this.acpSessionId),this.emit("event",i)}handlePermissionRequest(e,s){const i=s,t=i?.toolCall??{},o=String(e),r=Array.isArray(i?.options)?i.options:[];this.pendingPermissions.set(o,{rpcId:e,options:r});const l=t.title||t.kind||"permission",d=t.toolCallId||o,p=t.title||"",f={type:m.PermissionRequest,requestId:o,toolName:l,toolInput:p||d,sessionId:this.acpSessionId,permissionRequest:{requestId:o,toolCallId:d,toolName:l,toolTitle:p,options:r,rawParams:s}};this.emit("event",f)}matchAvailableMode(e){const s=e.toLowerCase();for(const i of this.availableModes)if(i.id.toLowerCase()===s||i.name.toLowerCase()===s)return i.id;return""}pickPermissionOptionId(e,s){if(s.length===0)return"";if(e==="deny"){for(const t of s)if(t.kind==="reject_once"||t.kind==="reject_always")return t.optionId;for(const t of s)if(t.kind.toLowerCase().includes("reject")||t.kind.toLowerCase().includes("deny"))return t.optionId;for(const t of s)if(t.name.toLowerCase().includes("reject")||t.name.toLowerCase().includes("deny"))return t.optionId;return s[s.length-1].optionId}const i=e==="allow-always"?"allow_always":e==="allow-once"?"allow_once":null;if(i){for(const t of s)if(t.kind===i)return t.optionId}for(const t of s)if(t.kind.toLowerCase().includes("allow"))return t.optionId;for(const t of s)if(t.name.toLowerCase().includes("allow"))return t.optionId;return s[0].optionId}buildPermissionResult(e,s){return e==="deny"?s?{outcome:{outcome:"selected",optionId:s}}:{outcome:{outcome:"cancelled"}}:s?{outcome:{outcome:"selected",optionId:s}}:{outcome:{outcome:"cancelled"}}}}export{c as AcpAuthRequiredError,S as AcpClient,u as isAuthRequiredError};