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.
4 lines (3 loc) • 2.42 kB
JavaScript
import{EventEmitter as p}from"events";class w extends p{nextId=1;pending=new Map;writeLock=Promise.resolve();closed=!1;_readable;_writable;_onNotify;_onRequest;constructor(t,e,r,o){super(),this._readable=t,this._writable=e,this._onNotify=r,this._onRequest=o,this.startReadLoop()}setHandlers(t,e){t&&(this._onNotify=t),e&&(this._onRequest=e)}async call(t,e,r){if(this.closed)throw new Error("transport closed");const o=this.nextId++,s=String(o),a=await new Promise((i,d)=>{if(this.pending.set(s,{resolve:i}),r){const n=()=>{this.pending.delete(s),d(new Error(`aborted: ${r.reason??"request cancelled"}`))};if(r.aborted){n();return}r.addEventListener("abort",n,{once:!0});const c=i,h=l=>{r.removeEventListener("abort",n),c(l)};this.pending.set(s,{resolve:h})}this.write({jsonrpc:"2.0",id:o,method:t,params:e??null}).catch(n=>{this.pending.delete(s),d(n)})});if(a.error){const i=new Error(a.error.message);throw i.code=a.error.code,i}return a.result}async notify(t,e){if(this.closed)throw new Error("transport closed");await this.write({jsonrpc:"2.0",method:t,params:e??null})}async respondSuccess(t,e){await this.write({jsonrpc:"2.0",id:t,result:e})}async respondError(t,e,r){await this.write({jsonrpc:"2.0",id:t,error:{code:e,message:r}})}close(){if(!this.closed){this.closed=!0;for(const[t,{resolve:e}]of this.pending)e({error:{code:-32e3,message:"transport closed"}});this.pending.clear(),this._readable.destroy(),this.emit("close")}}startReadLoop(){let t="";this._readable.on("data",e=>{t+=e.toString();const r=t.split(`
`);t=r.pop()??"";for(const o of r){const s=o.trim();s.length!==0&&this.dispatch(s)}}),this._readable.on("end",()=>{t.trim()&&this.dispatch(t.trim()),this.close()}),this._readable.on("error",e=>{this.emit("error",e),this.close()})}dispatch(t){let e;try{e=JSON.parse(t)}catch{return}if(e.id!=null&&!e.method){this.completePending(e.id,e.result,e.error);return}if(e.method&&(e.id==null||e.id===null)){this._onNotify?.(e.method,e.params);return}e.method&&e.id!=null&&this._onRequest?.(e.method,e.id,e.params)}completePending(t,e,r){const o=String(t),s=this.pending.get(o);s&&(this.pending.delete(o),s.resolve({result:e,error:r}))}async write(t){const e=this.writeLock;return this.writeLock=e.then(async()=>new Promise((r,o)=>{const s=JSON.stringify(t)+`
`;this._writable.write(s,"utf-8")?r():(this._writable.once("drain",r),this._writable.once("error",o))})),this.writeLock}}export{w as JsonRpcTransport};