@aerostack/gateway 0.15.1 → 0.15.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- import{readFile as u}from"node:fs/promises";import{join as p}from"node:path";import{homedir as m}from"node:os";import{info as f,warn as l,debug as c}from"./logger.js";import{addToBatch as g,detectCategory as w,summarizeToolInput as y}from"./hook-server.js";async function N(){try{const d=p(m(),".openclaw","openclaw.json"),e=await u(d,"utf-8");return JSON.parse(e)?.gateway?.auth?.token??null}catch{return null}}async function R(){try{const d=p(m(),".openclaw","exec-approvals.json"),e=await u(d,"utf-8");return JSON.parse(e)?.socket?.token??null}catch{return null}}const S=1e3,T=3e4;class M{opts;ws=null;destroyed=!1;reconnectMs=S;reconnectTimer=null;requestId=0;seenSessions=new Set;pendingRequests=new Map;constructor(e){this.opts=e}async connect(){if(this.destroyed)return!1;const e=await this.getWebSocket(),t=`ws://127.0.0.1:${this.opts.port}`;return new Promise(s=>{try{const n=new e(t);this.ws=n;const o=setTimeout(()=>{c("OpenClaw connect timeout");try{n.close()}catch{}s(!1)},1e4);n.onopen=()=>{clearTimeout(o),c("OpenClaw WS connected, sending handshake"),this.sendHandshake()},n.onmessage=i=>{try{const r=JSON.parse(String(i.data));this.handleFrame(r,s)}catch{}},n.onerror=()=>{clearTimeout(o),s(!1)},n.onclose=()=>{clearTimeout(o),this.ws=null,this.destroyed||this.scheduleReconnect()}}catch{s(!1)}})}stop(){if(this.destroyed=!0,this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null),this.ws){try{this.ws.close(1e3)}catch{}this.ws=null}this.pendingRequests.clear()}sendHandshake(){this.send({type:"req",id:String(++this.requestId),method:"connect",params:{minProtocol:3,maxProtocol:3,client:{id:"openclaw-tui",displayName:"Aerostack Guardian",version:"0.15.0",platform:process.platform,mode:"cli"},auth:{token:this.opts.token},scopes:["operator.read"],caps:["tool-events"]}})}connected=!1;subscribedSessions=new Set;handleFrame(e,t){if(e.type==="res"&&!this.connected){e.ok?(this.connected=!0,this.reconnectMs=S,f("OpenClaw connector connected",{port:this.opts.port}),t?.(!0),this.subscribeAllSessions().catch(s=>l("subscribeAllSessions failed",{error:s?.message}))):(l("OpenClaw connect rejected",{error:e.error?.message}),t?.(!1));return}if(e.type==="res"&&e.id&&this.pendingRequests.has(e.id)){const s=this.pendingRequests.get(e.id);this.pendingRequests.delete(e.id),s(e);return}if(e.type==="res"&&e.ok===!1&&e.error){l("OpenClaw RPC error",{error:e.error.message,code:e.error.code});return}e.type==="event"&&this.handleEvent(e)}subscribeToSession(e){if(this.subscribedSessions.has(e))return;this.subscribedSessions.add(e);const t=String(++this.requestId);this.pendingRequests.set(t,s=>{s.ok?c("OpenClaw subscribed to session",{key:e}):l("OpenClaw subscribe failed",{key:e,error:s.error?.message})}),this.send({type:"req",id:t,method:"sessions.messages.subscribe",params:{key:e}}),setTimeout(()=>this.pendingRequests.delete(t),1e4)}async subscribeAllSessions(){const e=await this.sendRequest("sessions.list",{limit:50});if(!e.ok){l("OpenClaw sessions.list failed",{error:e.error?.message});return}const t=e.payload?.sessions,s=Array.isArray(t)?t:[];f("OpenClaw active sessions",{count:s.length});for(const n of s)n.key&&this.subscribeToSession(n.key)}sendRequest(e,t){return new Promise(s=>{const n=String(++this.requestId),o=setTimeout(()=>{this.pendingRequests.delete(n),s({type:"res",id:n,ok:!1,error:{code:"TIMEOUT",message:"request timeout"}})},1e4);this.pendingRequests.set(n,i=>{clearTimeout(o),s(i)}),this.send({type:"req",id:n,method:e,params:t})})}handleEvent(e){const t=e.event,s=e.payload??{};t==="agent"?this.handleAgentEvent(s):t==="session.tool"?this.handleToolEvent(s):t==="session.message"?this.handleMessageEvent(s):t==="sessions.changed"&&this.handleSessionChanged(s)}handleAgentEvent(e){const t=e.stream,s=e.data,n=e.sessionKey??"";if(n&&this.seenSessions.add(n),t==="tool"&&s){const o=s.toolName??s.name??"unknown",i=s.phase??"",r=s.input??{};if(i==="start"){const{category:a,risk:h}=w(o,r),b=y(o,r);g({action:`${o}: ${b}`.slice(0,500),category:a,risk_level:h,details:JSON.stringify({tool:o,session:n,...r}).slice(0,500)}),c("OpenClaw agent tool event",{tool:o,category:a,risk:h})}}if(t==="lifecycle"&&s){const o=s.phase??"";(o==="start"||o==="end")&&c("OpenClaw agent lifecycle",{phase:o,session:n})}}handleToolEvent(e){const t=e.data;if(!t)return;const s=t.toolName??"unknown",n=t.phase??"",o=t.input??{},i=e.sessionKey??"";if(n!=="start"&&n!=="end"||n==="end")return;const{category:r,risk:a}=w(s,o),h=y(s,o);g({action:`${s}: ${h}`.slice(0,500),category:r,risk_level:a,details:JSON.stringify({tool:s,session:i,...o}).slice(0,500)}),c("OpenClaw tool event",{tool:s,category:r,risk:a,session:i}),i&&this.seenSessions.add(i)}handleMessageEvent(e){const t=e.sessionKey??"";t&&this.seenSessions.add(t)}handleSessionChanged(e){const t=e.sessionKey??"";t&&(this.seenSessions.add(t),this.subscribeToSession(t))}send(e){if(this.ws)try{this.ws.send(JSON.stringify(e))}catch{}}scheduleReconnect(){this.destroyed||this.reconnectTimer||(c("OpenClaw reconnecting in",{ms:this.reconnectMs}),this.reconnectTimer=setTimeout(async()=>{this.reconnectTimer=null,this.connected=!1,this.seenSessions.clear(),this.subscribedSessions.clear(),this.pendingRequests.clear(),await this.connect()},this.reconnectMs),this.reconnectMs=Math.min(this.reconnectMs*2,T))}async getWebSocket(){return typeof globalThis.WebSocket<"u"?globalThis.WebSocket:(await import("ws")).default}}export{M as OpenClawConnector,R as resolveExecApprovalToken,N as resolveOpenClawToken};
1
+ import{readFile as p}from"node:fs/promises";import{join as g}from"node:path";import{homedir as m}from"node:os";import{info as u,warn as a,debug as c}from"./logger.js";import{addToBatch as f,detectCategory as w,summarizeToolInput as y}from"./hook-server.js";async function R(){try{const d=g(m(),".openclaw","openclaw.json"),e=await p(d,"utf-8");return JSON.parse(e)?.gateway?.auth?.token??null}catch{return null}}async function M(){try{const d=g(m(),".openclaw","exec-approvals.json"),e=await p(d,"utf-8");return JSON.parse(e)?.socket?.token??null}catch{return null}}const b=1e3,T=3e4;class N{opts;ws=null;destroyed=!1;reconnectMs=b;reconnectTimer=null;requestId=0;seenSessions=new Set;pendingRequests=new Map;constructor(e){this.opts=e}async connect(){if(this.destroyed)return!1;const e=await this.getWebSocket(),s=`ws://127.0.0.1:${this.opts.port}`;return new Promise(t=>{try{const n=new e(s);this.ws=n;const o=setTimeout(()=>{c("OpenClaw connect timeout");try{n.close()}catch{}t(!1)},1e4);n.onopen=()=>{clearTimeout(o),c("OpenClaw WS connected, sending handshake"),this.sendHandshake()},n.onmessage=i=>{try{const r=JSON.parse(String(i.data));this.handleFrame(r,t)}catch{}},n.onerror=()=>{clearTimeout(o),t(!1)},n.onclose=()=>{clearTimeout(o),this.ws=null,this.destroyed||this.scheduleReconnect()}}catch{t(!1)}})}stop(){if(this.destroyed=!0,this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null),this.ws){try{this.ws.close(1e3)}catch{}this.ws=null}this.pendingRequests.clear()}sendHandshake(){this.send({type:"req",id:String(++this.requestId),method:"connect",params:{minProtocol:3,maxProtocol:3,client:{id:"openclaw-tui",displayName:"Aerostack Guardian",version:"0.15.0",platform:process.platform,mode:"cli"},auth:{token:this.opts.token},scopes:["operator.read"],caps:["tool-events"]}})}connected=!1;subscribedSessions=new Set;handleFrame(e,s){if(e.type==="res"&&!this.connected){e.ok?(this.connected=!0,this.reconnectMs=b,u("OpenClaw connector connected",{port:this.opts.port}),s?.(!0),this.subscribeAllSessions().catch(t=>a("subscribeAllSessions failed",{error:t?.message}))):(a("OpenClaw connect rejected",{error:e.error?.message}),s?.(!1));return}if(e.type==="res"&&e.id&&this.pendingRequests.has(e.id)){const t=this.pendingRequests.get(e.id);this.pendingRequests.delete(e.id),t(e);return}if(e.type==="res"&&e.ok===!1&&e.error){a("OpenClaw RPC error",{error:e.error.message,code:e.error.code});return}e.type==="event"&&this.handleEvent(e)}subscribeToSessionMessages(e){if(this.subscribedSessions.has(e))return;this.subscribedSessions.add(e);const s=String(++this.requestId);this.pendingRequests.set(s,t=>{t.ok?c("OpenClaw subscribed to session messages",{key:e}):a("OpenClaw messages.subscribe failed",{key:e,error:t.error?.message})}),this.send({type:"req",id:s,method:"sessions.messages.subscribe",params:{key:e}}),setTimeout(()=>this.pendingRequests.delete(s),1e4)}async subscribeAllSessions(){const e=await this.sendRequest("sessions.subscribe");e.ok?u("OpenClaw subscribed to all session events (tool + lifecycle)"):a("OpenClaw sessions.subscribe failed",{error:e.error?.message});const s=await this.sendRequest("sessions.list",{limit:50});if(!s.ok){a("OpenClaw sessions.list failed",{error:s.error?.message});return}const t=s.payload?.sessions,n=Array.isArray(t)?t:[];u("OpenClaw active sessions",{count:n.length});for(const o of n)o.key&&this.subscribeToSessionMessages(o.key)}sendRequest(e,s){return new Promise(t=>{const n=String(++this.requestId),o=setTimeout(()=>{this.pendingRequests.delete(n),t({type:"res",id:n,ok:!1,error:{code:"TIMEOUT",message:"request timeout"}})},1e4);this.pendingRequests.set(n,i=>{clearTimeout(o),t(i)}),this.send({type:"req",id:n,method:e,params:s})})}handleEvent(e){const s=e.event,t=e.payload??{};s==="agent"?this.handleAgentEvent(t):s==="session.tool"?this.handleToolEvent(t):s==="session.message"?this.handleMessageEvent(t):s==="sessions.changed"&&this.handleSessionChanged(t)}handleAgentEvent(e){const s=e.stream,t=e.data,n=e.sessionKey??"";if(n&&this.seenSessions.add(n),s==="tool"&&t){const o=t.toolName??t.name??"unknown",i=t.phase??"",r=t.input??{};if(i==="start"){const{category:l,risk:h}=w(o,r),S=y(o,r);f({action:`${o}: ${S}`.slice(0,500),category:l,risk_level:h,details:JSON.stringify({tool:o,session:n,...r}).slice(0,500)}),c("OpenClaw agent tool event",{tool:o,category:l,risk:h})}}if(s==="lifecycle"&&t){const o=t.phase??"";(o==="start"||o==="end")&&c("OpenClaw agent lifecycle",{phase:o,session:n})}}handleToolEvent(e){const s=e.data;if(!s)return;const t=s.toolName??"unknown",n=s.phase??"",o=s.input??{},i=e.sessionKey??"";if(n!=="start"&&n!=="end"||n==="end")return;const{category:r,risk:l}=w(t,o),h=y(t,o);f({action:`${t}: ${h}`.slice(0,500),category:r,risk_level:l,details:JSON.stringify({tool:t,session:i,...o}).slice(0,500)}),c("OpenClaw tool event",{tool:t,category:r,risk:l,session:i}),i&&this.seenSessions.add(i)}handleMessageEvent(e){const s=e.sessionKey??"";s&&this.seenSessions.add(s)}handleSessionChanged(e){const s=e.sessionKey??"";s&&(this.seenSessions.add(s),this.subscribeToSessionMessages(s))}send(e){if(this.ws)try{this.ws.send(JSON.stringify(e))}catch{}}scheduleReconnect(){this.destroyed||this.reconnectTimer||(c("OpenClaw reconnecting in",{ms:this.reconnectMs}),this.reconnectTimer=setTimeout(async()=>{this.reconnectTimer=null,this.connected=!1,this.seenSessions.clear(),this.subscribedSessions.clear(),this.pendingRequests.clear(),await this.connect()},this.reconnectMs),this.reconnectMs=Math.min(this.reconnectMs*2,T))}async getWebSocket(){return typeof globalThis.WebSocket<"u"?globalThis.WebSocket:(await import("ws")).default}}export{N as OpenClawConnector,M as resolveExecApprovalToken,R as resolveOpenClawToken};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aerostack/gateway",
3
- "version": "0.15.1",
3
+ "version": "0.15.2",
4
4
  "description": "stdio-to-HTTP bridge connecting any MCP client to Aerostack Workspaces",
5
5
  "author": "Aerostack",
6
6
  "license": "MIT",