grix-connector
Version:
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.
2 lines (1 loc) • 2.88 kB
JavaScript
import{EventEmitter as h}from"node:events";import{readFile as d}from"node:fs/promises";import{join as u}from"node:path";import{io as p}from"socket.io-client";import{log as a}from"../../core/log/index.js";const m=3e4,_=1e4,k=500,l=15e3;class b extends h{socket=null;rpcUrl="";bearerToken="";nextRpcId=1;closed=!1;async connect(t,o){this.bearerToken=o,this.rpcUrl=`${t}/rpc`,this.socket=p(t,{auth:{token:o},transports:["websocket"],reconnection:!0,reconnectionAttempts:10,reconnectionDelay:1e3,reconnectionDelayMax:3e4}),this.socket.on("connect",()=>{a.info("openhuman-transport",`Socket.io connected sid=${this.socket?.id}`)}),this.socket.on("disconnect",e=>{a.warn("openhuman-transport",`Socket.io disconnected: ${e}`)}),this.socket.on("connect_error",e=>{a.warn("openhuman-transport",`Socket.io connect error: ${e.message}`)}),this.registerEventHandlers(),await new Promise((e,s)=>{if(this.socket.connected){e();return}const n=setTimeout(()=>s(new Error("Socket.io connection timeout")),_);this.socket.once("connect",()=>{clearTimeout(n),e()}),this.socket.once("connect_error",i=>{clearTimeout(n),s(i)})})}registerEventHandlers(){if(!this.socket)return;const t=["inference_start","iteration_start","text_delta","thinking_delta","tool_args_delta","tool_call","tool_result","subagent_spawned","subagent_completed","subagent_failed","chat_done","chat_error","chat_accepted","chat_segment","task_board_updated"];for(const o of t)this.socket.on(o,e=>{this.emit("event",e)})}async call(t,o){if(this.closed)throw new Error("transport closed");const s={jsonrpc:"2.0",id:this.nextRpcId++,method:t,params:o},n=new AbortController,i=setTimeout(()=>n.abort(),m);try{const r=await(await fetch(this.rpcUrl,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.bearerToken}`},body:JSON.stringify(s),signal:n.signal})).json();if(r.error)throw new Error(`RPC ${t}: ${r.error.message} (code=${r.error.code})`);if(!r.result?.result)throw new Error(`RPC ${t}: missing result payload`);return r.result.result}finally{clearTimeout(i)}}async webChat(t){return this.call("openhuman.channel_web_chat",t)}async webCancel(t){await this.call("openhuman.channel_web_cancel",t)}async storeSession(t){await this.call("openhuman.auth_store_session",t)}async healthCheck(){try{return(await fetch(this.rpcUrl.replace("/rpc","/health"),{method:"GET",signal:AbortSignal.timeout(3e3)})).ok}catch{return!1}}disconnect(){this.closed=!0,this.socket&&(this.socket.removeAllListeners(),this.socket.disconnect(),this.socket=null),this.removeAllListeners()}get socketId(){return this.socket?.id}}async function S(c){const t=u(c,"core.token"),o=Date.now()+l;for(;Date.now()<o;){try{const e=(await d(t,"utf-8")).trim();if(e)return e}catch{}await new Promise(e=>setTimeout(e,k))}throw new Error(`core.token not found in ${c} after ${l/1e3}s`)}export{b as OpenHumanTransport,S as readBearerToken};