grix-connector 3.15.3 → 3.15.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.
@@ -1 +1 @@
1
- import{readJSONFile as o,writeJSONFileAtomic as e}from"../util/json-file.js";function n(t){const r=o(t);return r&&typeof r=="object"&&"owners"in r&&Array.isArray(r.owners)?r.owners.filter(i=>typeof i=="string"&&i.trim().length>0):[]}async function s(t,r){await e(t,{owners:r})}export{n as readAllowlist,s as writeAllowlist};
1
+ import{readJSONFile as o,writeJSONFileAtomic as i}from"../util/json-file.js";function n(t){const r=o(t);return r&&typeof r=="object"&&"owners"in r&&Array.isArray(r.owners)?r.owners.filter(e=>typeof e=="string"&&e.trim().length>0):[]}async function s(t,r){await i(t,{owners:r})}export{n as readAllowlist,s as writeAllowlist};
@@ -1,2 +1,2 @@
1
- import{EventEmitter as $}from"node:events";import{randomUUID as b}from"node:crypto";import k from"node:os";import m from"ws";import{log as a}from"../log/index.js";import{getMachineName as w}from"../util/index.js";import{detectTailnetIPv4 as E,ensureServerAndGetPort as q,getFileServerHttpsPort as A}from"../files/file-serve.js";import{AUTH_CODE_AGENT_DELETED as p,KICKED_REASON_AGENT_DELETED as S,AgentDeletedError as v,RequestTimeoutError as y}from"./errors.js";function R(g){return g.replace(/(?<=[\[:,\[]\s*)(\d{16,})(?=\s*[,}\]\n])/g,'"$1"')}function P(g){const e=[...g??["stream_chunk","local_action_v1","agent_invoke"]];return e.includes("agent_invoke")||e.push("agent_invoke"),e.includes("event_result_ack")||e.push("event_result_ack"),e}const T="aibot-agent-api-v1",I=1;class f extends ${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;heartbeatFailures=0;static HEARTBEAT_MAX_FAILURES=2;connected=!1;reconnecting=!1;reconnectAttempts=0;everConnected=!1;agentDeleted=!1;config;packetLog;pendingInvokes=new Map;seqEventMap=new Map;pendingRequests=new Map;outboundBuffer=[];ackPolicy=null;constructor(e,t){super(),this.packetLog=t?.packetLog??null,this.config={url:e.url,agentId:e.agentId,apiKey:e.apiKey,clientType:e.clientType,clientVersion:e.clientVersion??"",adapterHint:e.adapterHint??"",capabilities:P(e.capabilities),localActions:e.localActions??["exec_approve","exec_reject"],skills:e.skills}}get isConnected(){return this.connected}async connect(){let e,t,s;const i=(async()=>{try{if(e=await E(),e!==void 0)try{t=await q(e);const n=A();n>0&&(s=n)}catch(n){a.warn("aibot",`file server pre-start failed: ${n}`)}}catch(n){a.warn("aibot",`tailnet detect failed: ${n}`)}})();return new Promise((n,h)=>{const c=new m(this.config.url);this.ws=c;const o=setTimeout(()=>{h(new Error("Auth timeout: no auth_ack received within 15s")),this.cleanupSocket()},15e3),d=++this.seq,r=setTimeout(()=>{this.pendingRequests.delete(d),h(new Error("Auth request timeout")),this.cleanupSocket()},15e3);this.pendingRequests.set(d,{expected:["auth_ack"],resolve:u=>{clearTimeout(o);const l=u.payload;l.code===0?(this.connected=!0,this.everConnected=!0,this.reconnectAttempts=0,l.heartbeat_sec&&(this.heartbeatSec=l.heartbeat_sec),l.ack_policy&&(this.ackPolicy=l.ack_policy,a.info("aibot",`ack_policy received: push_ack_timeout_ms=${l.ack_policy.push_ack_timeout_ms??"default"} max_retries=${l.ack_policy.max_retries??"default"} timeout_action=${l.ack_policy.timeout_action??"default"}`)),this.startHeartbeat(),this.flushOutboundBuffer(),this.emit("auth",l),n(l)):l.code===p?(this.agentDeleted=!0,h(new v(`Agent deleted: code=${l.code} msg=${l.msg}`))):h(new Error(`Auth failed: code=${l.code} msg=${l.msg}`))},reject:u=>{clearTimeout(o),h(u)},timer:r}),c.on("open",async()=>{await i;const u={agent_id:this.config.agentId,api_key:this.config.apiKey,client_type:this.config.clientType,protocol_version:T,contract_version:I,capabilities:this.config.capabilities??[],local_actions:this.config.localActions,skills:this.config.skills};this.config.sharedOwnerId&&(u.shared_owner_id=this.config.sharedOwnerId),this.config.clientVersion&&(u.client="grix-connector",u.client_version=this.config.clientVersion,u.host_type=this.config.clientType,u.host_version=this.config.clientVersion),this.config.adapterHint&&(u.adapter_hint=this.config.adapterHint),u.host_meta={hostname:w(),platform:k.platform(),arch:k.arch(),os_release:k.release(),...e!==void 0&&{tailnet_ip:e},...t!==void 0&&t>0&&{file_server_port:t},...s!==void 0&&s>0&&{file_server_https_port:s}},this.config.concurrency&&(u.concurrency=this.config.concurrency),this.sendPacket("auth",u,d)}),c.on("message",u=>{if(this.ws!==c)return;let l;try{l=JSON.parse(R(u.toString()))}catch{return}try{this.handlePacket(l)}catch(_){this.emitClientError(new Error(`handlePacket error: ${_}`))}}),c.on("close",(u,l)=>{if(this.ws!==c)return;this.connected=!1,this.stopHeartbeat(),this.rejectAllPendingRequests("websocket closed"),this.emit("close",u,l.toString());const _=u!==1e3&&this.everConnected&&!this.agentDeleted;a.info("aibot",`ws closed agent=${this.config.clientType}:${this.config.agentId} code=${u} reason=${l.toString()||"<none>"} everConnected=${this.everConnected} reconnecting=${this.reconnecting} agentDeleted=${this.agentDeleted} willReconnect=${_}`),_&&this.attemptReconnect()}),c.on("error",u=>{this.ws===c&&(this.emitClientError(u instanceof Error?u:new Error(String(u))),this.connected||h(u))})})}handlePacket(e){if(this.packetLog?.logInboundPacket(e.cmd,e.seq,e.payload),e.seq>0&&this.pendingRequests.has(e.seq)){const t=this.pendingRequests.get(e.seq);this.pendingRequests.delete(e.seq),clearTimeout(t.timer),t.expected.includes(e.cmd)?t.resolve(e):t.reject(new Error(`unexpected response: got ${e.cmd}, expected ${t.expected.join("/")}`));return}switch(e.cmd){case"auth_ack":break;case"ping":{this.sendPacket("pong",e.payload??{});break}case"event_msg":{this.emit("event",e.payload);break}case"local_action":{this.emit("localAction",e.payload);break}case"event_stop":{this.emit("stop",e.payload);break}case"event_revoke":{this.emit("revoke",e.payload);break}case"event_edit":{this.emit("edit",e.payload);break}case"event_cancel":{this.emit("eventCancel",e.payload);break}case"queue_clear":{this.emit("queueClear",e.payload);break}case"queue_reorder":{this.emit("queueReorder",e.payload);break}case"queue_snapshot_query":{this.emit("queueSnapshotQuery",e.payload);break}case"event_hold":{this.emit("eventHold",e.payload);break}case"queue_edit":{this.emit("queueEdit",e.payload);break}case"control_share_set":{this.emit("shareSet",e.payload);break}case"agent_profile_push":{this.emit("profilePush",e.payload);break}case"skill_sync":{this.emit("skillSync",e.payload);break}case"kicked":{const t=e.payload;if(this.emit("kicked",t),this.connected=!1,this.stopHeartbeat(),this.rejectAllPendingRequests("kicked"),this.outboundBuffer.length=0,t?.reason===S){if(this.agentDeleted=!0,this.reconnecting=!1,this.ws){try{this.ws.close(4001,"kicked")}catch{}this.ws=null}a.error("aibot",`kicked: agent deleted on platform agent=${this.config.clientType}:${this.config.agentId}, reconnect disabled`),this.emit("agentDeleted",{source:"kicked",reason:t.reason});break}if(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,s=[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}${s?` ${s}`:""}`));break}case"agent_invoke_result":{this.handleInvokeResult(e.payload);break}case"mcp_frame":{const t=e.payload;this.emit("mcpFrame",t.session_id??"",t.frame??null);break}case"send_ack":break;case"send_nack":{const t=e.payload;if(t.code===4003&&e.seq>0){const s=this.seqEventMap.get(e.seq);s&&(this.seqEventMap.delete(e.seq),this.purgeBufferedStreamChunks(s),a.warn("aibot",`stream chunk rejected (4003), purging buffered chunks for event=${s}`),this.emit("streamRejected",s,t.code))}break}case"local_action_ack":break;default:break}}sendEventAck(e){this.sendPacket("event_ack",e)||a.warn("aibot",`event_ack NOT sent (ws not open) event=${e.event_id} ws=${this.ws?`state=${this.ws.readyState}`:"null"}`)}sendStreamChunk(e){!e.delta_content&&!e.is_finish&&(a.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)||a.warn("aibot",`send_msg NOT sent (ws not open) event=${e.event_id??""} session=${e.session_id??""} ws=${this.ws?`state=${this.ws.readyState}`:"null"}`)}editMsg(e){this.sendPacket("edit_msg",e)}sendEventResult(e){if(!this.ws||this.ws.readyState!==m.OPEN){this.sendPacket("event_result",e);return}this.sendEventResultReliable(e)}sendLocalActionResult(e){this.sendPacket("local_action_result",e)}sendEventStopAck(e){this.sendPacket("event_stop_ack",e)}sendEventStopResult(e){this.sendPacket("event_stop_result",e)}sendSessionActivitySet(e){this.sendPacket("session_activity_set",e)}sendCodexEvent(e){this.sendPacket("codex_event",e)}sendUpdateBindingCard(e){this.sendPacket("update_binding_card",e)}sendSkillsUpdate(e){this.sendPacket("agent_skills_update",e)}sendPing(){this.sendPacket("ping",{})}sendEventState(e){this.sendPacket("event_state",e)}sendEventCancelResult(e){this.sendPacket("event_cancel_result",e)}sendQueueClearResult(e){this.sendPacket("queue_clear_result",e)}sendQueueReorderResult(e){this.sendPacket("queue_reorder_result",e)}sendEventHoldResult(e){this.sendPacket("event_hold_result",e)}sendQueueEditResult(e){this.sendPacket("queue_edit_result",e)}sendQueueSnapshot(e){this.sendPacket("queue_snapshot",e)}agentInvoke(e,t,s=15e3){return new Promise((i,n)=>{const h=b(),c=Math.max(1e3,Math.min(s,6e4)),o=setTimeout(()=>{this.pendingInvokes.delete(h),n(new Error(`agent_invoke timeout: ${e}`))},c);this.pendingInvokes.set(h,{resolve:i,reject:n,timer:o}),this.sendPacket("agent_invoke",{invoke_id:h,action:e,params:t,timeout_ms:c})})}sendMcpFrame(e,t){this.sendPacket("mcp_frame",{session_id:e,frame:t})}request(e,t,s){return new Promise((i,n)=>{const h=++this.seq,c=setTimeout(()=>{this.pendingRequests.delete(h),n(new y(`request timeout: ${e} (expected ${s.expected.join("/")})`))},s.timeoutMs);this.pendingRequests.set(h,{expected:s.expected,resolve:i,reject:n,timer:c}),this.sendPacket(e,t,h)||(this.pendingRequests.delete(h),clearTimeout(c),n(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,s=2e4){return this.request("delete_msg",{session_id:e,msg_id:t},{expected:["send_ack","send_nack","error"],timeoutMs:s})}async sendEventResultRequest(e,t=5e3){return this.request("event_result",e,{expected:["send_ack","send_nack","error"],timeoutMs:t})}disconnect(){a.info("aibot",`disconnect() agent=${this.config.clientType}:${this.config.agentId} wasConnected=${this.connected} reconnecting=${this.reconnecting} reconnectAttempts=${this.reconnectAttempts}`),this.connected=!1,this.everConnected=!1,this.reconnecting=!1,this.reconnectAttempts=0,this.stopHeartbeat(),this.rejectAllPendingInvokes("disconnect"),this.rejectAllPendingRequests("disconnect"),this.outboundBuffer.length=0,this.ws&&(this.ws.close(1e3,"client disconnect"),this.ws=null)}static MAX_CONSECUTIVE_AUTH_FAILURES=5;async attemptReconnect(){if(this.reconnecting||this.agentDeleted)return;this.reconnecting=!0,a.info("aibot",`attemptReconnect start agent=${this.config.clientType}:${this.config.agentId} fromAttempts=${this.reconnectAttempts}`),this.emit("disconnected");let e=0;for(;this.reconnecting;){const t=Math.min(1e3*2**this.reconnectAttempts,3e4),s=Math.floor(t*.2*Math.random());if(this.reconnectAttempts++,await new Promise(i=>setTimeout(i,t+s)),!this.reconnecting)return;try{await this.connect();const i=this.reconnectAttempts;this.reconnectAttempts=0,this.reconnecting=!1,a.info("aibot",`reconnect succeeded agent=${this.config.clientType}:${this.config.agentId} attempt=${i}`);return}catch(i){if(this.ws){try{this.ws.close()}catch{}this.ws=null}const n=i instanceof Error?i.message:String(i);if(a.warn("aibot",`reconnect failed agent=${this.config.clientType}:${this.config.agentId} attempt=${this.reconnectAttempts} err=${n}`),i instanceof v){this.agentDeleted=!0,this.reconnecting=!1,a.error("aibot",`reconnect aborted: agent deleted on platform agent=${this.config.clientType}:${this.config.agentId}`),this.emit("agentDeleted",{source:"auth_ack",code:p});return}if(/Auth failed/i.test(n)){if(e++,e>=f.MAX_CONSECUTIVE_AUTH_FAILURES){this.reconnecting=!1,a.error("aibot",`reconnect giving up after ${e} consecutive auth failures agent=${this.config.clientType}:${this.config.agentId}`);return}}else e=0}}}sendPacket(e,t,s){if(this.ws&&this.ws.readyState===m.OPEN){const i=this.ws.bufferedAmount>f.BACKPRESSURE_THRESHOLD;if(!i||!f.DROPPABLE_COMMANDS.has(e)){if(i&&f.DROPPABLE_COMMANDS.has(e))return!1;const n=s??++this.seq;if(e==="client_stream_chunk"&&t&&typeof t=="object"){const c=t.event_id;if(c&&(this.seqEventMap.set(n,c),this.seqEventMap.size>200)){const o=this.seqEventMap.keys().next().value;o!==void 0&&this.seqEventMap.delete(o)}}const h={cmd:e,seq:n,payload:t};this.packetLog?.logOutboundPacket(e,n,t,"sent");try{const c=this.ws.readyState,o=this.ws.bufferedAmount;return this.ws.send(JSON.stringify(h),d=>{if(e==="event_result"){const r=t;d?a.warn("aibot",`event_result ws send callback failed event=${r.event_id??""} status=${r.status??""} seq=${n} readyState=${c} bufferedAmount=${o} err=${d.message}`):a.info("aibot",`event_result ws send callback ok event=${r.event_id??""} status=${r.status??""} seq=${n} readyState=${c} bufferedAmount=${o}`)}else if(e==="client_stream_chunk"){const r=t;d?a.warn("aibot",`stream_chunk ws send failed event=${r.event_id??""} session=${r.session_id??""} seq=${n} chunk_seq=${r.chunk_seq??""} is_finish=${r.is_finish??""} readyState=${c} bufferedAmount=${o} err=${d.message}`):a.info("aibot",`stream_chunk ws send ok event=${r.event_id??""} session=${r.session_id??""} seq=${n} chunk_seq=${r.chunk_seq??""} is_finish=${r.is_finish??""} client_msg_id=${r.client_msg_id??""} quoted_message_id=${r.quoted_message_id??""} readyState=${c} bufferedAmount=${o}`)}else if(e==="event_ack"){const r=t;d?a.warn("aibot",`event_ack ws send failed event=${r.event_id??""} seq=${n} readyState=${c} bufferedAmount=${o} err=${d.message}`):a.info("aibot",`event_ack ws send ok event=${r.event_id??""} seq=${n} readyState=${c} bufferedAmount=${o}`)}else if(e==="send_msg"){const r=t;d?a.warn("aibot",`send_msg ws send failed event=${r.event_id??""} session=${r.session_id??""} seq=${n} readyState=${c} bufferedAmount=${o} err=${d.message}`):a.info("aibot",`send_msg ws send ok event=${r.event_id??""} session=${r.session_id??""} seq=${n} readyState=${c} bufferedAmount=${o}`)}else if(d){const r=t;a.warn("aibot",`${e} ws send failed seq=${n} session=${r.session_id??""} event=${r.event_id??""} client_msg_id=${r.client_msg_id??""} readyState=${c} bufferedAmount=${o} err=${d.message}`)}}),!0}catch(c){return this.emitClientError(new Error(`sendPacket failed: ${c}`)),!1}}}if(f.DROPPABLE_COMMANDS.has(e))return this.packetLog?.logOutboundPacket(e,s??0,t,"dropped"),!1;if(s!==void 0)return this.packetLog?.logOutboundPacket(e,s,t,"dropped"),!1;if(this.outboundBuffer.length>=f.MAX_OUTBOUND_BUFFER_SIZE&&(this.outboundBuffer=this.outboundBuffer.filter(i=>f.BUFFER_OVERFLOW_RETAIN_COMMANDS.has(i.cmd)),this.outboundBuffer.length>=f.MAX_OUTBOUND_BUFFER_SIZE&&this.outboundBuffer.shift()),this.outboundBuffer.push({cmd:e,payload:t}),this.packetLog?.logOutboundPacket(e,s??0,t,"buffered"),e==="client_stream_chunk"){const i=t;a.info("aibot",`stream_chunk buffered (ws not open) event=${i.event_id??""} session=${i.session_id??""} chunk_seq=${i.chunk_seq??""} is_finish=${i.is_finish??""} ws=${this.ws?`state=${this.ws.readyState}`:"null"}`)}return!1}async sendEventResultReliable(e){const t=this.ackPolicy?.max_retries??3,s=this.ackPolicy?.push_ack_timeout_ms??5e3,i=750;for(let n=1;n<=t;n++){const h=this.ws?.readyState??-1,c=this.ws?.bufferedAmount??0;a.info("aibot",`event_result send attempt event=${e.event_id} status=${e.status} attempt=${n}/${t} readyState=${h} bufferedAmount=${c}`);try{const o=await this.sendEventResultRequest(e,s);if(o.cmd==="send_ack"){const r=o.payload;a.info("aibot",`event_result ack event=${e.event_id} status=${e.status} attempt=${n}/${t} ack_event=${r.event_id??""} ack_status=${r.status??""}`);return}const d=o.payload;if(a.warn("aibot",`event_result rejected event=${e.event_id} status=${e.status} attempt=${n}/${t} cmd=${o.cmd} code=${d.code??""} msg=${d.msg??""}${d.ref_cmd?` ref_cmd=${d.ref_cmd}`:""}${d.ref_id?` ref_id=${d.ref_id}`:""}`),d.code===4003){a.warn("aibot",`event_result stopping retries: 4003 ownership denied event=${e.event_id}`);return}return}catch(o){const d=o instanceof Error?o.message:String(o);if(a.warn("aibot",`event_result attempt failed event=${e.event_id} status=${e.status} attempt=${n}/${t} err=${d}`),n===t){this.emitClientError(new Error(`event_result ack failed after ${t} attempts: event=${e.event_id} status=${e.status}`));return}await new Promise(r=>setTimeout(r,i*n))}}}purgeBufferedStreamChunks(e){const t=this.outboundBuffer.length;this.outboundBuffer=this.outboundBuffer.filter(s=>s.cmd!=="client_stream_chunk"?!0:s.payload?.event_id!==e),this.outboundBuffer.length<t&&a.info("aibot",`purged ${t-this.outboundBuffer.length} buffered stream chunks for event=${e}`)}emitClientError(e){if(this.listenerCount("error")===0){a.warn("aibot",`Client error (no listeners): ${e.message}`);return}this.emit("error",e)}flushOutboundBuffer(){if(this.outboundBuffer.length===0||!this.ws||this.ws.readyState!==m.OPEN)return;const e=this.outboundBuffer;this.outboundBuffer=[];for(const{cmd:t,payload:s}of e){const i=++this.seq;if(t==="client_stream_chunk"&&s&&typeof s=="object"){const h=s.event_id;h&&this.seqEventMap.set(i,h)}const n={cmd:t,seq:i,payload:s};try{this.ws.send(JSON.stringify(n))}catch{break}}if(this.seqEventMap.size>200){const t=[...this.seqEventMap.entries()].sort((s,i)=>s[0]-i[0]);this.seqEventMap.clear();for(const[s,i]of t.slice(-100))this.seqEventMap.set(s,i)}}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{}}startHeartbeat(){this.stopHeartbeat(),this.heartbeatFailures=0,this.heartbeatTimer=setInterval(()=>{this.connected&&this.request("ping",{ts:Date.now()},{expected:["pong"],timeoutMs:5e3}).then(()=>{this.heartbeatFailures=0}).catch(()=>{this.connected&&(this.heartbeatFailures++,!(this.heartbeatFailures<f.HEARTBEAT_MAX_FAILURES)&&(this.cleanupSocket(),this.attemptReconnect()))})},this.heartbeatSec*1e3)}stopHeartbeat(){this.heartbeatTimer&&(clearInterval(this.heartbeatTimer),this.heartbeatTimer=null)}}export{f as AibotClient};
1
+ import{EventEmitter as $}from"node:events";import{randomUUID as b}from"node:crypto";import k from"node:os";import m from"ws";import{log as a}from"../log/index.js";import{getMachineName as w}from"../util/index.js";import{detectTailnetIPv4 as E,ensureServerAndGetPort as q,getFileServerHttpsPort as S}from"../files/file-serve.js";import{AUTH_CODE_AGENT_DELETED as p,KICKED_REASON_AGENT_DELETED as y,AgentDeletedError as v,RequestTimeoutError as A}from"./errors.js";function R(g){return g.replace(/(?<=[\[:,\[]\s*)(\d{16,})(?=\s*[,}\]\n])/g,'"$1"')}function P(g){const e=[...g??["stream_chunk","local_action_v1","agent_invoke"]];return e.includes("agent_invoke")||e.push("agent_invoke"),e.includes("event_result_ack")||e.push("event_result_ack"),e}const T="aibot-agent-api-v1",I=1;class f extends ${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;heartbeatFailures=0;static HEARTBEAT_MAX_FAILURES=2;connected=!1;reconnecting=!1;reconnectAttempts=0;everConnected=!1;agentDeleted=!1;config;packetLog;pendingInvokes=new Map;seqEventMap=new Map;pendingRequests=new Map;outboundBuffer=[];ackPolicy=null;constructor(e,t){super(),this.packetLog=t?.packetLog??null,this.config={url:e.url,agentId:e.agentId,apiKey:e.apiKey,clientType:e.clientType,clientVersion:e.clientVersion??"",adapterHint:e.adapterHint??"",capabilities:P(e.capabilities),localActions:e.localActions??["exec_approve","exec_reject"],skills:e.skills,librarySkills:e.librarySkills}}get isConnected(){return this.connected}async connect(){let e,t,s;const i=(async()=>{try{if(e=await E(),e!==void 0)try{t=await q(e);const n=S();n>0&&(s=n)}catch(n){a.warn("aibot",`file server pre-start failed: ${n}`)}}catch(n){a.warn("aibot",`tailnet detect failed: ${n}`)}})();return new Promise((n,h)=>{const c=new m(this.config.url);this.ws=c;const o=setTimeout(()=>{h(new Error("Auth timeout: no auth_ack received within 15s")),this.cleanupSocket()},15e3),d=++this.seq,r=setTimeout(()=>{this.pendingRequests.delete(d),h(new Error("Auth request timeout")),this.cleanupSocket()},15e3);this.pendingRequests.set(d,{expected:["auth_ack"],resolve:u=>{clearTimeout(o);const l=u.payload;l.code===0?(this.connected=!0,this.everConnected=!0,this.reconnectAttempts=0,l.heartbeat_sec&&(this.heartbeatSec=l.heartbeat_sec),l.ack_policy&&(this.ackPolicy=l.ack_policy,a.info("aibot",`ack_policy received: push_ack_timeout_ms=${l.ack_policy.push_ack_timeout_ms??"default"} max_retries=${l.ack_policy.max_retries??"default"} timeout_action=${l.ack_policy.timeout_action??"default"}`)),this.startHeartbeat(),this.flushOutboundBuffer(),this.emit("auth",l),n(l)):l.code===p?(this.agentDeleted=!0,h(new v(`Agent deleted: code=${l.code} msg=${l.msg}`))):h(new Error(`Auth failed: code=${l.code} msg=${l.msg}`))},reject:u=>{clearTimeout(o),h(u)},timer:r}),c.on("open",async()=>{await i;const u={agent_id:this.config.agentId,api_key:this.config.apiKey,client_type:this.config.clientType,protocol_version:T,contract_version:I,capabilities:this.config.capabilities??[],local_actions:this.config.localActions,skills:this.config.skills,library_skills:this.config.librarySkills};this.config.sharedOwnerId&&(u.shared_owner_id=this.config.sharedOwnerId),this.config.clientVersion&&(u.client="grix-connector",u.client_version=this.config.clientVersion,u.host_type=this.config.clientType,u.host_version=this.config.clientVersion),this.config.adapterHint&&(u.adapter_hint=this.config.adapterHint),u.host_meta={hostname:w(),platform:k.platform(),arch:k.arch(),os_release:k.release(),...e!==void 0&&{tailnet_ip:e},...t!==void 0&&t>0&&{file_server_port:t},...s!==void 0&&s>0&&{file_server_https_port:s}},this.config.concurrency&&(u.concurrency=this.config.concurrency),this.sendPacket("auth",u,d)}),c.on("message",u=>{if(this.ws!==c)return;let l;try{l=JSON.parse(R(u.toString()))}catch{return}try{this.handlePacket(l)}catch(_){this.emitClientError(new Error(`handlePacket error: ${_}`))}}),c.on("close",(u,l)=>{if(this.ws!==c)return;this.connected=!1,this.stopHeartbeat(),this.rejectAllPendingRequests("websocket closed"),this.emit("close",u,l.toString());const _=u!==1e3&&this.everConnected&&!this.agentDeleted;a.info("aibot",`ws closed agent=${this.config.clientType}:${this.config.agentId} code=${u} reason=${l.toString()||"<none>"} everConnected=${this.everConnected} reconnecting=${this.reconnecting} agentDeleted=${this.agentDeleted} willReconnect=${_}`),_&&this.attemptReconnect()}),c.on("error",u=>{this.ws===c&&(this.emitClientError(u instanceof Error?u:new Error(String(u))),this.connected||h(u))})})}handlePacket(e){if(this.packetLog?.logInboundPacket(e.cmd,e.seq,e.payload),e.seq>0&&this.pendingRequests.has(e.seq)){const t=this.pendingRequests.get(e.seq);this.pendingRequests.delete(e.seq),clearTimeout(t.timer),t.expected.includes(e.cmd)?t.resolve(e):t.reject(new Error(`unexpected response: got ${e.cmd}, expected ${t.expected.join("/")}`));return}switch(e.cmd){case"auth_ack":break;case"ping":{this.sendPacket("pong",e.payload??{});break}case"event_msg":{this.emit("event",e.payload);break}case"local_action":{this.emit("localAction",e.payload);break}case"event_stop":{this.emit("stop",e.payload);break}case"event_revoke":{this.emit("revoke",e.payload);break}case"event_edit":{this.emit("edit",e.payload);break}case"event_cancel":{this.emit("eventCancel",e.payload);break}case"queue_clear":{this.emit("queueClear",e.payload);break}case"queue_reorder":{this.emit("queueReorder",e.payload);break}case"queue_snapshot_query":{this.emit("queueSnapshotQuery",e.payload);break}case"event_hold":{this.emit("eventHold",e.payload);break}case"queue_edit":{this.emit("queueEdit",e.payload);break}case"control_share_set":{this.emit("shareSet",e.payload);break}case"agent_profile_push":{this.emit("profilePush",e.payload);break}case"skill_sync":{this.emit("skillSync",e.payload);break}case"kicked":{const t=e.payload;if(this.emit("kicked",t),this.connected=!1,this.stopHeartbeat(),this.rejectAllPendingRequests("kicked"),this.outboundBuffer.length=0,t?.reason===y){if(this.agentDeleted=!0,this.reconnecting=!1,this.ws){try{this.ws.close(4001,"kicked")}catch{}this.ws=null}a.error("aibot",`kicked: agent deleted on platform agent=${this.config.clientType}:${this.config.agentId}, reconnect disabled`),this.emit("agentDeleted",{source:"kicked",reason:t.reason});break}if(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,s=[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}${s?` ${s}`:""}`));break}case"agent_invoke_result":{this.handleInvokeResult(e.payload);break}case"mcp_frame":{const t=e.payload;this.emit("mcpFrame",t.session_id??"",t.frame??null);break}case"send_ack":break;case"send_nack":{const t=e.payload;if(t.code===4003&&e.seq>0){const s=this.seqEventMap.get(e.seq);s&&(this.seqEventMap.delete(e.seq),this.purgeBufferedStreamChunks(s),a.warn("aibot",`stream chunk rejected (4003), purging buffered chunks for event=${s}`),this.emit("streamRejected",s,t.code))}break}case"local_action_ack":break;default:break}}sendEventAck(e){this.sendPacket("event_ack",e)||a.warn("aibot",`event_ack NOT sent (ws not open) event=${e.event_id} ws=${this.ws?`state=${this.ws.readyState}`:"null"}`)}sendStreamChunk(e){!e.delta_content&&!e.is_finish&&(a.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)||a.warn("aibot",`send_msg NOT sent (ws not open) event=${e.event_id??""} session=${e.session_id??""} ws=${this.ws?`state=${this.ws.readyState}`:"null"}`)}editMsg(e){this.sendPacket("edit_msg",e)}sendEventResult(e){if(!this.ws||this.ws.readyState!==m.OPEN){this.sendPacket("event_result",e);return}this.sendEventResultReliable(e)}sendLocalActionResult(e){this.sendPacket("local_action_result",e)}sendEventStopAck(e){this.sendPacket("event_stop_ack",e)}sendEventStopResult(e){this.sendPacket("event_stop_result",e)}sendSessionActivitySet(e){this.sendPacket("session_activity_set",e)}sendCodexEvent(e){this.sendPacket("codex_event",e)}sendUpdateBindingCard(e){this.sendPacket("update_binding_card",e)}sendSkillsUpdate(e){this.sendPacket("agent_skills_update",e)}sendPing(){this.sendPacket("ping",{})}sendEventState(e){this.sendPacket("event_state",e)}sendEventCancelResult(e){this.sendPacket("event_cancel_result",e)}sendQueueClearResult(e){this.sendPacket("queue_clear_result",e)}sendQueueReorderResult(e){this.sendPacket("queue_reorder_result",e)}sendEventHoldResult(e){this.sendPacket("event_hold_result",e)}sendQueueEditResult(e){this.sendPacket("queue_edit_result",e)}sendQueueSnapshot(e){this.sendPacket("queue_snapshot",e)}agentInvoke(e,t,s=15e3){return new Promise((i,n)=>{const h=b(),c=Math.max(1e3,Math.min(s,6e4)),o=setTimeout(()=>{this.pendingInvokes.delete(h),n(new Error(`agent_invoke timeout: ${e}`))},c);this.pendingInvokes.set(h,{resolve:i,reject:n,timer:o}),this.sendPacket("agent_invoke",{invoke_id:h,action:e,params:t,timeout_ms:c})})}sendMcpFrame(e,t){this.sendPacket("mcp_frame",{session_id:e,frame:t})}request(e,t,s){return new Promise((i,n)=>{const h=++this.seq,c=setTimeout(()=>{this.pendingRequests.delete(h),n(new A(`request timeout: ${e} (expected ${s.expected.join("/")})`))},s.timeoutMs);this.pendingRequests.set(h,{expected:s.expected,resolve:i,reject:n,timer:c}),this.sendPacket(e,t,h)||(this.pendingRequests.delete(h),clearTimeout(c),n(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,s=2e4){return this.request("delete_msg",{session_id:e,msg_id:t},{expected:["send_ack","send_nack","error"],timeoutMs:s})}async sendEventResultRequest(e,t=5e3){return this.request("event_result",e,{expected:["send_ack","send_nack","error"],timeoutMs:t})}disconnect(){a.info("aibot",`disconnect() agent=${this.config.clientType}:${this.config.agentId} wasConnected=${this.connected} reconnecting=${this.reconnecting} reconnectAttempts=${this.reconnectAttempts}`),this.connected=!1,this.everConnected=!1,this.reconnecting=!1,this.reconnectAttempts=0,this.stopHeartbeat(),this.rejectAllPendingInvokes("disconnect"),this.rejectAllPendingRequests("disconnect"),this.outboundBuffer.length=0,this.ws&&(this.ws.close(1e3,"client disconnect"),this.ws=null)}static MAX_CONSECUTIVE_AUTH_FAILURES=5;async attemptReconnect(){if(this.reconnecting||this.agentDeleted)return;this.reconnecting=!0,a.info("aibot",`attemptReconnect start agent=${this.config.clientType}:${this.config.agentId} fromAttempts=${this.reconnectAttempts}`),this.emit("disconnected");let e=0;for(;this.reconnecting;){const t=Math.min(1e3*2**this.reconnectAttempts,3e4),s=Math.floor(t*.2*Math.random());if(this.reconnectAttempts++,await new Promise(i=>setTimeout(i,t+s)),!this.reconnecting)return;try{await this.connect();const i=this.reconnectAttempts;this.reconnectAttempts=0,this.reconnecting=!1,a.info("aibot",`reconnect succeeded agent=${this.config.clientType}:${this.config.agentId} attempt=${i}`);return}catch(i){if(this.ws){try{this.ws.close()}catch{}this.ws=null}const n=i instanceof Error?i.message:String(i);if(a.warn("aibot",`reconnect failed agent=${this.config.clientType}:${this.config.agentId} attempt=${this.reconnectAttempts} err=${n}`),i instanceof v){this.agentDeleted=!0,this.reconnecting=!1,a.error("aibot",`reconnect aborted: agent deleted on platform agent=${this.config.clientType}:${this.config.agentId}`),this.emit("agentDeleted",{source:"auth_ack",code:p});return}if(/Auth failed/i.test(n)){if(e++,e>=f.MAX_CONSECUTIVE_AUTH_FAILURES){this.reconnecting=!1,a.error("aibot",`reconnect giving up after ${e} consecutive auth failures agent=${this.config.clientType}:${this.config.agentId}`);return}}else e=0}}}sendPacket(e,t,s){if(this.ws&&this.ws.readyState===m.OPEN){const i=this.ws.bufferedAmount>f.BACKPRESSURE_THRESHOLD;if(!i||!f.DROPPABLE_COMMANDS.has(e)){if(i&&f.DROPPABLE_COMMANDS.has(e))return!1;const n=s??++this.seq;if(e==="client_stream_chunk"&&t&&typeof t=="object"){const c=t.event_id;if(c&&(this.seqEventMap.set(n,c),this.seqEventMap.size>200)){const o=this.seqEventMap.keys().next().value;o!==void 0&&this.seqEventMap.delete(o)}}const h={cmd:e,seq:n,payload:t};this.packetLog?.logOutboundPacket(e,n,t,"sent");try{const c=this.ws.readyState,o=this.ws.bufferedAmount;return this.ws.send(JSON.stringify(h),d=>{if(e==="event_result"){const r=t;d?a.warn("aibot",`event_result ws send callback failed event=${r.event_id??""} status=${r.status??""} seq=${n} readyState=${c} bufferedAmount=${o} err=${d.message}`):a.info("aibot",`event_result ws send callback ok event=${r.event_id??""} status=${r.status??""} seq=${n} readyState=${c} bufferedAmount=${o}`)}else if(e==="client_stream_chunk"){const r=t;d?a.warn("aibot",`stream_chunk ws send failed event=${r.event_id??""} session=${r.session_id??""} seq=${n} chunk_seq=${r.chunk_seq??""} is_finish=${r.is_finish??""} readyState=${c} bufferedAmount=${o} err=${d.message}`):a.info("aibot",`stream_chunk ws send ok event=${r.event_id??""} session=${r.session_id??""} seq=${n} chunk_seq=${r.chunk_seq??""} is_finish=${r.is_finish??""} client_msg_id=${r.client_msg_id??""} quoted_message_id=${r.quoted_message_id??""} readyState=${c} bufferedAmount=${o}`)}else if(e==="event_ack"){const r=t;d?a.warn("aibot",`event_ack ws send failed event=${r.event_id??""} seq=${n} readyState=${c} bufferedAmount=${o} err=${d.message}`):a.info("aibot",`event_ack ws send ok event=${r.event_id??""} seq=${n} readyState=${c} bufferedAmount=${o}`)}else if(e==="send_msg"){const r=t;d?a.warn("aibot",`send_msg ws send failed event=${r.event_id??""} session=${r.session_id??""} seq=${n} readyState=${c} bufferedAmount=${o} err=${d.message}`):a.info("aibot",`send_msg ws send ok event=${r.event_id??""} session=${r.session_id??""} seq=${n} readyState=${c} bufferedAmount=${o}`)}else if(d){const r=t;a.warn("aibot",`${e} ws send failed seq=${n} session=${r.session_id??""} event=${r.event_id??""} client_msg_id=${r.client_msg_id??""} readyState=${c} bufferedAmount=${o} err=${d.message}`)}}),!0}catch(c){return this.emitClientError(new Error(`sendPacket failed: ${c}`)),!1}}}if(f.DROPPABLE_COMMANDS.has(e))return this.packetLog?.logOutboundPacket(e,s??0,t,"dropped"),!1;if(s!==void 0)return this.packetLog?.logOutboundPacket(e,s,t,"dropped"),!1;if(this.outboundBuffer.length>=f.MAX_OUTBOUND_BUFFER_SIZE&&(this.outboundBuffer=this.outboundBuffer.filter(i=>f.BUFFER_OVERFLOW_RETAIN_COMMANDS.has(i.cmd)),this.outboundBuffer.length>=f.MAX_OUTBOUND_BUFFER_SIZE&&this.outboundBuffer.shift()),this.outboundBuffer.push({cmd:e,payload:t}),this.packetLog?.logOutboundPacket(e,s??0,t,"buffered"),e==="client_stream_chunk"){const i=t;a.info("aibot",`stream_chunk buffered (ws not open) event=${i.event_id??""} session=${i.session_id??""} chunk_seq=${i.chunk_seq??""} is_finish=${i.is_finish??""} ws=${this.ws?`state=${this.ws.readyState}`:"null"}`)}return!1}async sendEventResultReliable(e){const t=this.ackPolicy?.max_retries??3,s=this.ackPolicy?.push_ack_timeout_ms??5e3,i=750;for(let n=1;n<=t;n++){const h=this.ws?.readyState??-1,c=this.ws?.bufferedAmount??0;a.info("aibot",`event_result send attempt event=${e.event_id} status=${e.status} attempt=${n}/${t} readyState=${h} bufferedAmount=${c}`);try{const o=await this.sendEventResultRequest(e,s);if(o.cmd==="send_ack"){const r=o.payload;a.info("aibot",`event_result ack event=${e.event_id} status=${e.status} attempt=${n}/${t} ack_event=${r.event_id??""} ack_status=${r.status??""}`);return}const d=o.payload;if(a.warn("aibot",`event_result rejected event=${e.event_id} status=${e.status} attempt=${n}/${t} cmd=${o.cmd} code=${d.code??""} msg=${d.msg??""}${d.ref_cmd?` ref_cmd=${d.ref_cmd}`:""}${d.ref_id?` ref_id=${d.ref_id}`:""}`),d.code===4003){a.warn("aibot",`event_result stopping retries: 4003 ownership denied event=${e.event_id}`);return}return}catch(o){const d=o instanceof Error?o.message:String(o);if(a.warn("aibot",`event_result attempt failed event=${e.event_id} status=${e.status} attempt=${n}/${t} err=${d}`),n===t){this.emitClientError(new Error(`event_result ack failed after ${t} attempts: event=${e.event_id} status=${e.status}`));return}await new Promise(r=>setTimeout(r,i*n))}}}purgeBufferedStreamChunks(e){const t=this.outboundBuffer.length;this.outboundBuffer=this.outboundBuffer.filter(s=>s.cmd!=="client_stream_chunk"?!0:s.payload?.event_id!==e),this.outboundBuffer.length<t&&a.info("aibot",`purged ${t-this.outboundBuffer.length} buffered stream chunks for event=${e}`)}emitClientError(e){if(this.listenerCount("error")===0){a.warn("aibot",`Client error (no listeners): ${e.message}`);return}this.emit("error",e)}flushOutboundBuffer(){if(this.outboundBuffer.length===0||!this.ws||this.ws.readyState!==m.OPEN)return;const e=this.outboundBuffer;this.outboundBuffer=[];for(const{cmd:t,payload:s}of e){const i=++this.seq;if(t==="client_stream_chunk"&&s&&typeof s=="object"){const h=s.event_id;h&&this.seqEventMap.set(i,h)}const n={cmd:t,seq:i,payload:s};try{this.ws.send(JSON.stringify(n))}catch{break}}if(this.seqEventMap.size>200){const t=[...this.seqEventMap.entries()].sort((s,i)=>s[0]-i[0]);this.seqEventMap.clear();for(const[s,i]of t.slice(-100))this.seqEventMap.set(s,i)}}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{}}startHeartbeat(){this.stopHeartbeat(),this.heartbeatFailures=0,this.heartbeatTimer=setInterval(()=>{this.connected&&this.request("ping",{ts:Date.now()},{expected:["pong"],timeoutMs:5e3}).then(()=>{this.heartbeatFailures=0}).catch(()=>{this.connected&&(this.heartbeatFailures++,!(this.heartbeatFailures<f.HEARTBEAT_MAX_FAILURES)&&(this.cleanupSocket(),this.attemptReconnect()))})},this.heartbeatSec*1e3)}stopHeartbeat(){this.heartbeatTimer&&(clearInterval(this.heartbeatTimer),this.heartbeatTimer=null)}}export{f as AibotClient};
@@ -1 +1 @@
1
- import{readdir as r,stat as m}from"node:fs/promises";import{join as l,extname as d}from"node:path";const x={pdf:"application/pdf",doc:"application/msword",docx:"application/vnd.openxmlformats-officedocument.wordprocessingml.document",xls:"application/vnd.ms-excel",xlsx:"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",ppt:"application/vnd.ms-powerpoint",pptx:"application/vnd.openxmlformats-officedocument.presentationml.presentation",txt:"text/plain",md:"text/markdown",csv:"text/csv",json:"application/json",xml:"application/xml",yaml:"text/yaml",yml:"text/yaml",html:"text/html",css:"text/css",js:"text/javascript",ts:"text/typescript",zip:"application/zip",rar:"application/x-rar-compressed","7z":"application/x-7z-compressed",tar:"application/x-tar",gz:"application/gzip",jpg:"image/jpeg",jpeg:"image/jpeg",png:"image/png",gif:"image/gif",webp:"image/webp",svg:"image/svg+xml",mp4:"video/mp4",mov:"video/quicktime",avi:"video/x-msvideo",mkv:"video/x-matroska",webm:"video/webm",mp3:"audio/mpeg",wav:"audio/wav",flac:"audio/flac",aac:"audio/aac"};function n(a){const p=d(a).slice(1).toLowerCase();return x[p]}async function f(a,p=!1){const c=await r(a,{withFileTypes:!0}),s=[];for(const t of c){if(!p&&t.name.startsWith("."))continue;const i=l(a,t.name),e={id:i,name:t.name,is_directory:t.isDirectory()};try{if(t.isDirectory()){const o=await m(i);e.modified_at=o.mtime.toISOString()}else{const o=await m(i);e.size=o.size,e.modified_at=o.mtime.toISOString(),e.mime_type=n(t.name)}}catch{}s.push(e)}return s.sort((t,i)=>t.is_directory!==i.is_directory?t.is_directory?-1:1:t.name.localeCompare(i.name)),s}export{f as listFiles,n as resolveMimeType};
1
+ import{readdir as r,stat as m}from"node:fs/promises";import{join as l,extname as d}from"node:path";const x={pdf:"application/pdf",doc:"application/msword",docx:"application/vnd.openxmlformats-officedocument.wordprocessingml.document",xls:"application/vnd.ms-excel",xlsx:"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",ppt:"application/vnd.ms-powerpoint",pptx:"application/vnd.openxmlformats-officedocument.presentationml.presentation",txt:"text/plain",md:"text/markdown",csv:"text/csv",json:"application/json",xml:"application/xml",yaml:"text/yaml",yml:"text/yaml",html:"text/html",css:"text/css",js:"text/javascript",ts:"text/typescript",zip:"application/zip",rar:"application/x-rar-compressed","7z":"application/x-7z-compressed",tar:"application/x-tar",gz:"application/gzip",jpg:"image/jpeg",jpeg:"image/jpeg",png:"image/png",gif:"image/gif",webp:"image/webp",svg:"image/svg+xml",mp4:"video/mp4",mov:"video/quicktime",avi:"video/x-msvideo",mkv:"video/x-matroska",webm:"video/webm",mp3:"audio/mpeg",wav:"audio/wav",flac:"audio/flac",aac:"audio/aac"};function n(a){const p=d(a).slice(1).toLowerCase();return x[p]}async function f(a,p=!1){const c=await r(a,{withFileTypes:!0}),s=[];for(const i of c){if(!p&&i.name.startsWith("."))continue;const t=l(a,i.name),e={id:t,name:i.name,is_directory:i.isDirectory()};try{if(i.isDirectory()){const o=await m(t);e.modified_at=o.mtime.toISOString()}else{const o=await m(t);e.size=o.size,e.modified_at=o.mtime.toISOString(),e.mime_type=n(i.name)}}catch{}s.push(e)}return s.sort((i,t)=>i.is_directory!==t.is_directory?i.is_directory?-1:1:i.name.localeCompare(t.name)),s}export{f as listFiles,n as resolveMimeType};
@@ -0,0 +1 @@
1
+ import{join as o}from"node:path";function i(n,t){const{home:e}=t,l=t.cwd?.trim()||void 0,r=t.env??process.env;switch(n){case"claude":return{globalRoot:o(e,".claude","skills"),projectRoot:l?o(l,".claude","skills"):null};case"codex":{const s=r.CODEX_HOME?.trim()||o(e,".codex");return{globalRoot:o(s,"skills"),projectRoot:l?o(l,".codex","skills"):null}}case"gemini":return{globalRoot:o(e,".gemini","skills"),projectRoot:l?o(l,".gemini","skills"):null};case"qwen":return{globalRoot:o(e,".qwen","skills"),projectRoot:l?o(l,".qwen","skills"):null};case"pi":return{globalRoot:o(e,".pi","agent","skills"),projectRoot:l?o(l,".pi","skills"):null};case"kiro":return{globalRoot:o(e,".kiro","skills"),projectRoot:l?o(l,".kiro","skills"):null};case"cursor":return{globalRoot:o(e,".cursor","skills"),projectRoot:l?o(l,".cursor","skills"):null};case"codewhale":return{globalRoot:o(e,".codewhale","skills"),projectRoot:l?o(l,".codewhale","skills"):null};case"opencode":{const s=r.XDG_CONFIG_HOME?.trim()||o(e,".config");return{globalRoot:o(s,"opencode","skills"),projectRoot:null}}case"reasonix":return{globalRoot:o(e,".reasonix","skills"),projectRoot:null};case"kimi":{const s=r.KIMI_CODE_HOME?.trim()||o(e,".kimi-code");return{globalRoot:o(s,"skills"),projectRoot:null}}default:return{globalRoot:null,projectRoot:null}}}export{i as resolveEnableRoots};
@@ -0,0 +1 @@
1
+ import{readFileSync as l}from"node:fs";import{join as m}from"node:path";import{parseSkillFrontmatter as d}from"../../adapter/claude/skill-scanner.js";import{readSkillManifestSync as a}from"./manifest.js";import{resolveEnableRoots as f}from"./enable-roots.js";import{computeEnableScope as c,isSystemSkillEntry as p}from"./skill-enable.js";function u(t,o){try{const r=l(m(t,o,"SKILL.md"),"utf8");return d(r).description??""}catch{return""}}function R(t){const o=a(t.skillsDir),r=f(t.mode,{home:t.home,cwd:t.cwd,env:t.env}),n=[];for(const[i,e]of Object.entries(o.skills)){const s=p(e);n.push({name:i,description:u(t.skillsDir,e.dir),digest:e.digest,dir:e.dir,owner_id:e.owner_id,system:s,enable_scopes:{global:c({name:i,targetRoot:r.globalRoot,system:s,sourceDigest:e.digest}),project:c({name:i,targetRoot:r.projectRoot,system:s,sourceDigest:e.digest})}})}return n}export{R as buildLibrarySkills};
@@ -0,0 +1 @@
1
+ import{readFile as s,writeFile as l}from"node:fs/promises";import{readFileSync as a}from"node:fs";import{join as n}from"node:path";const i=".grix-sync.json";function e(r){try{const t=JSON.parse(r);if(t&&typeof t=="object"&&t.skills)return t}catch{}return null}async function u(r){try{const t=await s(n(r,i),"utf8");return e(t)??{skills:{}}}catch{return{skills:{}}}}function p(r){try{const t=a(n(r,i),"utf8");return e(t)??{skills:{}}}catch{return{skills:{}}}}async function y(r,t){await l(n(r,i),JSON.stringify(t,null,2),"utf8")}export{i as MANIFEST_FILE,u as readSkillManifest,p as readSkillManifestSync,y as writeSkillManifest};
@@ -0,0 +1 @@
1
+ import{existsSync as m,lstatSync as w,readFileSync as D,realpathSync as $}from"node:fs";import{mkdir as I,readFile as N,realpath as k,rm as R,rmdir as M,symlink as A,unlink as C}from"node:fs/promises";import{dirname as O,join as a}from"node:path";import{parseSkillFrontmatter as K,MANAGED_MARKER as h}from"../../adapter/claude/skill-scanner.js";import{computeContentDigest as y}from"./sync-state.js";import{readSkillManifest as x}from"./manifest.js";import{resolveEnableRoots as F}from"./enable-roots.js";class o extends Error{code;constructor(t,r){super(t),this.code=r}}function p(e){return e.trim().toLowerCase().startsWith("grix-")}function j(e){return e?e.system===!0||e.owner_id==="0":!1}function P(e){return e instanceof Error?e.message:String(e)}function T(e){try{const t=D(a(e,"SKILL.md"),"utf8");return y(t)}catch{return null}}function B(e){if(!e.targetRoot)return"unavailable";if(p(e.name)||e.system===!0)return"blocked";const t=a(e.targetRoot,e.name);let r;try{r=w(t)}catch{return"none"}if(r.isSymbolicLink()){let n;try{n=$(t)}catch{return"broken"}return m(a(n,h))?"blocked":"link"}if(r.isDirectory()){if(m(a(t,h)))return"blocked";const n=T(t);return n===null?"conflict":n===e.sourceDigest?"unmanaged":"conflict"}return"conflict"}async function G(e,t){try{const[r,n]=await Promise.all([k(e),k(t)]);return r===n}catch{return!1}}async function U(e,t){await I(O(t),{recursive:!0}),await A(e,t,process.platform==="win32"?"junction":"dir")}async function S(e){process.platform==="win32"?await M(e):await C(e)}const g=new Map;function b(e,t){return`${e}:${t}`}function E(e,t){const n=(g.get(e)??Promise.resolve()).catch(()=>{}).then(t);return g.set(e,n.catch(()=>{})),n}function V(e,t){const r=e.skills[t];if(!r)throw new o(`skill "${t}" not found in library`,"SKILL_NOT_FOUND");return r}async function q(e,t,r){const n=a(e,r.dir);let s;try{s=await N(a(n,"SKILL.md"),"utf8")}catch(c){throw new o(`failed to read library skill "${t}": ${P(c)}`,"SKILL_FILE_MISSING")}const i=K(s);if(i.name!==t)throw new o(`frontmatter name "${i.name}" does not match library skill name "${t}"`,"NAME_MISMATCH");return{sourceDir:n,digest:y(s)}}function L(e,t){const r=F(e.mode,{home:e.home,cwd:e.cwd,env:e.env}),n=t==="global"?r.globalRoot:r.projectRoot;if(!n)throw new o(t==="project"?"project scope unavailable: no session working directory is bound":"skill enable is not supported for this agent mode","SCOPE_UNAVAILABLE");return n}function v(e,t){if(!e.trim())throw new o("name is required","MISSING_SKILL_NAME");if(t!=="global"&&t!=="project")throw new o(`invalid scope: ${t}`,"INVALID_SCOPE")}async function Z(e,t){const r=t.name.trim();v(r,t.scope);const n=t.scope;return E(b(n,r),async()=>{const s=await x(e.skillsDir),i=V(s,r),{sourceDir:c,digest:_}=await q(e.skillsDir,r,i);if(p(r))throw new o(`"${r}" uses the reserved "grix-" prefix and cannot be enabled`,"BLOCKED");const f=j(i);if(f)throw new o(`"${r}" is a platform system skill and cannot be enabled`,"BLOCKED");const d=L(e,n),u=a(d,r),l=B({name:r,targetRoot:d,system:f,sourceDigest:_});if(l==="blocked")throw new o(`"${r}" target slot is managed by the connector`,"BLOCKED");if(l==="conflict")throw new o(`"${r}" already exists locally with different content and cannot be overwritten`,"CONFLICT");if(l==="unmanaged"&&t.force!=="replace_with_link")throw new o(`"${r}" already exists locally as a real directory; pass force="replace_with_link" to overwrite`,"NEEDS_FORCE");if(l==="link"){if(await G(u,c))return{name:r,scope:n,status:"link",path:u,changed:!1};if(t.force!=="replace_link")throw new o(`"${r}" is already linked elsewhere; pass force="replace_link" to overwrite`,"NEEDS_FORCE")}return l==="broken"||l==="link"?await S(u):l==="unmanaged"&&await R(u,{recursive:!0,force:!0}),await U(c,u),{name:r,scope:n,status:"link",path:u,changed:!0}})}async function ee(e,t){const r=t.name.trim();v(r,t.scope);const n=t.scope;return E(b(n,r),async()=>{const s=L(e,n),i=a(s,r);let c;try{c=w(i)}catch{return{name:r,scope:n,path:i,removed:!1}}if(!c.isSymbolicLink())throw new o(`"${r}" at ${i} is a real directory, not a connector-managed link, and cannot be disabled`,"NOT_MANAGED");return await S(i),{name:r,scope:n,path:i,removed:!0}})}export{o as SkillEnableError,B as computeEnableScope,ee as disableSkill,Z as enableSkill,j as isSystemSkillEntry};
@@ -1 +1 @@
1
- import{mkdir as d,readFile as $,writeFile as y,rm as p}from"node:fs/promises";import{join as u}from"node:path";import{createHash as v}from"node:crypto";import{log as a}from"../log/index.js";const S=6e4,m=".grix-sync.json",w=15e3;class _{configs;skillsDir;timer=null;running=!1;rerunPending=!1;stopped=!1;fetchImpl;intervalMs;constructor(t,i,n={}){this.configs=t,this.skillsDir=i,this.intervalMs=n.intervalMs??S,this.fetchImpl=n.fetchImpl??fetch}async start(){this.stopped=!1,await this.syncOnce().catch(t=>a.warn("skill-sync",`initial sync failed: ${o(t)}`)),this.timer=setInterval(()=>{this.syncOnce().catch(t=>a.warn("skill-sync",`sync failed: ${o(t)}`))},this.intervalMs),this.timer.unref?.()}stop(){this.stopped=!0,this.rerunPending=!1,this.timer&&(clearInterval(this.timer),this.timer=null)}triggerSync(){if(this.running){this.rerunPending=!0;return}this.syncOnce().catch(t=>a.warn("skill-sync",`triggered sync failed: ${o(t)}`))}async syncOnce(){if(!this.running){this.running=!0;try{const t=await this.pickReachable();if(!t)return;const{cfg:i,remote:n}=t;await d(this.skillsDir,{recursive:!0});const e=await this.readManifest(),c=new Set(n.map(s=>s.name));for(const s of n){const l=e.skills[s.name];if(l&&l.digest===s.digest)continue;const h=I(s.name);if(!h){a.warn("skill-sync",`skip skill with unsafe name: ${s.name}`);continue}const f=await this.fetchContent(i,s.id);if(f!==null)try{await d(u(this.skillsDir,h),{recursive:!0}),await y(u(this.skillsDir,h,"SKILL.md"),f,"utf8"),e.skills[s.name]={id:s.id,version:s.version,digest:s.digest,dir:h},l&&l.dir!==h&&!this.dirSharedByOthers(e,s.name,l.dir)&&await p(u(this.skillsDir,l.dir),{recursive:!0,force:!0}).catch(()=>{})}catch(g){a.warn("skill-sync",`write skill "${s.name}" failed: ${o(g)}`)}}for(const s of Object.keys(e.skills)){if(c.has(s))continue;const l=e.skills[s];this.dirSharedByOthers(e,s,l.dir)||await p(u(this.skillsDir,l.dir),{recursive:!0,force:!0}).catch(()=>{}),delete e.skills[s]}await this.writeManifest(e)}finally{this.running=!1,this.rerunPending&&(this.rerunPending=!1,(this.timer!==null||!this.stopped)&&this.syncOnce().catch(t=>a.warn("skill-sync",`rerun sync failed: ${o(t)}`)))}}}dirSharedByOthers(t,i,n){return Object.entries(t.skills).some(([e,c])=>e!==i&&c.dir===n)}async pickReachable(){for(const t of this.configs){if(!t.apiKey||!t.wsUrl)continue;const i=await this.fetchList(t);if(i!==null)return{cfg:t,remote:i}}return null}async fetchList(t){const i=`${k(t.wsUrl)}/v1/agent-api/skills`;try{const n=await this.fetchImpl(i,{headers:{Authorization:`Bearer ${t.apiKey}`},signal:AbortSignal.timeout(w)});if(!n.ok)return a.warn("skill-sync",`list returned ${n.status}`),null;const e=await n.json();return e.code!==0?null:e.data?.items??[]}catch(n){return a.warn("skill-sync",`list error: ${o(n)}`),null}}async fetchContent(t,i){const n=`${k(t.wsUrl)}/v1/agent-api/skills/${encodeURIComponent(i)}/content`;try{const e=await this.fetchImpl(n,{headers:{Authorization:`Bearer ${t.apiKey}`},signal:AbortSignal.timeout(w)});if(!e.ok)return a.warn("skill-sync",`content ${i} returned ${e.status}`),null;const c=await e.json();return c.code!==0?null:typeof c.data?.content=="string"?c.data.content:null}catch(e){return a.warn("skill-sync",`content ${i} error: ${o(e)}`),null}}async readManifest(){try{const t=await $(u(this.skillsDir,m),"utf8"),i=JSON.parse(t);if(i&&typeof i=="object"&&i.skills)return i}catch{}return{skills:{}}}async writeManifest(t){await y(u(this.skillsDir,m),JSON.stringify(t,null,2),"utf8")}}function k(r){return new URL(r.replace(/^wss:/,"https:").replace(/^ws:/,"http:")).origin}function I(r){const t=r.trim().replace(/[/\\]/g,"_").replace(/[<>:"|?*\u0000-\u001f]/g,"_").replace(/^\.+/,"").replace(/[. ]+$/,"");return!t||t.includes("..")||/^(con|prn|aux|nul|com[1-9]|lpt[1-9])$/i.test(t)?null:t!==r?`${t}-${v("sha1").update(r).digest("hex").slice(0,8)}`:t}function o(r){return r instanceof Error?r.message:String(r)}export{_ as SkillSyncer};
1
+ import{mkdir as y,writeFile as g,rm as m}from"node:fs/promises";import{join as h}from"node:path";import{createHash as S}from"node:crypto";import{log as r}from"../log/index.js";import{readSkillManifest as $,writeSkillManifest as v}from"./manifest.js";const _=6e4,p=15e3;class L{configs;skillsDir;timer=null;running=!1;rerunPending=!1;stopped=!1;fetchImpl;intervalMs;onSyncSuccessHandler=null;constructor(t,s,n={}){this.configs=t,this.skillsDir=s,this.intervalMs=n.intervalMs??_,this.fetchImpl=n.fetchImpl??fetch}async start(){this.stopped=!1,await this.syncOnce().catch(t=>r.warn("skill-sync",`initial sync failed: ${c(t)}`)),this.timer=setInterval(()=>{this.syncOnce().catch(t=>r.warn("skill-sync",`sync failed: ${c(t)}`))},this.intervalMs),this.timer.unref?.()}stop(){this.stopped=!0,this.rerunPending=!1,this.timer&&(clearInterval(this.timer),this.timer=null)}setOnSyncSuccess(t){this.onSyncSuccessHandler=t}triggerSync(){if(this.running){this.rerunPending=!0;return}this.syncOnce().catch(t=>r.warn("skill-sync",`triggered sync failed: ${c(t)}`))}async syncOnce(){if(!this.running){this.running=!0;try{const t=await this.pickReachable();if(!t)return;const{cfg:s,remote:n}=t;await y(this.skillsDir,{recursive:!0});const e=await $(this.skillsDir),o=new Set(n.map(i=>i.name));for(const i of n){const a=e.skills[i.name],f=i.owner_id==="0";if(a&&a.digest===i.digest){e.skills[i.name]={...a,id:i.id,version:i.version,owner_id:i.owner_id,system:f};continue}const u=O(i.name);if(!u){r.warn("skill-sync",`skip skill with unsafe name: ${i.name}`);continue}const d=await this.fetchContent(s,i.id);if(d!==null)try{await y(h(this.skillsDir,u),{recursive:!0}),await g(h(this.skillsDir,u,"SKILL.md"),d,"utf8"),e.skills[i.name]={id:i.id,version:i.version,digest:i.digest,dir:u,owner_id:i.owner_id,system:f},a&&a.dir!==u&&!this.dirSharedByOthers(e,i.name,a.dir)&&await m(h(this.skillsDir,a.dir),{recursive:!0,force:!0}).catch(()=>{})}catch(k){r.warn("skill-sync",`write skill "${i.name}" failed: ${c(k)}`)}}for(const i of Object.keys(e.skills)){if(o.has(i))continue;const a=e.skills[i];this.dirSharedByOthers(e,i,a.dir)||await m(h(this.skillsDir,a.dir),{recursive:!0,force:!0}).catch(()=>{}),delete e.skills[i]}await v(this.skillsDir,e);try{this.onSyncSuccessHandler?.()}catch(i){r.warn("skill-sync",`onSyncSuccess handler failed: ${c(i)}`)}}finally{this.running=!1,this.rerunPending&&(this.rerunPending=!1,(this.timer!==null||!this.stopped)&&this.syncOnce().catch(t=>r.warn("skill-sync",`rerun sync failed: ${c(t)}`)))}}}dirSharedByOthers(t,s,n){return Object.entries(t.skills).some(([e,o])=>e!==s&&o.dir===n)}async pickReachable(){for(const t of this.configs){if(!t.apiKey||!t.wsUrl)continue;const s=await this.fetchList(t);if(s!==null)return{cfg:t,remote:s}}return null}async fetchList(t){const s=`${w(t.wsUrl)}/v1/agent-api/skills`;try{const n=await this.fetchImpl(s,{headers:{Authorization:`Bearer ${t.apiKey}`},signal:AbortSignal.timeout(p)});if(!n.ok)return r.warn("skill-sync",`list returned ${n.status}`),null;const e=await n.json();return e.code!==0?null:e.data?.items??[]}catch(n){return r.warn("skill-sync",`list error: ${c(n)}`),null}}async fetchContent(t,s){const n=`${w(t.wsUrl)}/v1/agent-api/skills/${encodeURIComponent(s)}/content`;try{const e=await this.fetchImpl(n,{headers:{Authorization:`Bearer ${t.apiKey}`},signal:AbortSignal.timeout(p)});if(!e.ok)return r.warn("skill-sync",`content ${s} returned ${e.status}`),null;const o=await e.json();return o.code!==0?null:typeof o.data?.content=="string"?o.data.content:null}catch(e){return r.warn("skill-sync",`content ${s} error: ${c(e)}`),null}}}function w(l){return new URL(l.replace(/^wss:/,"https:").replace(/^ws:/,"http:")).origin}function O(l){const t=l.trim().replace(/[/\\]/g,"_").replace(/[<>:"|?*\u0000-\u001f]/g,"_").replace(/^\.+/,"").replace(/[. ]+$/,"");return!t||t.includes("..")||/^(con|prn|aux|nul|com[1-9]|lpt[1-9])$/i.test(t)?null:t!==l?`${t}-${S("sha1").update(l).digest("hex").slice(0,8)}`:t}function c(l){return l instanceof Error?l.message:String(l)}export{L as SkillSyncer};
package/dist/log.js CHANGED
@@ -1,3 +1,3 @@
1
- import{createWriteStream as g,mkdirSync as l,existsSync as f}from"node:fs";import{join as t}from"node:path";import{homedir as m}from"node:os";const i=t(m(),".grix"),s={base:i,config:t(i,"config"),log:t(i,"log"),data:t(i,"data")};function S(){for(const o of Object.values(s))f(o)||l(o,{recursive:!0})}let a=null;function $(){const o=new Date().toISOString().slice(0,10),r=t(s.log,`grix-acp-${o}.log`);a=g(r,{flags:"a"})}function c(){return new Date().toISOString().slice(11,19)}const u={info(o,r,...n){const e=`${c()} [${o}] ${r}${n.length?" "+n.map(String).join(" "):""}`;console.log(e),a?.write(e+`
2
- `)},error(o,r,...n){const e=`${c()} [${o}] ERROR ${r}${n.length?" "+n.map(String).join(" "):""}`;console.error(e),a?.write(e+`
1
+ import{createWriteStream as g,mkdirSync as l,existsSync as f}from"node:fs";import{join as i}from"node:path";import{homedir as m}from"node:os";const n=i(m(),".grix"),s={base:n,config:i(n,"config"),log:i(n,"log"),data:i(n,"data")};function S(){for(const o of Object.values(s))f(o)||l(o,{recursive:!0})}let a=null;function $(){const o=new Date().toISOString().slice(0,10),r=i(s.log,`grix-acp-${o}.log`);a=g(r,{flags:"a"})}function c(){return new Date().toISOString().slice(11,19)}const u={info(o,r,...t){const e=`${c()} [${o}] ${r}${t.length?" "+t.map(String).join(" "):""}`;console.log(e),a?.write(e+`
2
+ `)},error(o,r,...t){const e=`${c()} [${o}] ERROR ${r}${t.length?" "+t.map(String).join(" "):""}`;console.error(e),a?.write(e+`
3
3
  `)}};export{s as GRIX_PATHS,S as ensureGrixDirs,$ as initLogger,u as log};
package/dist/manager.js CHANGED
@@ -1,2 +1,2 @@
1
- import{readFileSync as z,readdirSync as J,writeFileSync as V}from"node:fs";import{join as v}from"node:path";import{AgentInstance as T}from"./bridge/bridge.js";import{buildSharedInstanceConfig as X,diffSharedOwners as Y,sharedInstanceKey as Z,SharedOwnersCache as ee}from"./manager-share-config.js";import{isAgentDeletedError as P}from"./core/aibot/errors.js";import{GRIX_PATHS as w,log as l}from"./core/log/index.js";import{resolveClientVersion as te}from"./core/util/client-version.js";import{UpgradeChecker as ne}from"./core/upgrade/upgrade-checker.js";import{SkillSyncer as ae}from"./core/skill-sync/skill-syncer.js";import{AgentGlobalConfigStore as re}from"./core/persistence/agent-global-config-store.js";import{resolveSkillScanMode as se}from"./adapter/claude/skill-scanner.js";import{buildWireSkills as oe}from"./core/skill-sync/sync-state.js";import{buildReportedSkills as ie,logDefaultSkillsCheck as ce,cleanupProjectedSkills as O}from"./default-skills/index.js";import{supportsDirectProviderConfig as le}from"./core/config/provider-env.js";import{resolveCopilotCommand as q}from"./core/runtime/copilot-resolve.js";import{resolveCursorCommand as H}from"./core/runtime/cursor-resolve.js";import{getCliVersion as de,resolveCliPath as pe}from"./core/util/cli-probe.js";import{AgentInstaller as me}from"./core/installer/installer.js";import{reportInstallFailure as ue}from"./core/observability/sentry.js";const ge=8e3;function he(){const t=q();return[{clientType:"openclaw",command:"openclaw"},{clientType:"claude",command:"claude"},{clientType:"codex",command:"codex"},{clientType:"qwen",command:"qwen"},{clientType:"hermes",command:"hermes"},{clientType:"reasonix",command:"reasonix"},{clientType:"codewhale",command:"codewhale"},{clientType:"opencode",command:"opencode"},{clientType:"cursor",command:H()},{clientType:"pi",command:"pi"},{clientType:"openhuman",command:"openhuman-core"},{clientType:"kiro",command:"kiro-cli"},{clientType:"kimi",command:"kimi"},{clientType:"copilot",command:t.command},{clientType:"agy",command:"agy"}]}async function fe(){return Promise.all(he().map(async t=>{const e=await pe(t.command);if(!e)return{client_type:t.clientType,command:t.command,installed:!1,path:null,version:null};const n=await de(e,t.versionArgs??["--version"]);return{client_type:t.clientType,command:t.command,installed:!0,path:e,version:n.version,error:n.error}}))}function _e(t){switch(t){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:H()};case"reasonix":return{adapterType:"acp",command:"reasonix",args:["acp"],enableSessionBinding:!0};case"codewhale":return{adapterType:"codewhale",command:"codewhale",enableSessionBinding:!0};case"openhuman":return{adapterType:"openhuman",command:"openhuman-core",enableSessionBinding:!0};case"kiro":return{adapterType:"acp",command:"kiro-cli",args:["acp"],enableSessionBinding:!0};case"kimi":return{adapterType:"acp",command:"kimi",args:["acp"],options:{initial_mode:"yolo"},enableSessionBinding:!0};case"opencode":return{adapterType:"opencode",command:"opencode",args:["serve"],enableSessionBinding:!0};case"copilot":{const e=q();return{adapterType:"acp",command:e.command,args:[...e.prefixArgs,"--acp"],enableSessionBinding:!0}}case"agy":return{adapterType:"agy",command:"agy",enableSessionBinding:!0};case"hermes":return{adapterType:"acp",command:"hermes",args:["acp"],enableSessionBinding:!0};default:throw new Error(`Unsupported client_type: ${t}`)}}function N(t,e){return String(t??"").trim().toLowerCase().replace(/[^a-z0-9._-]+/g,"-").replace(/-+/g,"-").replace(/^-|-$/g,"")||String(e??"").trim().toLowerCase().replace(/[^a-z0-9._-]+/g,"-").replace(/-+/g,"-").replace(/^-|-$/g,"")||"default"}function ye(t,e){return v(w.data,`session-bindings-${N(t,e)}.json`)}function we(t,e){return v(w.data,`active-events-${N(t,e)}.json`)}function Se(...t){const e=[],n=new Set;for(const r of t)for(const s of r??[]){const i=String(s??"").trim(),o=i.toLowerCase();!i||n.has(o)||(n.add(o),e.push(i))}return e.length>0?e:void 0}function ve(t,e){const n={claude:{maxConcurrent:1,maxQueued:5,queueTimeoutMs:0},codex:{maxConcurrent:1,maxQueued:5,queueTimeoutMs:0},cursor:{maxConcurrent:1,maxQueued:3,queueTimeoutMs:0},acp:{maxConcurrent:1,maxQueued:3,queueTimeoutMs:0},pi:{maxConcurrent:1,maxQueued:5,queueTimeoutMs:0},codewhale:{maxConcurrent:1,maxQueued:3,queueTimeoutMs:0},openhuman:{maxConcurrent:1,maxQueued:3,queueTimeoutMs:0},opencode:{maxConcurrent:1,maxQueued:3,queueTimeoutMs:0},agy:{maxConcurrent:1,maxQueued:3,queueTimeoutMs:0}},r=n[t]??n.acp;return{maxConcurrent:e?.max_concurrent??r.maxConcurrent??1,maxQueued:e?.max_queued??r.maxQueued??3,queueTimeoutMs:e?.queue_timeout_ms??r.queueTimeoutMs??0,cancelableQueued:!0,cancelableRunning:!0}}function b(t,e={}){const n=te(),r=String(t.client_type??"").trim().toLowerCase(),s=_e(r),i=String(t.ws_url??"").trim(),o="get_session_usage",p="get_rate_limits",c="get_agent_global_config";if(!t.name?.trim())throw new Error("agent name is required");if(!i)throw new Error(`agent ${t.name}: ws_url is required`);if(!t.agent_id?.trim())throw new Error(`agent ${t.name}: agent_id is required`);if(!t.api_key?.trim())throw new Error(`agent ${t.name}: api_key is required`);const a=s.adapterType,u=a==="acp",m=r==="qwen",_={...s.options??{}},S=a==="codex"?{capabilities:["local_action_v1","agent_invoke"],localActions:["session_control","get_context","set_model","set_mode","set_reasoning_effort","set_service_tier","set_sandbox_mode","exec_approve","exec_reject","file_list","create_folder","turn_interrupt","permission_approve","permission_reject","thread_compact",o,p]}:null,d=a==="claude"?{localActions:["session_control","set_mode","set_model","claude_interaction_reply","exec_approve","exec_reject","file_list","create_folder","thread_compact",o,p]}:null,f=m?{capabilities:["stream_chunk","local_action_v1"],localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","file_list","create_folder",o],adapterHint:"qwen/base"}:null,h=a==="pi"?{adapterHint:"pi/base",capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","get_context","file_list","create_folder",o]}:null,g=a==="openhuman"?{adapterHint:"openhuman/base",capabilities:["local_action_v1"],localActions:["session_control","set_model","file_list","create_folder"]}:null,y=a==="cursor"?{adapterHint:"cursor/base",capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","set_mode","get_context","file_list","create_folder","thread_compact","claude_interaction_reply",p]}:null,A=a==="codewhale"?{capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","file_list","create_folder",o]}:null,C=a==="opencode"?{adapterHint:"opencode/base",capabilities:["stream_chunk","local_action_v1"],localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","file_list","create_folder"]}:null,$=a==="agy"?{adapterHint:"agy/base",capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","file_list","create_folder"]}:null,I=new Set(["hermes","reasonix","kimi","copilot"]),R=u&&!I.has(r),B=u&&!m?{localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","file_list","create_folder",...R?[o]:[]]}:null,F=r==="kiro"?{localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","file_list","create_folder","thread_compact",o,p]}:null,L=a==="codex"||a==="claude"||r==="gemini"?["session_control","set_model","set_mode"]:void 0,Q=a==="cursor"||a==="opencode"||a==="openhuman"||a==="agy"||u&&I.has(r)?[p]:[o,p];u&&_.raw_transport===void 0&&(_.raw_transport=r==="gemini");const W=`${r}/base`,E=se(a,r),K=E==="kiro"?void 0:process.cwd(),M=ie({mode:E,projectDir:K}),j=M.filter(x=>x.source==="connector").map(x=>x.name);j.length>0&&!e.quiet&&l.info("manager",`[${t.name}] injecting connector skills: [${j.join(", ")}]`);const D=oe(M,w.skills);let G=D.length>0?D:void 0;return{name:t.name,adapterType:a,aibot:{url:i,agentId:t.agent_id,apiKey:t.api_key,clientType:r,clientVersion:n,adapterHint:s.adapterHint??f?.adapterHint??h?.adapterHint??g?.adapterHint??y?.adapterHint??C?.adapterHint??$?.adapterHint??W,capabilities:S?.capabilities??A?.capabilities??h?.capabilities??g?.capabilities??y?.capabilities??C?.capabilities??$?.capabilities??f?.capabilities??["stream_chunk","local_action_v1","connector_upgrade"],localActions:Se(S?.localActions??A?.localActions??d?.localActions??h?.localActions??g?.localActions??y?.localActions??C?.localActions??$?.localActions??f?.localActions??F?.localActions??B?.localActions??["exec_approve","exec_reject"],L,Q,["connector_rollback","connector_upgrade_push",c,"configure_gateway_provider","skill_upload"]),skills:G},agent:{command:s.command,args:[...s.args??[],...t.args??[]],env:t.env,clientType:r,...t.provider?{provider:{...t.provider.provider_id?.trim()?{providerId:t.provider.provider_id.trim()}:{},...t.provider.base_url?.trim()?{baseUrl:t.provider.base_url.trim()}:{},...t.provider.quota_base_url?.trim()?{quotaBaseUrl:t.provider.quota_base_url.trim()}:{},...t.provider.api_key?.trim()?{apiKey:t.provider.api_key.trim()}:{},...t.provider.model?.trim()?{model:t.provider.model.trim()}:{}}}:{}},adapterOptions:_,acpAuthMethod:_.auth_method,acpInitialMode:_.initial_mode,acpMcpTools:_.acp_mcp_tools,promptTimeoutMs:t.prompt_timeout_ms,bindingsPath:ye(t.name,t.agent_id),activeEventStorePath:we(t.name,t.agent_id),...s.enableSessionBinding||u?{enableSessionBinding:!0}:{},...s.autoInjectArgs||r==="reasonix"&&t.provider?.model?.trim()?{autoInjectArgs:{...s.autoInjectArgs??{},...r==="reasonix"&&t.provider?.model?.trim()?{model:t.provider.model.trim()}:{}}}:{},poolMaxSize:t.pool?.maxSize,poolIdleTimeoutMs:t.pool?.idleTimeoutMs,eventQueue:ve(a,t.event_queue),logDir:w.log,providerBaseUrl:t.provider_base_url?.trim()||void 0,providerApiKey:t.provider_api_key?.trim()||void 0}}function Ae(){const t=process.env.GRIX_AGENT_STARTUP_WAIT_MS,e=Number(t);return Number.isFinite(e)&&e>=500?Math.floor(e):ge}function U(t){const e=[],n=[],r=[],s=J(t).filter(i=>i.endsWith(".json")).sort();for(const i of s)try{const o=JSON.parse(z(v(t,i),"utf-8"));if(o===null||typeof o!="object"||Array.isArray(o)||!("agents"in o)){r.push(i);continue}if(!Array.isArray(o.agents)){n.push({file:i,error:"agents is not an array"});continue}if(o.agents.length===0){n.push({file:i,error:"agents array is empty"});continue}for(const p of o.agents)e.push({entry:p,file:i})}catch(o){n.push({file:i,error:o instanceof Error?o.message:String(o)})}return{entries:e,fileErrors:n,skipped:r}}function k(t){if(t===null||typeof t!="object")return JSON.stringify(t)??"null";if(Array.isArray(t))return`[${t.map(k).join(",")}]`;const e=t;return`{${Object.keys(e).sort().map(r=>`${JSON.stringify(r)}:${k(e[r])}`).join(",")}}`}function be(t,e){return k(t)===k(e)}class We{instances=[];configMap=new Map;rawConfigMap=new Map;reloadChain=Promise.resolve();sharedInstances=new Map;shareSyncChains=new Map;stopping=!1;deletedAgentCleanups=new Set;upgradeChecker=null;skillSyncer=null;globalConfigStore;configDir=w.config;sharedOwnersCache=new ee(w.data);installer=new me;async start(e){const n=e??w.config;this.configDir=n,l.info("manager",`Loading configs from ${n}`),ce(),O(),this.globalConfigStore=new re(v(w.data,"agent-global-configs.json")),this.globalConfigStore.load();const{entries:r,fileErrors:s,skipped:i}=U(n);for(const a of s)l.error("manager",`Failed to load ${a.file}: ${a.error}`);for(const a of i)l.info("manager",`Skipped ${a}: not an agent config (no "agents" key)`);if(r.length===0&&s.length===0&&i.length===0)throw new Error(`No config files found in ${n}`);const o=[];let p=s.length;for(const{entry:a,file:u}of r)try{const m=b(a);o.push({config:m,entry:a,file:u}),l.info("manager",`Loaded ${m.name} (${m.adapterType??"acp"}) from ${u}`)}catch(m){const _=typeof a?.name=="string"?a.name:"<unknown>";l.error("manager",`Invalid agent config in ${u} (name=${_}): ${m}`),p++}let c=0;if(o.length>0){const a=Ae();l.info("manager",`Starting ${o.length} agent(s), startup wait=${a}ms`);const u=()=>this.upgradeChecker?.triggerCheck(),m=d=>{this.instances=this.instances.filter(f=>f!==d)},_=o.map(({config:d,entry:f,file:h})=>{const g=new T(d,this.globalConfigStore);return g.setUpgradeTrigger(u),g.setShareSetHandler(y=>this.onShareSet(d,y)),g.setAgentDeletedHandler(()=>this.onAgentDeletedByPlatform(d.name)),g.setSkillSyncHandler(()=>this.skillSyncer?.triggerSync()),this.instances.push(g),this.configMap.set(d.name,d),this.rawConfigMap.set(d.name,{entry:f,file:h}),{config:d,instance:g,startPromise:g.start()}}),S=await Promise.all(_.map(async d=>{const f=await new Promise(h=>{let g=!1;const y=setTimeout(()=>{g||(g=!0,h({kind:"timeout"}))},a);d.startPromise.then(()=>{g||(g=!0,clearTimeout(y),h({kind:"started"}))}).catch(A=>{g||(g=!0,clearTimeout(y),h({kind:"failed",error:A}))})});return{task:d,outcome:f}}));for(const{task:d,outcome:f}of S){if(f.kind==="started"){this.restoreCachedSharedInstances(d.config);continue}if(f.kind==="failed"){if(P(f.error)){this.onAgentDeletedByPlatform(d.config.name);continue}m(d.instance),l.error("manager",`Failed to start ${d.config.name}: ${f.error}`);continue}c++,l.warn("manager",`Startup pending for ${d.config.name}, continue retrying in background`),d.startPromise.then(()=>{l.info("manager",`Delayed start succeeded: ${d.config.name}`),this.restoreCachedSharedInstances(d.config)}).catch(h=>{if(P(h)){this.onAgentDeletedByPlatform(d.config.name);return}m(d.instance),l.error("manager",`Delayed start failed: ${d.config.name}: ${h}`)})}if(this.instances.length>0){const d=Math.max(0,this.instances.length-c);l.info("manager",`${d}/${o.length} agent(s) running now`)}c>0&&l.warn("manager",`${c} agent(s) still connecting in background`)}if(this.instances.length===0&&o.length>0)throw new Error("All agent configurations failed to start");o.length>0&&(this.upgradeChecker=new ne(o.map(({config:a})=>({apiKey:a.aibot.apiKey,wsUrl:a.aibot.url})),()=>this.instances.some(a=>a.hasPendingOrBackgroundWork())||[...this.sharedInstances.values()].some(a=>a.hasPendingOrBackgroundWork())),await this.upgradeChecker.start()),o.length>0&&(this.skillSyncer=new ae(o.map(({config:a})=>({apiKey:a.aibot.apiKey,wsUrl:a.aibot.url})),w.skills),await this.skillSyncer.start())}async stop(){l.info("manager","Stopping all agents..."),this.stopping=!0,this.upgradeChecker?.stop(),this.skillSyncer?.stop(),await Promise.allSettled([...this.shareSyncChains.values()]),this.shareSyncChains.clear();const e=[...this.sharedInstances.values()];this.sharedInstances.clear(),await Promise.allSettled([...this.instances.map(n=>n.stop()),...e.map(n=>n.stop())]),await this.globalConfigStore?.flush(),this.instances=[],O(),l.info("manager","All stopped")}onShareSet(e,n){this.sharedOwnersCache.save(e.name,n);const s=(this.shareSyncChains.get(e.name)??Promise.resolve()).catch(()=>{}).then(()=>this.syncSharedInstances(e,n));this.shareSyncChains.set(e.name,s)}async syncSharedInstances(e,n){if(this.stopping)return;const{toAdd:r,toRemove:s}=Y(e.name,this.sharedInstances.keys(),n);for(const i of r){if(this.stopping)break;const o=Z(e.name,i);try{const p=X(e,i),c=new T(p,this.globalConfigStore);this.sharedInstances.set(o,c),await c.start(),l.info("manager",`shared instance started: ${o}`)}catch(p){this.sharedInstances.delete(o);const c=p instanceof Error?p.message:String(p);/Auth failed/i.test(c)?(this.sharedOwnersCache.remove(e.name,i),l.warn("manager",`shared instance auth rejected, removed from cache: ${o}`)):l.error("manager",`start shared instance failed ${o}: ${p}`)}}for(const{key:i}of s){const o=this.sharedInstances.get(i);o&&(this.sharedInstances.delete(i),o.stop().catch(p=>l.error("manager",`stop shared instance failed ${i}: ${p}`)),l.info("manager",`shared instance stopped: ${i}`))}}restoreCachedSharedInstances(e){const n=this.sharedOwnersCache.load(e.name);n.length!==0&&(l.info("manager",`restoring ${n.length} cached shared instance(s) for ${e.name}`),this.onShareSet(e,n))}getAgentsStatus(){return this.instances.map(e=>e.getStatus())}async addAgent(e){await this.addAgentInternal(e,"agents.json"),this.persistAgentsConfig(),l.info("manager",`Added agent: ${e.name}`)}async addAgentInternal(e,n){const r=b(e);if(this.instances.some(s=>s.name===r.name))throw new Error(`Agent "${r.name}" already exists`);await this.startInstanceFromConfig(r,e,n)}async startInstanceFromConfig(e,n,r){const s=new T(e,this.globalConfigStore);s.setUpgradeTrigger(()=>this.upgradeChecker?.triggerCheck()),s.setShareSetHandler(i=>this.onShareSet(e,i)),s.setAgentDeletedHandler(()=>this.onAgentDeletedByPlatform(e.name)),s.setSkillSyncHandler(()=>this.skillSyncer?.triggerSync()),s.setProviderConfigHandler(i=>this.setAgentProvider(e.name,i)),s.setRelayEnvSettledHandler(()=>this.relayEnvSettledHandler?.()),await s.start(),this.instances.push(s),this.configMap.set(e.name,e),this.rawConfigMap.set(e.name,{entry:n,file:r}),this.relayEnvSettledHandler?.()}async removeAgent(e){if(!this.instances.some(n=>n.name===e))throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});await this.removeAgentInternal(e),this.persistAgentsConfig(),l.info("manager",`Removed agent: ${e}`),this.relayEnvSettledHandler?.()}async removeAgentInternal(e,n){const r=this.instances.findIndex(p=>p.name===e),s=r===-1?void 0:this.instances[r];r!==-1&&this.instances.splice(r,1),this.configMap.delete(e),this.rawConfigMap.delete(e);const i=this.shareSyncChains.get(e);this.shareSyncChains.delete(e),i&&await i.catch(()=>{});const o=`${e}#shared:`;for(const[p,c]of[...this.sharedInstances.entries()])p.startsWith(o)&&(this.sharedInstances.delete(p),c.stop().catch(a=>l.error("manager",`stop shared instance failed ${p}: ${a}`)));n?.keepShareCache||this.sharedOwnersCache.delete(e),s&&await s.stop()}onAgentDeletedByPlatform(e){this.stopping||this.deletedAgentCleanups.has(e)||(this.deletedAgentCleanups.add(e),(async()=>{try{if(!(this.rawConfigMap.has(e)||this.instances.some(r=>r.name===e)))return;l.warn("manager",`Agent "${e}" \u5DF2\u5728\u5E73\u53F0\u5220\u9664\uFF0C\u505C\u6B62\u5B9E\u4F8B\u5E76\u6E05\u7406\u672C\u5730\u914D\u7F6E`),await this.removeAgentInternal(e),this.persistAgentsConfig(),l.info("manager",`Agent "${e}" \u5DF2\u5728\u5E73\u53F0\u5220\u9664\uFF0C\u672C\u5730\u914D\u7F6E\u5DF2\u6E05\u7406\uFF08agents.json \u5DF2\u79FB\u9664\u8BE5\u6761\u76EE\uFF09`)}catch(n){l.error("manager",`cleanup for deleted agent "${e}" failed: ${n}`)}finally{this.deletedAgentCleanups.delete(e)}})())}persistAgentsConfig(){const e=v(this.configDir,"agents.json");try{const n=[];for(const[,s]of this.configMap){const i=this.rawConfigMap.get(s.name)?.entry.provider;n.push({name:s.name,ws_url:s.aibot.url,agent_id:s.aibot.agentId,api_key:s.aibot.apiKey,client_type:s.aibot.clientType,...i?{provider:i}:{}})}V(e,JSON.stringify({agents:n},null,4)+`
2
- `,"utf-8")}catch(n){l.error("manager",`Failed to persist agents config: ${n}`)}}isAgentBusy(e){return this.instances.find(r=>r.name===e)?.hasPendingWork()??!1}getAgentsWithStaleRelayEnv(){return this.instances.filter(e=>e.hasStaleRelayEnv()).map(e=>e.name)}markAgentRelayEnvStale(e){const n=this.instances.find(r=>r.name===e);return n?(n.markRelayEnvStale(),!0):!1}relayEnvSettledHandler;setRelayEnvSettledHandler(e){this.relayEnvSettledHandler=e}async restartAgent(e){const n=this.rawConfigMap.get(e);if(!n)throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});await this.replaceInstance(e,n.entry,n.file),l.info("manager",`Restarted agent: ${e}`)}async setAgentProvider(e,n){const r=this.rawConfigMap.get(e);if(!r)throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});const s={...r.entry,provider:n};await this.replaceInstance(e,s,r.file),this.persistAgentsConfig(),l.info("manager",`Updated provider config for agent: ${e}`)}async clearDirectProviderConfigs(){const e=[...this.rawConfigMap.entries()].filter(([,r])=>!!r.entry.provider&&le(r.entry.client_type)).map(([r])=>r),n=[];for(const r of e)try{await this.setAgentProvider(r,void 0),n.push(r)}catch(s){l.warn("manager",`Failed to clear provider config for "${r}": ${s instanceof Error?s.message:String(s)}`)}return n}async replaceInstance(e,n,r){const s=b(n);await this.removeAgentInternal(e,{keepShareCache:!0}),await this.startInstanceFromConfig(s,n,r),this.restoreCachedSharedInstances(s)}async reload(){const e=this.reloadChain.catch(()=>{}).then(()=>this.doReload());return this.reloadChain=e.catch(()=>{}),e}async doReload(){if(this.stopping)throw Object.assign(new Error("manager is stopping"),{code:"RELOAD_UNSAFE"});const{entries:e,fileErrors:n,skipped:r}=U(this.configDir);for(const c of r)l.info("manager",`reload: skipped ${c}: not an agent config (no "agents" key)`);if(n.length>0){const c=n.map(a=>`${a.file}: ${a.error}`).join("; ");throw Object.assign(new Error(`reload aborted: ${n.length} config file(s) failed to parse [${c}]`),{code:"RELOAD_UNSAFE"})}if(e.length===0)throw Object.assign(new Error("reload aborted: no valid agent config found"),{code:"RELOAD_UNSAFE"});const s=[];for(const{entry:c}of e)try{b(c,{quiet:!0})}catch(a){const u=c?.name,m=typeof u=="string"&&u.trim()?u:"<unknown>";s.push({name:m,error:a instanceof Error?a.message:String(a)})}if(s.length>0){const c=s.map(a=>`${a.name}: ${a.error}`).join("; ");throw Object.assign(new Error(`reload aborted: ${s.length} agent config(s) invalid [${c}]`),{code:"RELOAD_UNSAFE"})}const i=new Map;for(const c of e){const a=String(c.entry?.name??"").trim();a&&(i.has(a)&&l.warn("manager",`reload: duplicate agent name "${a}", last one wins`),i.set(a,c))}const o={added:[],removed:[],restarted:[],unchanged:[],failed:[]},p=new Set(this.rawConfigMap.keys());for(const c of p)if(!i.has(c))try{await this.removeAgentInternal(c),o.removed.push(c)}catch(a){o.failed.push({name:c,error:a instanceof Error?a.message:String(a)})}for(const[c,a]of i){const u=this.rawConfigMap.get(c);if(u)if(be(u.entry,a.entry))this.rawConfigMap.set(c,a),o.unchanged.push(c);else try{await this.replaceInstance(c,a.entry,a.file),o.restarted.push(c)}catch(m){o.failed.push({name:c,error:m instanceof Error?m.message:String(m)})}else try{await this.addAgentInternal(a.entry,a.file),o.added.push(c)}catch(m){o.failed.push({name:c,error:m instanceof Error?m.message:String(m)})}}return l.info("manager",`reload done: +${o.added.length} -${o.removed.length} ~${o.restarted.length} =${o.unchanged.length} !${o.failed.length}`),o}async checkUpgrade(){return this.upgradeChecker?this.upgradeChecker.checkForUpdate():{available:!1}}triggerUpgrade(){this.upgradeChecker?.triggerCheck()}async probeAll(e={}){return ke(this.instances,e)}async probeOne(e,n={}){return Ce(this.instances,e,n)}listInstallable(){return this.installer.listInstallable()}async installAgent(e){const n=await this.installer.install(e);return ue(n),n}getInstallProgress(e){return this.installer.getProgress(e)??null}}async function ke(t,e){const n=e.concurrency??4,r=Date.now(),s=new Array(t.length);await new Promise(a=>{let u=0,m=0;const _=t.length;if(_===0){a();return}function S(h){const g=t[h];g.probe(e).then(y=>{s[h]=y,d()},y=>{s[h]={agent_name:g.name,client_type:"unknown",adapter_type:"acp",ok:!1,status:"error",probed_at:Date.now(),duration_ms:0,cached:!1,cli:{command:"",installed:!1,path:null,version:null,error:{code:"internal",message:y?.message??String(y)}},conversation:{attempted:!1,ok:!1,latency_ms:null},config:{model:null,base_url:null,source:{model:"unknown",base_url:"unknown"}},process:{started:!1,alive:!1,busy:!1}},d()})}function d(){m++,u<_?S(u++):m===_&&a()}const f=Math.min(n,_);for(let h=0;h<f;h++)S(u++)});const i=s.filter(a=>a.status==="healthy").length,o=s.filter(a=>a.status==="degraded").length,p=s.filter(a=>a.status==="unavailable").length,c=await fe();return{ok:i===s.length&&s.length>0,total:s.length,healthy:i,degraded:o,unavailable:p,installed_clients:c,agents:s,probed_at:r,duration_ms:Date.now()-r}}async function Ce(t,e,n){const r=t.find(s=>s.name===e);if(!r)throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});return r.probe(n)}export{We as Manager,fe as probeInstalledClientCommands,ke as probeInstances,Ce as probeOneInstance};
1
+ import{readFileSync as J,readdirSync as V,writeFileSync as X}from"node:fs";import{join as v}from"node:path";import{homedir as Y}from"node:os";import{AgentInstance as I}from"./bridge/bridge.js";import{buildSharedInstanceConfig as Z,diffSharedOwners as ee,sharedInstanceKey as te,SharedOwnersCache as ne}from"./manager-share-config.js";import{isAgentDeletedError as O}from"./core/aibot/errors.js";import{GRIX_PATHS as w,log as l}from"./core/log/index.js";import{resolveClientVersion as ae}from"./core/util/client-version.js";import{UpgradeChecker as re}from"./core/upgrade/upgrade-checker.js";import{SkillSyncer as se}from"./core/skill-sync/skill-syncer.js";import{AgentGlobalConfigStore as oe}from"./core/persistence/agent-global-config-store.js";import{resolveSkillScanMode as ie}from"./adapter/claude/skill-scanner.js";import{buildWireSkills as ce}from"./core/skill-sync/sync-state.js";import{buildLibrarySkills as le}from"./core/skill-sync/library-skills.js";import{buildReportedSkills as de,logDefaultSkillsCheck as pe,cleanupProjectedSkills as P}from"./default-skills/index.js";import{supportsDirectProviderConfig as me}from"./core/config/provider-env.js";import{resolveCopilotCommand as q}from"./core/runtime/copilot-resolve.js";import{resolveCursorCommand as H}from"./core/runtime/cursor-resolve.js";import{getCliVersion as ue,resolveCliPath as ge}from"./core/util/cli-probe.js";import{AgentInstaller as he}from"./core/installer/installer.js";import{reportInstallFailure as fe}from"./core/observability/sentry.js";const _e=8e3;function ye(){const n=q();return[{clientType:"openclaw",command:"openclaw"},{clientType:"claude",command:"claude"},{clientType:"codex",command:"codex"},{clientType:"qwen",command:"qwen"},{clientType:"hermes",command:"hermes"},{clientType:"reasonix",command:"reasonix"},{clientType:"codewhale",command:"codewhale"},{clientType:"opencode",command:"opencode"},{clientType:"cursor",command:H()},{clientType:"pi",command:"pi"},{clientType:"openhuman",command:"openhuman-core"},{clientType:"kiro",command:"kiro-cli"},{clientType:"kimi",command:"kimi"},{clientType:"copilot",command:n.command},{clientType:"agy",command:"agy"}]}async function we(){return Promise.all(ye().map(async n=>{const e=await ge(n.command);if(!e)return{client_type:n.clientType,command:n.command,installed:!1,path:null,version:null};const t=await ue(e,n.versionArgs??["--version"]);return{client_type:n.clientType,command:n.command,installed:!0,path:e,version:t.version,error:t.error}}))}function Se(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:H()};case"reasonix":return{adapterType:"acp",command:"reasonix",args:["acp"],enableSessionBinding:!0};case"codewhale":return{adapterType:"codewhale",command:"codewhale",enableSessionBinding:!0};case"openhuman":return{adapterType:"openhuman",command:"openhuman-core",enableSessionBinding:!0};case"kiro":return{adapterType:"acp",command:"kiro-cli",args:["acp"],enableSessionBinding:!0};case"kimi":return{adapterType:"acp",command:"kimi",args:["acp"],options:{initial_mode:"yolo"},enableSessionBinding:!0};case"opencode":return{adapterType:"opencode",command:"opencode",args:["serve"],enableSessionBinding:!0};case"copilot":{const e=q();return{adapterType:"acp",command:e.command,args:[...e.prefixArgs,"--acp"],enableSessionBinding:!0}}case"agy":return{adapterType:"agy",command:"agy",enableSessionBinding:!0};case"hermes":return{adapterType:"acp",command:"hermes",args:["acp"],enableSessionBinding:!0};default:throw new Error(`Unsupported client_type: ${n}`)}}function R(n,e){return String(n??"").trim().toLowerCase().replace(/[^a-z0-9._-]+/g,"-").replace(/-+/g,"-").replace(/^-|-$/g,"")||String(e??"").trim().toLowerCase().replace(/[^a-z0-9._-]+/g,"-").replace(/-+/g,"-").replace(/^-|-$/g,"")||"default"}function ve(n,e){return v(w.data,`session-bindings-${R(n,e)}.json`)}function Ae(n,e){return v(w.data,`active-events-${R(n,e)}.json`)}function ke(...n){const e=[],t=new Set;for(const r of n)for(const s of r??[]){const i=String(s??"").trim(),o=i.toLowerCase();!i||t.has(o)||(t.add(o),e.push(i))}return e.length>0?e:void 0}function be(n,e){const t={claude:{maxConcurrent:1,maxQueued:5,queueTimeoutMs:0},codex:{maxConcurrent:1,maxQueued:5,queueTimeoutMs:0},cursor:{maxConcurrent:1,maxQueued:3,queueTimeoutMs:0},acp:{maxConcurrent:1,maxQueued:3,queueTimeoutMs:0},pi:{maxConcurrent:1,maxQueued:5,queueTimeoutMs:0},codewhale:{maxConcurrent:1,maxQueued:3,queueTimeoutMs:0},openhuman:{maxConcurrent:1,maxQueued:3,queueTimeoutMs:0},opencode:{maxConcurrent:1,maxQueued:3,queueTimeoutMs:0},agy:{maxConcurrent:1,maxQueued:3,queueTimeoutMs:0}},r=t[n]??t.acp;return{maxConcurrent:e?.max_concurrent??r.maxConcurrent??1,maxQueued:e?.max_queued??r.maxQueued??3,queueTimeoutMs:e?.queue_timeout_ms??r.queueTimeoutMs??0,cancelableQueued:!0,cancelableRunning:!0}}function k(n,e={}){const t=ae(),r=String(n.client_type??"").trim().toLowerCase(),s=Se(r),i=String(n.ws_url??"").trim(),o="get_session_usage",p="get_rate_limits",c="get_agent_global_config";if(!n.name?.trim())throw new Error("agent name is required");if(!i)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 a=s.adapterType,u=a==="acp",m=r==="qwen",_={...s.options??{}},S=a==="codex"?{capabilities:["local_action_v1","agent_invoke"],localActions:["session_control","get_context","set_model","set_mode","set_reasoning_effort","set_service_tier","set_sandbox_mode","exec_approve","exec_reject","file_list","create_folder","turn_interrupt","permission_approve","permission_reject","thread_compact",o,p]}:null,d=a==="claude"?{localActions:["session_control","set_mode","set_model","claude_interaction_reply","exec_approve","exec_reject","file_list","create_folder","thread_compact",o,p]}:null,f=m?{capabilities:["stream_chunk","local_action_v1"],localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","file_list","create_folder",o],adapterHint:"qwen/base"}:null,h=a==="pi"?{adapterHint:"pi/base",capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","get_context","file_list","create_folder",o]}:null,g=a==="openhuman"?{adapterHint:"openhuman/base",capabilities:["local_action_v1"],localActions:["session_control","set_model","file_list","create_folder"]}:null,y=a==="cursor"?{adapterHint:"cursor/base",capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","set_mode","get_context","file_list","create_folder","thread_compact","claude_interaction_reply",p]}:null,A=a==="codewhale"?{capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","file_list","create_folder",o]}:null,C=a==="opencode"?{adapterHint:"opencode/base",capabilities:["stream_chunk","local_action_v1"],localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","file_list","create_folder"]}:null,$=a==="agy"?{adapterHint:"agy/base",capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","file_list","create_folder"]}:null,E=new Set(["hermes","reasonix","kimi","copilot"]),U=u&&!E.has(r),B=u&&!m?{localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","file_list","create_folder",...U?[o]:[]]}:null,F=r==="kiro"?{localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","file_list","create_folder","thread_compact",o,p]}:null,L=a==="codex"||a==="claude"||r==="gemini"?["session_control","set_model","set_mode"]:void 0,Q=a==="cursor"||a==="opencode"||a==="openhuman"||a==="agy"||u&&E.has(r)?[p]:[o,p];u&&_.raw_transport===void 0&&(_.raw_transport=r==="gemini");const W=`${r}/base`,x=ie(a,r),K=x==="kiro"?void 0:process.cwd(),M=de({mode:x,projectDir:K}),j=M.filter(T=>T.source==="connector").map(T=>T.name);j.length>0&&!e.quiet&&l.info("manager",`[${n.name}] injecting connector skills: [${j.join(", ")}]`);const D=ce(M,w.skills);let G=D.length>0?D:void 0;const z=le({skillsDir:w.skills,mode:x,home:Y()});return{name:n.name,adapterType:a,aibot:{url:i,agentId:n.agent_id,apiKey:n.api_key,clientType:r,clientVersion:t,adapterHint:s.adapterHint??f?.adapterHint??h?.adapterHint??g?.adapterHint??y?.adapterHint??C?.adapterHint??$?.adapterHint??W,capabilities:S?.capabilities??A?.capabilities??h?.capabilities??g?.capabilities??y?.capabilities??C?.capabilities??$?.capabilities??f?.capabilities??["stream_chunk","local_action_v1","connector_upgrade"],localActions:ke(S?.localActions??A?.localActions??d?.localActions??h?.localActions??g?.localActions??y?.localActions??C?.localActions??$?.localActions??f?.localActions??F?.localActions??B?.localActions??["exec_approve","exec_reject"],L,Q,["connector_rollback","connector_upgrade_push",c,"configure_gateway_provider","skill_upload","skill_enable","skill_disable"]),skills:G,librarySkills:z},agent:{command:s.command,args:[...s.args??[],...n.args??[]],env:n.env,clientType:r,...n.provider?{provider:{...n.provider.provider_id?.trim()?{providerId:n.provider.provider_id.trim()}:{},...n.provider.base_url?.trim()?{baseUrl:n.provider.base_url.trim()}:{},...n.provider.quota_base_url?.trim()?{quotaBaseUrl:n.provider.quota_base_url.trim()}:{},...n.provider.api_key?.trim()?{apiKey:n.provider.api_key.trim()}:{},...n.provider.model?.trim()?{model:n.provider.model.trim()}:{}}}:{}},adapterOptions:_,acpAuthMethod:_.auth_method,acpInitialMode:_.initial_mode,acpMcpTools:_.acp_mcp_tools,promptTimeoutMs:n.prompt_timeout_ms,bindingsPath:ve(n.name,n.agent_id),activeEventStorePath:Ae(n.name,n.agent_id),...s.enableSessionBinding||u?{enableSessionBinding:!0}:{},...s.autoInjectArgs||r==="reasonix"&&n.provider?.model?.trim()?{autoInjectArgs:{...s.autoInjectArgs??{},...r==="reasonix"&&n.provider?.model?.trim()?{model:n.provider.model.trim()}:{}}}:{},poolMaxSize:n.pool?.maxSize,poolIdleTimeoutMs:n.pool?.idleTimeoutMs,eventQueue:be(a,n.event_queue),logDir:w.log,providerBaseUrl:n.provider_base_url?.trim()||void 0,providerApiKey:n.provider_api_key?.trim()||void 0}}function Ce(){const n=process.env.GRIX_AGENT_STARTUP_WAIT_MS,e=Number(n);return Number.isFinite(e)&&e>=500?Math.floor(e):_e}function N(n){const e=[],t=[],r=[],s=V(n).filter(i=>i.endsWith(".json")).sort();for(const i of s)try{const o=JSON.parse(J(v(n,i),"utf-8"));if(o===null||typeof o!="object"||Array.isArray(o)||!("agents"in o)){r.push(i);continue}if(!Array.isArray(o.agents)){t.push({file:i,error:"agents is not an array"});continue}if(o.agents.length===0){t.push({file:i,error:"agents array is empty"});continue}for(const p of o.agents)e.push({entry:p,file:i})}catch(o){t.push({file:i,error:o instanceof Error?o.message:String(o)})}return{entries:e,fileErrors:t,skipped:r}}function b(n){if(n===null||typeof n!="object")return JSON.stringify(n)??"null";if(Array.isArray(n))return`[${n.map(b).join(",")}]`;const e=n;return`{${Object.keys(e).sort().map(r=>`${JSON.stringify(r)}:${b(e[r])}`).join(",")}}`}function $e(n,e){return b(n)===b(e)}class Ve{instances=[];configMap=new Map;rawConfigMap=new Map;reloadChain=Promise.resolve();sharedInstances=new Map;shareSyncChains=new Map;stopping=!1;deletedAgentCleanups=new Set;upgradeChecker=null;skillSyncer=null;globalConfigStore;configDir=w.config;sharedOwnersCache=new ne(w.data);installer=new he;async start(e){const t=e??w.config;this.configDir=t,l.info("manager",`Loading configs from ${t}`),pe(),P(),this.globalConfigStore=new oe(v(w.data,"agent-global-configs.json")),this.globalConfigStore.load();const{entries:r,fileErrors:s,skipped:i}=N(t);for(const a of s)l.error("manager",`Failed to load ${a.file}: ${a.error}`);for(const a of i)l.info("manager",`Skipped ${a}: not an agent config (no "agents" key)`);if(r.length===0&&s.length===0&&i.length===0)throw new Error(`No config files found in ${t}`);const o=[];let p=s.length;for(const{entry:a,file:u}of r)try{const m=k(a);o.push({config:m,entry:a,file:u}),l.info("manager",`Loaded ${m.name} (${m.adapterType??"acp"}) from ${u}`)}catch(m){const _=typeof a?.name=="string"?a.name:"<unknown>";l.error("manager",`Invalid agent config in ${u} (name=${_}): ${m}`),p++}let c=0;if(o.length>0){const a=Ce();l.info("manager",`Starting ${o.length} agent(s), startup wait=${a}ms`);const u=()=>this.upgradeChecker?.triggerCheck(),m=d=>{this.instances=this.instances.filter(f=>f!==d)},_=o.map(({config:d,entry:f,file:h})=>{const g=new I(d,this.globalConfigStore);return g.setUpgradeTrigger(u),g.setShareSetHandler(y=>this.onShareSet(d,y)),g.setAgentDeletedHandler(()=>this.onAgentDeletedByPlatform(d.name)),g.setSkillSyncHandler(()=>this.skillSyncer?.triggerSync()),this.instances.push(g),this.configMap.set(d.name,d),this.rawConfigMap.set(d.name,{entry:f,file:h}),{config:d,instance:g,startPromise:g.start()}}),S=await Promise.all(_.map(async d=>{const f=await new Promise(h=>{let g=!1;const y=setTimeout(()=>{g||(g=!0,h({kind:"timeout"}))},a);d.startPromise.then(()=>{g||(g=!0,clearTimeout(y),h({kind:"started"}))}).catch(A=>{g||(g=!0,clearTimeout(y),h({kind:"failed",error:A}))})});return{task:d,outcome:f}}));for(const{task:d,outcome:f}of S){if(f.kind==="started"){this.restoreCachedSharedInstances(d.config);continue}if(f.kind==="failed"){if(O(f.error)){this.onAgentDeletedByPlatform(d.config.name);continue}m(d.instance),l.error("manager",`Failed to start ${d.config.name}: ${f.error}`);continue}c++,l.warn("manager",`Startup pending for ${d.config.name}, continue retrying in background`),d.startPromise.then(()=>{l.info("manager",`Delayed start succeeded: ${d.config.name}`),this.restoreCachedSharedInstances(d.config)}).catch(h=>{if(O(h)){this.onAgentDeletedByPlatform(d.config.name);return}m(d.instance),l.error("manager",`Delayed start failed: ${d.config.name}: ${h}`)})}if(this.instances.length>0){const d=Math.max(0,this.instances.length-c);l.info("manager",`${d}/${o.length} agent(s) running now`)}c>0&&l.warn("manager",`${c} agent(s) still connecting in background`)}if(this.instances.length===0&&o.length>0)throw new Error("All agent configurations failed to start");o.length>0&&(this.upgradeChecker=new re(o.map(({config:a})=>({apiKey:a.aibot.apiKey,wsUrl:a.aibot.url})),()=>this.instances.some(a=>a.hasPendingOrBackgroundWork())||[...this.sharedInstances.values()].some(a=>a.hasPendingOrBackgroundWork())),await this.upgradeChecker.start()),o.length>0&&(this.skillSyncer=new se(o.map(({config:a})=>({apiKey:a.aibot.apiKey,wsUrl:a.aibot.url})),w.skills),this.skillSyncer.setOnSyncSuccess(()=>this.refreshAllInstanceSkills()),await this.skillSyncer.start())}refreshAllInstanceSkills(){for(const e of this.instances)try{e.forceRefreshSkills()}catch(t){l.warn("manager",`forceRefreshSkills failed for ${e.name}: ${t}`)}for(const e of this.sharedInstances.values())try{e.forceRefreshSkills()}catch(t){l.warn("manager",`forceRefreshSkills failed for shared instance: ${t}`)}}async stop(){l.info("manager","Stopping all agents..."),this.stopping=!0,this.upgradeChecker?.stop(),this.skillSyncer?.stop(),await Promise.allSettled([...this.shareSyncChains.values()]),this.shareSyncChains.clear();const e=[...this.sharedInstances.values()];this.sharedInstances.clear(),await Promise.allSettled([...this.instances.map(t=>t.stop()),...e.map(t=>t.stop())]),await this.globalConfigStore?.flush(),this.instances=[],P(),l.info("manager","All stopped")}onShareSet(e,t){this.sharedOwnersCache.save(e.name,t);const s=(this.shareSyncChains.get(e.name)??Promise.resolve()).catch(()=>{}).then(()=>this.syncSharedInstances(e,t));this.shareSyncChains.set(e.name,s)}async syncSharedInstances(e,t){if(this.stopping)return;const{toAdd:r,toRemove:s}=ee(e.name,this.sharedInstances.keys(),t);for(const i of r){if(this.stopping)break;const o=te(e.name,i);try{const p=Z(e,i),c=new I(p,this.globalConfigStore);this.sharedInstances.set(o,c),await c.start(),l.info("manager",`shared instance started: ${o}`)}catch(p){this.sharedInstances.delete(o);const c=p instanceof Error?p.message:String(p);/Auth failed/i.test(c)?(this.sharedOwnersCache.remove(e.name,i),l.warn("manager",`shared instance auth rejected, removed from cache: ${o}`)):l.error("manager",`start shared instance failed ${o}: ${p}`)}}for(const{key:i}of s){const o=this.sharedInstances.get(i);o&&(this.sharedInstances.delete(i),o.stop().catch(p=>l.error("manager",`stop shared instance failed ${i}: ${p}`)),l.info("manager",`shared instance stopped: ${i}`))}}restoreCachedSharedInstances(e){const t=this.sharedOwnersCache.load(e.name);t.length!==0&&(l.info("manager",`restoring ${t.length} cached shared instance(s) for ${e.name}`),this.onShareSet(e,t))}getAgentsStatus(){return this.instances.map(e=>e.getStatus())}async addAgent(e){await this.addAgentInternal(e,"agents.json"),this.persistAgentsConfig(),l.info("manager",`Added agent: ${e.name}`)}async addAgentInternal(e,t){const r=k(e);if(this.instances.some(s=>s.name===r.name))throw new Error(`Agent "${r.name}" already exists`);await this.startInstanceFromConfig(r,e,t)}async startInstanceFromConfig(e,t,r){const s=new I(e,this.globalConfigStore);s.setUpgradeTrigger(()=>this.upgradeChecker?.triggerCheck()),s.setShareSetHandler(i=>this.onShareSet(e,i)),s.setAgentDeletedHandler(()=>this.onAgentDeletedByPlatform(e.name)),s.setSkillSyncHandler(()=>this.skillSyncer?.triggerSync()),s.setProviderConfigHandler(i=>this.setAgentProvider(e.name,i)),s.setRelayEnvSettledHandler(()=>this.relayEnvSettledHandler?.()),await s.start(),this.instances.push(s),this.configMap.set(e.name,e),this.rawConfigMap.set(e.name,{entry:t,file:r}),this.relayEnvSettledHandler?.()}async removeAgent(e){if(!this.instances.some(t=>t.name===e))throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});await this.removeAgentInternal(e),this.persistAgentsConfig(),l.info("manager",`Removed agent: ${e}`),this.relayEnvSettledHandler?.()}async removeAgentInternal(e,t){const r=this.instances.findIndex(p=>p.name===e),s=r===-1?void 0:this.instances[r];r!==-1&&this.instances.splice(r,1),this.configMap.delete(e),this.rawConfigMap.delete(e);const i=this.shareSyncChains.get(e);this.shareSyncChains.delete(e),i&&await i.catch(()=>{});const o=`${e}#shared:`;for(const[p,c]of[...this.sharedInstances.entries()])p.startsWith(o)&&(this.sharedInstances.delete(p),c.stop().catch(a=>l.error("manager",`stop shared instance failed ${p}: ${a}`)));t?.keepShareCache||this.sharedOwnersCache.delete(e),s&&await s.stop()}onAgentDeletedByPlatform(e){this.stopping||this.deletedAgentCleanups.has(e)||(this.deletedAgentCleanups.add(e),(async()=>{try{if(!(this.rawConfigMap.has(e)||this.instances.some(r=>r.name===e)))return;l.warn("manager",`Agent "${e}" \u5DF2\u5728\u5E73\u53F0\u5220\u9664\uFF0C\u505C\u6B62\u5B9E\u4F8B\u5E76\u6E05\u7406\u672C\u5730\u914D\u7F6E`),await this.removeAgentInternal(e),this.persistAgentsConfig(),l.info("manager",`Agent "${e}" \u5DF2\u5728\u5E73\u53F0\u5220\u9664\uFF0C\u672C\u5730\u914D\u7F6E\u5DF2\u6E05\u7406\uFF08agents.json \u5DF2\u79FB\u9664\u8BE5\u6761\u76EE\uFF09`)}catch(t){l.error("manager",`cleanup for deleted agent "${e}" failed: ${t}`)}finally{this.deletedAgentCleanups.delete(e)}})())}persistAgentsConfig(){const e=v(this.configDir,"agents.json");try{const t=[];for(const[,s]of this.configMap){const i=this.rawConfigMap.get(s.name)?.entry.provider;t.push({name:s.name,ws_url:s.aibot.url,agent_id:s.aibot.agentId,api_key:s.aibot.apiKey,client_type:s.aibot.clientType,...i?{provider:i}:{}})}X(e,JSON.stringify({agents:t},null,4)+`
2
+ `,"utf-8")}catch(t){l.error("manager",`Failed to persist agents config: ${t}`)}}isAgentBusy(e){return this.instances.find(r=>r.name===e)?.hasPendingWork()??!1}getAgentsWithStaleRelayEnv(){return this.instances.filter(e=>e.hasStaleRelayEnv()).map(e=>e.name)}markAgentRelayEnvStale(e){const t=this.instances.find(r=>r.name===e);return t?(t.markRelayEnvStale(),!0):!1}relayEnvSettledHandler;setRelayEnvSettledHandler(e){this.relayEnvSettledHandler=e}async restartAgent(e){const t=this.rawConfigMap.get(e);if(!t)throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});await this.replaceInstance(e,t.entry,t.file),l.info("manager",`Restarted agent: ${e}`)}async setAgentProvider(e,t){const r=this.rawConfigMap.get(e);if(!r)throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});const s={...r.entry,provider:t};await this.replaceInstance(e,s,r.file),this.persistAgentsConfig(),l.info("manager",`Updated provider config for agent: ${e}`)}async clearDirectProviderConfigs(){const e=[...this.rawConfigMap.entries()].filter(([,r])=>!!r.entry.provider&&me(r.entry.client_type)).map(([r])=>r),t=[];for(const r of e)try{await this.setAgentProvider(r,void 0),t.push(r)}catch(s){l.warn("manager",`Failed to clear provider config for "${r}": ${s instanceof Error?s.message:String(s)}`)}return t}async replaceInstance(e,t,r){const s=k(t);await this.removeAgentInternal(e,{keepShareCache:!0}),await this.startInstanceFromConfig(s,t,r),this.restoreCachedSharedInstances(s)}async reload(){const e=this.reloadChain.catch(()=>{}).then(()=>this.doReload());return this.reloadChain=e.catch(()=>{}),e}async doReload(){if(this.stopping)throw Object.assign(new Error("manager is stopping"),{code:"RELOAD_UNSAFE"});const{entries:e,fileErrors:t,skipped:r}=N(this.configDir);for(const c of r)l.info("manager",`reload: skipped ${c}: not an agent config (no "agents" key)`);if(t.length>0){const c=t.map(a=>`${a.file}: ${a.error}`).join("; ");throw Object.assign(new Error(`reload aborted: ${t.length} config file(s) failed to parse [${c}]`),{code:"RELOAD_UNSAFE"})}if(e.length===0)throw Object.assign(new Error("reload aborted: no valid agent config found"),{code:"RELOAD_UNSAFE"});const s=[];for(const{entry:c}of e)try{k(c,{quiet:!0})}catch(a){const u=c?.name,m=typeof u=="string"&&u.trim()?u:"<unknown>";s.push({name:m,error:a instanceof Error?a.message:String(a)})}if(s.length>0){const c=s.map(a=>`${a.name}: ${a.error}`).join("; ");throw Object.assign(new Error(`reload aborted: ${s.length} agent config(s) invalid [${c}]`),{code:"RELOAD_UNSAFE"})}const i=new Map;for(const c of e){const a=String(c.entry?.name??"").trim();a&&(i.has(a)&&l.warn("manager",`reload: duplicate agent name "${a}", last one wins`),i.set(a,c))}const o={added:[],removed:[],restarted:[],unchanged:[],failed:[]},p=new Set(this.rawConfigMap.keys());for(const c of p)if(!i.has(c))try{await this.removeAgentInternal(c),o.removed.push(c)}catch(a){o.failed.push({name:c,error:a instanceof Error?a.message:String(a)})}for(const[c,a]of i){const u=this.rawConfigMap.get(c);if(u)if($e(u.entry,a.entry))this.rawConfigMap.set(c,a),o.unchanged.push(c);else try{await this.replaceInstance(c,a.entry,a.file),o.restarted.push(c)}catch(m){o.failed.push({name:c,error:m instanceof Error?m.message:String(m)})}else try{await this.addAgentInternal(a.entry,a.file),o.added.push(c)}catch(m){o.failed.push({name:c,error:m instanceof Error?m.message:String(m)})}}return l.info("manager",`reload done: +${o.added.length} -${o.removed.length} ~${o.restarted.length} =${o.unchanged.length} !${o.failed.length}`),o}async checkUpgrade(){return this.upgradeChecker?this.upgradeChecker.checkForUpdate():{available:!1}}triggerUpgrade(){this.upgradeChecker?.triggerCheck()}async probeAll(e={}){return xe(this.instances,e)}async probeOne(e,t={}){return Te(this.instances,e,t)}listInstallable(){return this.installer.listInstallable()}async installAgent(e){const t=await this.installer.install(e);return fe(t),t}getInstallProgress(e){return this.installer.getProgress(e)??null}}async function xe(n,e){const t=e.concurrency??4,r=Date.now(),s=new Array(n.length);await new Promise(a=>{let u=0,m=0;const _=n.length;if(_===0){a();return}function S(h){const g=n[h];g.probe(e).then(y=>{s[h]=y,d()},y=>{s[h]={agent_name:g.name,client_type:"unknown",adapter_type:"acp",ok:!1,status:"error",probed_at:Date.now(),duration_ms:0,cached:!1,cli:{command:"",installed:!1,path:null,version:null,error:{code:"internal",message:y?.message??String(y)}},conversation:{attempted:!1,ok:!1,latency_ms:null},config:{model:null,base_url:null,source:{model:"unknown",base_url:"unknown"}},process:{started:!1,alive:!1,busy:!1}},d()})}function d(){m++,u<_?S(u++):m===_&&a()}const f=Math.min(t,_);for(let h=0;h<f;h++)S(u++)});const i=s.filter(a=>a.status==="healthy").length,o=s.filter(a=>a.status==="degraded").length,p=s.filter(a=>a.status==="unavailable").length,c=await we();return{ok:i===s.length&&s.length>0,total:s.length,healthy:i,degraded:o,unavailable:p,installed_clients:c,agents:s,probed_at:r,duration_ms:Date.now()-r}}async function Te(n,e,t){const r=n.find(s=>s.name===e);if(!r)throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});return r.probe(t)}export{Ve as Manager,we as probeInstalledClientCommands,xe as probeInstances,Te as probeOneInstance};
@@ -1 +1 @@
1
- import*as i from"node:net";const e={bind:"127.0.0.1",port:0,endpoint:"/mcp",sessionTimeoutMs:18e5,invokeTimeoutMs:3e4};function n(o){const u={bind:o?.bind??e.bind,port:o?.port??e.port,endpoint:o?.endpoint??e.endpoint,sessionTimeoutMs:o?.sessionTimeoutMs??e.sessionTimeoutMs,invokeTimeoutMs:o?.invokeTimeoutMs??e.invokeTimeoutMs,allowedOrigins:o?.allowedOrigins,allowedHosts:o?.allowedHosts};return s(u.bind),u.port!==0&&t(u.port),r(u.sessionTimeoutMs),u}function s(o){if(!o||!i.isIPv4(o)&&!i.isIPv6(o))throw new Error(`\u914D\u7F6E\u6821\u9A8C\u5931\u8D25: bind \u5730\u5740 "${o}" \u4E0D\u662F\u5408\u6CD5\u7684 IPv4 \u6216 IPv6 \u5730\u5740`)}function t(o){if(!Number.isInteger(o)||o<1||o>65535)throw new Error(`\u914D\u7F6E\u6821\u9A8C\u5931\u8D25: port \u503C ${o} \u4E0D\u5728\u5408\u6CD5\u8303\u56F4 1-65535 \u5185\u6216\u4E0D\u662F\u6574\u6570`)}function r(o){if(!Number.isInteger(o)||o<1e3||o>864e5)throw new Error(`\u914D\u7F6E\u6821\u9A8C\u5931\u8D25: session_timeout_ms \u503C ${o} \u4E0D\u5728\u5408\u6CD5\u8303\u56F4 1000-86400000 \u5185`)}export{n as createDefaultGatewayConfig};
1
+ import*as n from"node:net";const i={bind:"127.0.0.1",port:0,endpoint:"/mcp",sessionTimeoutMs:18e5,invokeTimeoutMs:3e4};function s(u){const e={bind:u?.bind??i.bind,port:u?.port??i.port,endpoint:u?.endpoint??i.endpoint,sessionTimeoutMs:u?.sessionTimeoutMs??i.sessionTimeoutMs,invokeTimeoutMs:u?.invokeTimeoutMs??i.invokeTimeoutMs,allowedOrigins:u?.allowedOrigins,allowedHosts:u?.allowedHosts};return t(e.bind),e.port!==0&&o(e.port),r(e.sessionTimeoutMs),e}function t(u){if(!u||!n.isIPv4(u)&&!n.isIPv6(u))throw new Error(`\u914D\u7F6E\u6821\u9A8C\u5931\u8D25: bind \u5730\u5740 "${u}" \u4E0D\u662F\u5408\u6CD5\u7684 IPv4 \u6216 IPv6 \u5730\u5740`)}function o(u){if(!Number.isInteger(u)||u<1||u>65535)throw new Error(`\u914D\u7F6E\u6821\u9A8C\u5931\u8D25: port \u503C ${u} \u4E0D\u5728\u5408\u6CD5\u8303\u56F4 1-65535 \u5185\u6216\u4E0D\u662F\u6574\u6570`)}function r(u){if(!Number.isInteger(u)||u<1e3||u>864e5)throw new Error(`\u914D\u7F6E\u6821\u9A8C\u5931\u8D25: session_timeout_ms \u503C ${u} \u4E0D\u5728\u5408\u6CD5\u8303\u56F4 1000-86400000 \u5185`)}export{s as createDefaultGatewayConfig};
@@ -1 +1 @@
1
- const r=3e4;class c{connectionManager;onDisconnected;bindings=new Map;constructor(n,i){this.connectionManager=n,this.onDisconnected=i}async bind(n,i){if(this.bindings.has(n))throw new Error(`Session ${n} is already bound to a connection`);const e=await this.connectWithTimeout(i),t=[],s=e.onDisconnected(()=>{this.removeBinding(n),this.onDisconnected(n)});return t.push(s),this.bindings.set(n,{sessionId:n,handle:e,subscriptions:t}),e}getHandle(n){return this.bindings.get(n)?.handle}unbind(n){const i=this.bindings.get(n);if(i){this.bindings.delete(n);for(const e of i.subscriptions)e();i.handle.disconnect()}}unbindAll(){const n=[...this.bindings.keys()];for(const i of n)this.unbind(i)}connectWithTimeout(n){return new Promise((i,e)=>{let t=!1;const s=setTimeout(()=>{t||(t=!0,e(new Error("Connection bind timeout after 30000ms")))},3e4);this.connectionManager.connect({agentId:n.agentId,apiKey:n.apiKey,url:n.wsUrl,clientType:n.clientType,capabilities:["agent_invoke"],adapterHint:`${n.clientType}/base`},{maxRetries:0}).then(o=>{t?o.disconnect():(t=!0,clearTimeout(s),i(o))}).catch(o=>{t||(t=!0,clearTimeout(s),e(o))})})}removeBinding(n){const i=this.bindings.get(n);if(i){this.bindings.delete(n);for(const e of i.subscriptions)e()}}}export{c as ConnectionBindingImpl};
1
+ const a=3e4;class c{connectionManager;onDisconnected;bindings=new Map;constructor(n,i){this.connectionManager=n,this.onDisconnected=i}async bind(n,i){if(this.bindings.has(n))throw new Error(`Session ${n} is already bound to a connection`);const e=await this.connectWithTimeout(i),t=[],s=e.onDisconnected(()=>{this.removeBinding(n),this.onDisconnected(n)});return t.push(s),this.bindings.set(n,{sessionId:n,handle:e,subscriptions:t}),e}getHandle(n){return this.bindings.get(n)?.handle}unbind(n){const i=this.bindings.get(n);if(i){this.bindings.delete(n);for(const e of i.subscriptions)e();i.handle.disconnect()}}unbindAll(){const n=[...this.bindings.keys()];for(const i of n)this.unbind(i)}connectWithTimeout(n){return new Promise((i,e)=>{let t=!1;const s=setTimeout(()=>{t||(t=!0,e(new Error("Connection bind timeout after 30000ms")))},3e4);this.connectionManager.connect({agentId:n.agentId,apiKey:n.apiKey,url:n.wsUrl,clientType:n.clientType,capabilities:["agent_invoke"],adapterHint:`${n.clientType}/base`},{maxRetries:0}).then(o=>{t?o.disconnect():(t=!0,clearTimeout(s),i(o))}).catch(o=>{t||(t=!0,clearTimeout(s),e(o))})})}removeBinding(n){const i=this.bindings.get(n);if(i){this.bindings.delete(n);for(const e of i.subscriptions)e()}}}export{c as ConnectionBindingImpl};
@@ -1 +1 @@
1
- function a(o){const e=new Set([`http://127.0.0.1:${o.serverPort}`,`http://localhost:${o.serverPort}`,...o.allowedOrigins]),t=new Set([`127.0.0.1:${o.serverPort}`,`localhost:${o.serverPort}`,...o.allowedHosts]);return{validateRequest(s){const r=i(s,e);if(!r.ok)return r;const n=l(s,t);return n.ok?{ok:!0}:n}}}function i(o,e){const t=o.headers.origin;return t?e.has(t)?{ok:!0}:{ok:!1,statusCode:403,message:`Origin not allowed: ${t}`}:{ok:!0}}function l(o,e){const t=o.headers.host;return t?e.has(t)?{ok:!0}:{ok:!1,statusCode:403,message:`Host not allowed: ${t}`}:{ok:!1,statusCode:403,message:"Missing Host header"}}export{a as createSecurityPolicy};
1
+ function a(t){const o=new Set([`http://127.0.0.1:${t.serverPort}`,`http://localhost:${t.serverPort}`,...t.allowedOrigins]),e=new Set([`127.0.0.1:${t.serverPort}`,`localhost:${t.serverPort}`,...t.allowedHosts]);return{validateRequest(s){const r=i(s,o);if(!r.ok)return r;const n=l(s,e);return n.ok?{ok:!0}:n}}}function i(t,o){const e=t.headers.origin;return e?o.has(e)?{ok:!0}:{ok:!1,statusCode:403,message:`Origin not allowed: ${e}`}:{ok:!0}}function l(t,o){const e=t.headers.host;return e?o.has(e)?{ok:!0}:{ok:!1,statusCode:403,message:`Host not allowed: ${e}`}:{ok:!1,statusCode:403,message:"Missing Host header"}}export{a as createSecurityPolicy};
@@ -1 +1 @@
1
- import{toolCallToInvoke as i}from"../../core/mcp/tools.js";import{ToolRegistryImpl as l}from"./tool-registry.js";import{validateToolArgs as a}from"./tool-schemas.js";import{isEventTool as p,executeEventTool as d}from"./event-tool-executor.js";class y{registry;constructor(){this.registry=new l}async execute(t,e,r,n){if(!this.registry.hasTool(e))return this.errorResult(`\u672A\u77E5\u5DE5\u5177: ${e}`);const s=a(e,r);if(!s.valid)return this.errorResult(`\u53C2\u6570\u6821\u9A8C\u5931\u8D25: ${s.error}`);if(t.status!=="ready")return this.errorResult(`\u8FDE\u63A5\u4E0D\u53EF\u7528: \u5F53\u524D\u72B6\u6001\u4E3A ${t.status}`);if(p(e))return this.executeEventTool(t,e,r);const o=i(e,r);try{const u=await t.agentInvoke(o.action,o.params,n);return this.normalizeResult(u)}catch(u){const c=u instanceof Error?u.message:String(u);return c.toLowerCase().includes("timeout")?this.errorResult(`\u8C03\u7528\u8D85\u65F6: ${c}`):this.errorResult(`\u8C03\u7528\u5931\u8D25: ${c}`)}}normalizeResult(t){if(t==null||typeof t!="object")return this.successResult(t??null);const e=t,r=typeof e.code=="number"?e.code:0;if(r===0){const s="data"in e?e.data:null;return this.successResult(s??null)}const n=typeof e.msg=="string"?e.msg:"\u672A\u77E5\u9519\u8BEF";return this.errorResult(`\u4E0A\u6E38\u9519\u8BEF [code=${r}]: ${n}`)}successResult(t){return{content:[{type:"text",text:JSON.stringify(t)}],isError:!1}}errorResult(t){return{content:[{type:"text",text:t}],isError:!0}}async executeEventTool(t,e,r){return e==="grix_access_control"?this.executeAccessControl(t,r):d(t,e,r)}async executeAccessControl(t,e){const r=String(e.action??""),n={pair_approve:"pair_approve",pair_deny:"pair_deny",allow_sender:"sender_allow",remove_sender:"sender_remove",set_policy:"policy_set"}[r];if(!n)return this.errorResult(`\u672A\u77E5 access_control action: ${r}`);const s={};e.code!=null&&(s.code=e.code),e.sender_id!=null&&(s.sender_id=e.sender_id),e.policy!=null&&(s.policy=e.policy);try{const o=await t.agentInvoke("claude_access_control",{verb:n,payload:s},3e4);return this.successResult(o)}catch(o){const u=o instanceof Error?o.message:String(o);return this.errorResult(`access_control \u8C03\u7528\u5931\u8D25: ${u}`)}}}export{y as ToolExecutorImpl};
1
+ import{toolCallToInvoke as i}from"../../core/mcp/tools.js";import{ToolRegistryImpl as l}from"./tool-registry.js";import{validateToolArgs as a}from"./tool-schemas.js";import{isEventTool as p,executeEventTool as d}from"./event-tool-executor.js";class y{registry;constructor(){this.registry=new l}async execute(r,e,t,n){if(!this.registry.hasTool(e))return this.errorResult(`\u672A\u77E5\u5DE5\u5177: ${e}`);const s=a(e,t);if(!s.valid)return this.errorResult(`\u53C2\u6570\u6821\u9A8C\u5931\u8D25: ${s.error}`);if(r.status!=="ready")return this.errorResult(`\u8FDE\u63A5\u4E0D\u53EF\u7528: \u5F53\u524D\u72B6\u6001\u4E3A ${r.status}`);if(p(e))return this.executeEventTool(r,e,t);const o=i(e,t);try{const u=await r.agentInvoke(o.action,o.params,n);return this.normalizeResult(u)}catch(u){const c=u instanceof Error?u.message:String(u);return c.toLowerCase().includes("timeout")?this.errorResult(`\u8C03\u7528\u8D85\u65F6: ${c}`):this.errorResult(`\u8C03\u7528\u5931\u8D25: ${c}`)}}normalizeResult(r){if(r==null||typeof r!="object")return this.successResult(r??null);const e=r,t=typeof e.code=="number"?e.code:0;if(t===0){const s="data"in e?e.data:null;return this.successResult(s??null)}const n=typeof e.msg=="string"?e.msg:"\u672A\u77E5\u9519\u8BEF";return this.errorResult(`\u4E0A\u6E38\u9519\u8BEF [code=${t}]: ${n}`)}successResult(r){return{content:[{type:"text",text:JSON.stringify(r)}],isError:!1}}errorResult(r){return{content:[{type:"text",text:r}],isError:!0}}async executeEventTool(r,e,t){return e==="grix_access_control"?this.executeAccessControl(r,t):d(r,e,t)}async executeAccessControl(r,e){const t=String(e.action??""),n={pair_approve:"pair_approve",pair_deny:"pair_deny",allow_sender:"sender_allow",remove_sender:"sender_remove",set_policy:"policy_set"}[t];if(!n)return this.errorResult(`\u672A\u77E5 access_control action: ${t}`);const s={};e.code!=null&&(s.code=e.code),e.sender_id!=null&&(s.sender_id=e.sender_id),e.policy!=null&&(s.policy=e.policy);try{const o=await r.agentInvoke("claude_access_control",{verb:n,payload:s},3e4);return this.successResult(o)}catch(o){const u=o instanceof Error?o.message:String(o);return this.errorResult(`access_control \u8C03\u7528\u5931\u8D25: ${u}`)}}}export{y as ToolExecutorImpl};
@@ -1 +1 @@
1
- import{TOOLS as s,EVENT_TOOLS as t}from"../../core/mcp/tools.js";const e=new Set(["grix_query","grix_group","grix_message_send","grix_message_unsend","grix_admin"]),r=new Set(["grix_reply","grix_complete","grix_event_ack","grix_composing","grix_access_control","grix_status"]);class a{tools;toolMap;constructor(){this.tools=[...s.filter(o=>e.has(o.name)),...t.filter(o=>r.has(o.name))],this.toolMap=new Map(this.tools.map(o=>[o.name,o]))}getTools(){return this.tools}getTool(o){return this.toolMap.get(o)}hasTool(o){return this.toolMap.has(o)}}export{a as ToolRegistryImpl};
1
+ import{TOOLS as o,EVENT_TOOLS as s}from"../../core/mcp/tools.js";const e=new Set(["grix_query","grix_group","grix_message_send","grix_message_unsend","grix_admin"]),r=new Set(["grix_reply","grix_complete","grix_event_ack","grix_composing","grix_access_control","grix_status"]);class a{tools;toolMap;constructor(){this.tools=[...o.filter(t=>e.has(t.name)),...s.filter(t=>r.has(t.name))],this.toolMap=new Map(this.tools.map(t=>[t.name,t]))}getTools(){return this.tools}getTool(t){return this.toolMap.get(t)}hasTool(t){return this.toolMap.has(t)}}export{a as ToolRegistryImpl};
@@ -1 +1 @@
1
- const o={required:["action"],properties:{action:{type:"string",enum:["contact_search","session_search","message_history","message_search"]},id:{type:"string"},keyword:{type:"string",maxLength:200},limit:{type:"integer",minimum:1,maximum:100},offset:{type:"integer",minimum:0},sessionId:{type:"string"},beforeId:{type:"string"}}},a={required:["action"],properties:{action:{type:"string",enum:["create","detail","leave","add_members","remove_members","update_member_role","update_all_members_muted","update_member_speaking","dissolve"]},sessionId:{type:"string"},name:{type:"string",maxLength:128},memberIds:{type:"array",items:{type:"string"},maxItems:100},memberTypes:{type:"array",items:{type:"integer",enum:[1,2]}},memberId:{type:"string"},role:{type:"integer",enum:[1,2]},memberType:{type:"integer"},allMembersMuted:{type:"boolean"},isSpeakMuted:{type:"boolean"},canSpeakWhenAllMuted:{type:"boolean"}}},p={required:["sessionId","content"],properties:{sessionId:{type:"string"},content:{type:"string",maxLength:1e4},msgType:{type:"integer"},quotedMessageId:{type:"string"},threadId:{type:"string"}}},m={required:["sessionId","msgId"],properties:{sessionId:{type:"string"},msgId:{type:"string"}}},g={required:["action"],properties:{action:{type:"string",enum:["create_agent","list_categories","create_category","update_category","assign_category","rotate_api_key"]},agentName:{type:"string"},introduction:{type:"string"},isMain:{type:"boolean"},agentId:{type:"string"},categoryId:{type:"string"},name:{type:"string"},parentId:{type:"string"},sortOrder:{type:"integer"}}},y={required:["session_id","text"],properties:{event_id:{type:"string"},session_id:{type:"string"},text:{type:"string",maxLength:5e4},quoted_message_id:{type:"string"},is_final:{type:"boolean"}}},d={required:["event_id","status"],properties:{event_id:{type:"string"},status:{type:"string",enum:["responded","canceled","failed"]},msg:{type:"string",maxLength:500}}},c={required:["event_id"],properties:{event_id:{type:"string"},session_id:{type:"string"}}},l={required:["session_id","active"],properties:{session_id:{type:"string"},active:{type:"boolean"},event_id:{type:"string"}}},_={required:["action"],properties:{action:{type:"string",enum:["pair_approve","pair_deny","allow_sender","remove_sender","set_policy"]},code:{type:"string"},sender_id:{type:"string"},policy:{type:"string",enum:["allowlist","open","disabled"]}}},f={required:[],properties:{}},C={grix_query:o,grix_group:a,grix_message_send:p,grix_message_unsend:m,grix_admin:g,grix_reply:y,grix_complete:d,grix_event_ack:c,grix_composing:l,grix_access_control:_,grix_status:f};function B(u,t){const e=C[u];if(!e)return{valid:!1,error:`\u672A\u77E5\u5DE5\u5177: ${u}`};for(const i of e.required)if(t[i]===void 0||t[i]===null)return{valid:!1,error:`\u7F3A\u5C11\u5FC5\u586B\u53C2\u6570: ${i}`};for(const[i,r]of Object.entries(t)){if(r==null)continue;const n=e.properties[i];if(!n)continue;const s=$(i,r,n);if(s)return{valid:!1,error:s}}return{valid:!0}}function $(u,t,e){switch(e.type){case"string":if(typeof t!="string")return`\u53C2\u6570 ${u} \u7C7B\u578B\u9519\u8BEF: \u671F\u671B string\uFF0C\u5B9E\u9645 ${typeof t}`;if(e.maxLength!==void 0&&t.length>e.maxLength)return`\u53C2\u6570 ${u} \u8D85\u8FC7\u6700\u5927\u957F\u5EA6 ${e.maxLength}\uFF0C\u5B9E\u9645 ${t.length}`;if(e.enum&&!e.enum.includes(t))return`\u53C2\u6570 ${u} \u503C "${t}" \u4E0D\u5728\u5141\u8BB8\u8303\u56F4 [${e.enum.join(", ")}]`;break;case"integer":if(typeof t!="number"||!Number.isInteger(t))return`\u53C2\u6570 ${u} \u7C7B\u578B\u9519\u8BEF: \u671F\u671B integer\uFF0C\u5B9E\u9645 ${typeof t=="number"?"\u6D6E\u70B9\u6570":typeof t}`;if(e.minimum!==void 0&&t<e.minimum)return`\u53C2\u6570 ${u} \u503C ${t} \u5C0F\u4E8E\u6700\u5C0F\u503C ${e.minimum}`;if(e.maximum!==void 0&&t>e.maximum)return`\u53C2\u6570 ${u} \u503C ${t} \u5927\u4E8E\u6700\u5927\u503C ${e.maximum}`;if(e.enum&&!e.enum.includes(t))return`\u53C2\u6570 ${u} \u503C ${t} \u4E0D\u5728\u5141\u8BB8\u8303\u56F4 [${e.enum.join(", ")}]`;break;case"boolean":if(typeof t!="boolean")return`\u53C2\u6570 ${u} \u7C7B\u578B\u9519\u8BEF: \u671F\u671B boolean\uFF0C\u5B9E\u9645 ${typeof t}`;break;case"array":if(!Array.isArray(t))return`\u53C2\u6570 ${u} \u7C7B\u578B\u9519\u8BEF: \u671F\u671B array\uFF0C\u5B9E\u9645 ${typeof t}`;if(e.maxItems!==void 0&&t.length>e.maxItems)return`\u53C2\u6570 ${u} \u8D85\u8FC7\u6700\u5927\u5143\u7D20\u6570 ${e.maxItems}\uFF0C\u5B9E\u9645 ${t.length}`;if(e.items)for(let i=0;i<t.length;i++){const r=t[i];if(e.items.type==="string"&&typeof r!="string")return`\u53C2\u6570 ${u}[${i}] \u7C7B\u578B\u9519\u8BEF: \u671F\u671B string\uFF0C\u5B9E\u9645 ${typeof r}`;if(e.items.type==="integer"){if(typeof r!="number"||!Number.isInteger(r))return`\u53C2\u6570 ${u}[${i}] \u7C7B\u578B\u9519\u8BEF: \u671F\u671B integer\uFF0C\u5B9E\u9645 ${typeof r}`;if(e.items.enum&&!e.items.enum.includes(r))return`\u53C2\u6570 ${u}[${i}] \u503C ${r} \u4E0D\u5728\u5141\u8BB8\u8303\u56F4 [${e.items.enum.join(", ")}]`}}break}}export{B as validateToolArgs};
1
+ const o={required:["action"],properties:{action:{type:"string",enum:["contact_search","session_search","message_history","message_search"]},id:{type:"string"},keyword:{type:"string",maxLength:200},limit:{type:"integer",minimum:1,maximum:100},offset:{type:"integer",minimum:0},sessionId:{type:"string"},beforeId:{type:"string"}}},a={required:["action"],properties:{action:{type:"string",enum:["create","detail","leave","add_members","remove_members","update_member_role","update_all_members_muted","update_member_speaking","dissolve"]},sessionId:{type:"string"},name:{type:"string",maxLength:128},memberIds:{type:"array",items:{type:"string"},maxItems:100},memberTypes:{type:"array",items:{type:"integer",enum:[1,2]}},memberId:{type:"string"},role:{type:"integer",enum:[1,2]},memberType:{type:"integer"},allMembersMuted:{type:"boolean"},isSpeakMuted:{type:"boolean"},canSpeakWhenAllMuted:{type:"boolean"}}},p={required:["sessionId","content"],properties:{sessionId:{type:"string"},content:{type:"string",maxLength:1e4},msgType:{type:"integer"},quotedMessageId:{type:"string"},threadId:{type:"string"}}},m={required:["sessionId","msgId"],properties:{sessionId:{type:"string"},msgId:{type:"string"}}},g={required:["action"],properties:{action:{type:"string",enum:["create_agent","list_categories","create_category","update_category","assign_category","rotate_api_key"]},agentName:{type:"string"},introduction:{type:"string"},isMain:{type:"boolean"},agentId:{type:"string"},categoryId:{type:"string"},name:{type:"string"},parentId:{type:"string"},sortOrder:{type:"integer"}}},y={required:["session_id","text"],properties:{event_id:{type:"string"},session_id:{type:"string"},text:{type:"string",maxLength:5e4},quoted_message_id:{type:"string"},is_final:{type:"boolean"}}},d={required:["event_id","status"],properties:{event_id:{type:"string"},status:{type:"string",enum:["responded","canceled","failed"]},msg:{type:"string",maxLength:500}}},c={required:["event_id"],properties:{event_id:{type:"string"},session_id:{type:"string"}}},l={required:["session_id","active"],properties:{session_id:{type:"string"},active:{type:"boolean"},event_id:{type:"string"}}},_={required:["action"],properties:{action:{type:"string",enum:["pair_approve","pair_deny","allow_sender","remove_sender","set_policy"]},code:{type:"string"},sender_id:{type:"string"},policy:{type:"string",enum:["allowlist","open","disabled"]}}},f={required:[],properties:{}},C={grix_query:o,grix_group:a,grix_message_send:p,grix_message_unsend:m,grix_admin:g,grix_reply:y,grix_complete:d,grix_event_ack:c,grix_composing:l,grix_access_control:_,grix_status:f};function B(r,t){const e=C[r];if(!e)return{valid:!1,error:`\u672A\u77E5\u5DE5\u5177: ${r}`};for(const i of e.required)if(t[i]===void 0||t[i]===null)return{valid:!1,error:`\u7F3A\u5C11\u5FC5\u586B\u53C2\u6570: ${i}`};for(const[i,u]of Object.entries(t)){if(u==null)continue;const n=e.properties[i];if(!n)continue;const s=$(i,u,n);if(s)return{valid:!1,error:s}}return{valid:!0}}function $(r,t,e){switch(e.type){case"string":if(typeof t!="string")return`\u53C2\u6570 ${r} \u7C7B\u578B\u9519\u8BEF: \u671F\u671B string\uFF0C\u5B9E\u9645 ${typeof t}`;if(e.maxLength!==void 0&&t.length>e.maxLength)return`\u53C2\u6570 ${r} \u8D85\u8FC7\u6700\u5927\u957F\u5EA6 ${e.maxLength}\uFF0C\u5B9E\u9645 ${t.length}`;if(e.enum&&!e.enum.includes(t))return`\u53C2\u6570 ${r} \u503C "${t}" \u4E0D\u5728\u5141\u8BB8\u8303\u56F4 [${e.enum.join(", ")}]`;break;case"integer":if(typeof t!="number"||!Number.isInteger(t))return`\u53C2\u6570 ${r} \u7C7B\u578B\u9519\u8BEF: \u671F\u671B integer\uFF0C\u5B9E\u9645 ${typeof t=="number"?"\u6D6E\u70B9\u6570":typeof t}`;if(e.minimum!==void 0&&t<e.minimum)return`\u53C2\u6570 ${r} \u503C ${t} \u5C0F\u4E8E\u6700\u5C0F\u503C ${e.minimum}`;if(e.maximum!==void 0&&t>e.maximum)return`\u53C2\u6570 ${r} \u503C ${t} \u5927\u4E8E\u6700\u5927\u503C ${e.maximum}`;if(e.enum&&!e.enum.includes(t))return`\u53C2\u6570 ${r} \u503C ${t} \u4E0D\u5728\u5141\u8BB8\u8303\u56F4 [${e.enum.join(", ")}]`;break;case"boolean":if(typeof t!="boolean")return`\u53C2\u6570 ${r} \u7C7B\u578B\u9519\u8BEF: \u671F\u671B boolean\uFF0C\u5B9E\u9645 ${typeof t}`;break;case"array":if(!Array.isArray(t))return`\u53C2\u6570 ${r} \u7C7B\u578B\u9519\u8BEF: \u671F\u671B array\uFF0C\u5B9E\u9645 ${typeof t}`;if(e.maxItems!==void 0&&t.length>e.maxItems)return`\u53C2\u6570 ${r} \u8D85\u8FC7\u6700\u5927\u5143\u7D20\u6570 ${e.maxItems}\uFF0C\u5B9E\u9645 ${t.length}`;if(e.items)for(let i=0;i<t.length;i++){const u=t[i];if(e.items.type==="string"&&typeof u!="string")return`\u53C2\u6570 ${r}[${i}] \u7C7B\u578B\u9519\u8BEF: \u671F\u671B string\uFF0C\u5B9E\u9645 ${typeof u}`;if(e.items.type==="integer"){if(typeof u!="number"||!Number.isInteger(u))return`\u53C2\u6570 ${r}[${i}] \u7C7B\u578B\u9519\u8BEF: \u671F\u671B integer\uFF0C\u5B9E\u9645 ${typeof u}`;if(e.items.enum&&!e.items.enum.includes(u))return`\u53C2\u6570 ${r}[${i}] \u503C ${u} \u4E0D\u5728\u5141\u8BB8\u8303\u56F4 [${e.items.enum.join(", ")}]`}}break}}export{B as validateToolArgs};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "grix-connector",
3
- "version": "3.15.3",
3
+ "version": "3.15.4",
4
4
  "description": "Connect local AI coding agents (Claude, Codex, Gemini, Qwen, DeepSeek, Cursor, OpenCode, Pi, OpenHuman, Reasonix) to the Grix scheduling platform. Also serves as an OpenClaw plugin for Grix channel transport.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",