@aerostack/gateway 0.15.13 → 0.15.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +16 -18
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,26 +1,20 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import{Server as k}from"@modelcontextprotocol/sdk/server/index.js";import{StdioServerTransport as P}from"@modelcontextprotocol/sdk/server/stdio.js";import{ListToolsRequestSchema as C,CallToolRequestSchema as N,ListResourcesRequestSchema as I,ReadResourceRequestSchema as b,ListPromptsRequestSchema as L,GetPromptRequestSchema as U}from"@modelcontextprotocol/sdk/types.js";import{readFile as $,writeFile as K}from"node:fs/promises";import{join as D}from"node:path";import{homedir as x}from"node:os";import{resolveApproval as j}from"./resolution.js";import{startHookServer as q,installClaudeHook as H,stopHookServer as V}from"./hook-server.js";import{OpenClawConnector as M,resolveOpenClawToken as G}from"./openclaw-connector.js";import{info as i,warn as w,error as W}from"./logger.js";const g=D(x(),".openclaw","pre-authorized.json");async function Y(t){try{let s={};try{s=JSON.parse(await $(g,"utf-8"))}catch{}s[t]=Date.now(),await K(g,JSON.stringify(s))}catch{}}const R=process.env.AEROSTACK_WORKSPACE_URL,h=process.env.AEROSTACK_TOKEN;function f(t,s,r){const e=parseInt(t??String(s),10);return Number.isFinite(e)&&e>=r?e:s}const B=f(process.env.AEROSTACK_APPROVAL_POLL_MS,3e3,500),J=f(process.env.AEROSTACK_APPROVAL_TIMEOUT_MS,864e5,5e3),z=f(process.env.AEROSTACK_REQUEST_TIMEOUT_MS,3e4,1e3),F=process.env.AEROSTACK_HOOK_SERVER!=="false",Q=f(process.env.AEROSTACK_HOOK_PORT,18321,1024),X=process.env.AEROSTACK_HOOK_AUTO_INSTALL!=="false",Z=process.env.AEROSTACK_OPENCLAW_ENABLED!=="false",y=f(process.env.AEROSTACK_OPENCLAW_PORT,18789,1024),ee=process.env.AEROSTACK_OPENCLAW_TOKEN;R||(process.stderr.write(`ERROR: AEROSTACK_WORKSPACE_URL is required
4
- `),process.exit(1)),h||(process.stderr.write(`ERROR: AEROSTACK_TOKEN is required
5
- `),process.exit(1));let A;try{if(A=new URL(R),A.protocol!=="https:"&&A.protocol!=="http:")throw new Error("must be http or https")}catch{process.stderr.write(`ERROR: AEROSTACK_WORKSPACE_URL must be a valid HTTP(S) URL
6
- `),process.exit(1)}A.protocol==="http:"&&!A.hostname.match(/^(localhost|127\.0\.0\.1)$/)&&process.stderr.write(`WARNING: Using HTTP (not HTTPS) \u2014 token will be sent in plaintext
7
- `);const d=R.replace(/\/+$/,""),te=crypto.randomUUID(),se=process.env.AEROSTACK_AGENT_TYPE||"unknown";let u=null;async function c(t,s){const r={jsonrpc:"2.0",id:Date.now(),method:t,params:s??{}},e=new AbortController,o=setTimeout(()=>e.abort(),z);try{const n=await fetch(d,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${h}`,"User-Agent":"aerostack-gateway/0.15.8","X-Agent-Id":"aerostack-gateway","X-Bridge-Id":te,"X-Agent-Type":se},body:JSON.stringify(r),signal:e.signal});if(clearTimeout(o),(n.headers.get("content-type")??"").includes("text/event-stream")){const v=await n.text();return re(v,r.id)}return await n.json()}catch(n){clearTimeout(o);const a=n instanceof Error?n.message:"Unknown error";return n instanceof Error&&n.name==="AbortError"?{jsonrpc:"2.0",id:r.id,error:{code:-32603,message:"Request timed out"}}:{jsonrpc:"2.0",id:r.id,error:{code:-32603,message:`HTTP error: ${a}`}}}}function re(t,s){const r=t.split(`
8
- `);let e=null;for(const o of r)if(o.startsWith("data: "))try{e=JSON.parse(o.slice(6))}catch{}return e??{jsonrpc:"2.0",id:s,error:{code:-32603,message:"Empty SSE response"}}}const oe=new Set(["aerostack__guardian_report","aerostack__check_approval","aerostack__guardian_check"]);function ne(t,s){if(oe.has(t))return;let r="other";const e=t.toLowerCase();e.includes("exec")||e.includes("bash")||e.includes("shell")||e.includes("command")||e.includes("run")?r="exec_command":e.includes("write")||e.includes("edit")||e.includes("create")||e.includes("patch")?r="file_write":e.includes("delete")||e.includes("remove")||e.includes("trash")||e.includes("unlink")?r="file_delete":e.includes("fetch")||e.includes("http")||e.includes("request")||e.includes("api")||e.includes("get")||e.includes("post")?r="api_call":e.includes("install")||e.includes("package")||e.includes("npm")||e.includes("pip")?r="package_install":e.includes("config")||e.includes("setting")||e.includes("env")?r="config_change":e.includes("deploy")||e.includes("publish")||e.includes("release")?r="deploy":e.includes("send")||e.includes("message")||e.includes("email")||e.includes("notify")||e.includes("slack")||e.includes("telegram")?r="message_send":(e.includes("read")||e.includes("query")||e.includes("search")||e.includes("list")||e.includes("get"))&&(r="data_access");let o;try{const n=JSON.stringify(s);o=n.length>500?n.slice(0,500)+"...":n}catch{o="(unable to serialize)"}c("tools/call",{name:"aerostack__guardian_report",arguments:{action:`${t}(${Object.keys(s).join(", ")})`,category:r,risk_level:"low",details:o}}).catch(()=>{})}const ae=new Set(["aerostack__check_approval"]);async function ie(t,s){ne(t,s);const r=await c("tools/call",{name:t,arguments:s});if(r.error?.code===-32050){const n=r.error.data,a=n?.approval_id;if(!a||!/^[a-zA-Z0-9_-]{4,128}$/.test(a))return{jsonrpc:"2.0",id:r.id,error:{code:-32603,message:"Approval required but no approval_id returned"}};const l=u?.getLastActiveSession()??null;return i("Tool gate: returning pending, background resolver started",{tool:t,approvalId:a,sessionKey:l}),O({approvalId:a,toolName:t,toolArgs:s,sessionKey:l,gate:"tool_gate",wsUrl:n?.ws_url,pollUrl:n?.polling_url??`${d}/approval-status/${a}`,authToken:h}),{jsonrpc:"2.0",id:r.id,result:{content:[{type:"text",text:`This action requires workspace approval.
3
+ import{Server as q}from"@modelcontextprotocol/sdk/server/index.js";import{StdioServerTransport as V}from"@modelcontextprotocol/sdk/server/stdio.js";import{ListToolsRequestSchema as W,CallToolRequestSchema as G,ListResourcesRequestSchema as Y,ReadResourceRequestSchema as z,ListPromptsRequestSchema as B,GetPromptRequestSchema as F}from"@modelcontextprotocol/sdk/types.js";import{readFile as J,writeFile as Q}from"node:fs/promises";import{join as X}from"node:path";import{homedir as Z}from"node:os";import{resolveApproval as b}from"./resolution.js";import{startHookServer as ee,installClaudeHook as te,stopHookServer as se}from"./hook-server.js";import{OpenClawConnector as re,resolveOpenClawToken as oe}from"./openclaw-connector.js";import{info as c,warn as m,error as ne}from"./logger.js";const L=X(Z(),".openclaw","pre-authorized.json");async function ae(t){try{let s={};try{s=JSON.parse(await J(L,"utf-8"))}catch{}s[t]=Date.now(),await Q(L,JSON.stringify(s))}catch{}}const N=process.env.AEROSTACK_WORKSPACE_URL,l=process.env.AEROSTACK_TOKEN;function E(t,s,r){const e=parseInt(t??String(s),10);return Number.isFinite(e)&&e>=r?e:s}const P=E(process.env.AEROSTACK_APPROVAL_POLL_MS,3e3,500),I=E(process.env.AEROSTACK_APPROVAL_TIMEOUT_MS,864e5,5e3),x=E(process.env.AEROSTACK_REQUEST_TIMEOUT_MS,3e4,1e3),ie=process.env.AEROSTACK_HOOK_SERVER!=="false",ce=E(process.env.AEROSTACK_HOOK_PORT,18321,1024),le=process.env.AEROSTACK_HOOK_AUTO_INSTALL!=="false",ue=process.env.AEROSTACK_OPENCLAW_ENABLED!=="false",U=E(process.env.AEROSTACK_OPENCLAW_PORT,18789,1024),pe=process.env.AEROSTACK_OPENCLAW_TOKEN;N||(process.stderr.write(`ERROR: AEROSTACK_WORKSPACE_URL is required
4
+ `),process.exit(1)),l||(process.stderr.write(`ERROR: AEROSTACK_TOKEN is required
5
+ `),process.exit(1));let R;try{if(R=new URL(N),R.protocol!=="https:"&&R.protocol!=="http:")throw new Error("must be http or https")}catch{process.stderr.write(`ERROR: AEROSTACK_WORKSPACE_URL must be a valid HTTP(S) URL
6
+ `),process.exit(1)}R.protocol==="http:"&&!R.hostname.match(/^(localhost|127\.0\.0\.1)$/)&&process.stderr.write(`WARNING: Using HTTP (not HTTPS) \u2014 token will be sent in plaintext
7
+ `);const u=N.replace(/\/+$/,""),K=crypto.randomUUID();function de(){return process.env.AEROSTACK_AGENT_TYPE?process.env.AEROSTACK_AGENT_TYPE:process.env.CLAUDECODE||process.env.CLAUDE_CODE?"claude-code":process.env.MCP_TRANSPORT==="stdio"?"mcp-stdio":"unknown"}const D=de();let p=null,T=Date.now();const me=3e4;async function d(t,s){const r={jsonrpc:"2.0",id:Date.now(),method:t,params:s??{}},e=new AbortController,n=setTimeout(()=>e.abort(),x);try{const o=await fetch(u,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${l}`,"User-Agent":"aerostack-gateway/0.15.14","X-Agent-Id":"aerostack-gateway","X-Bridge-Id":K,"X-Agent-Type":D},body:JSON.stringify(r),signal:e.signal});if(clearTimeout(n),(o.headers.get("content-type")??"").includes("text/event-stream")){const w=await o.text();return he(w,r.id)}return await o.json()}catch(o){clearTimeout(n);const a=o instanceof Error?o.message:"Unknown error";return o instanceof Error&&o.name==="AbortError"?{jsonrpc:"2.0",id:r.id,error:{code:-32603,message:"Request timed out"}}:{jsonrpc:"2.0",id:r.id,error:{code:-32603,message:`HTTP error: ${a}`}}}}function he(t,s){const r=t.split(`
8
+ `);let e=null;for(const n of r)if(n.startsWith("data: "))try{e=JSON.parse(n.slice(6))}catch{}return e??{jsonrpc:"2.0",id:s,error:{code:-32603,message:"Empty SSE response"}}}const fe=new Set(["aerostack__guardian_report","aerostack__check_approval","aerostack__guardian_check"]);function we(t,s){if(fe.has(t))return;let r="other";const e=t.toLowerCase();e.includes("exec")||e.includes("bash")||e.includes("shell")||e.includes("command")||e.includes("run")?r="exec_command":e.includes("write")||e.includes("edit")||e.includes("create")||e.includes("patch")?r="file_write":e.includes("delete")||e.includes("remove")||e.includes("trash")||e.includes("unlink")?r="file_delete":e.includes("fetch")||e.includes("http")||e.includes("request")||e.includes("api")||e.includes("get")||e.includes("post")?r="api_call":e.includes("install")||e.includes("package")||e.includes("npm")||e.includes("pip")?r="package_install":e.includes("config")||e.includes("setting")||e.includes("env")?r="config_change":e.includes("deploy")||e.includes("publish")||e.includes("release")?r="deploy":e.includes("send")||e.includes("message")||e.includes("email")||e.includes("notify")||e.includes("slack")||e.includes("telegram")?r="message_send":(e.includes("read")||e.includes("query")||e.includes("search")||e.includes("list")||e.includes("get"))&&(r="data_access");let n;try{const o=JSON.stringify(s);n=o.length>500?o.slice(0,500)+"...":o}catch{n="(unable to serialize)"}d("tools/call",{name:"aerostack__guardian_report",arguments:{action:`${t}(${Object.keys(s).join(", ")})`,category:r,risk_level:"low",details:n}}).catch(()=>{})}const ge=new Set(["aerostack__check_approval"]),j=[{name:"aerostack__chat_check",description:"Check for new messages from the workspace owner. Call this periodically or when asked to check for instructions.",inputSchema:{type:"object",properties:{},required:[]}},{name:"aerostack__chat_reply",description:"Send a reply message to the workspace owner via Agent Chat.",inputSchema:{type:"object",properties:{message:{type:"string",description:"Your reply message to the workspace owner"}},required:["message"]}}],Ae=new Set(j.map(t=>t.name));function S(t){return t.replace(/\[/g,"(").replace(/\]/g,")")}async function k(t,s){const r=new AbortController,e=setTimeout(()=>r.abort(),x);try{return await fetch(t,{...s,signal:r.signal})}finally{clearTimeout(e)}}async function ye(){try{const s=await k(`${u}/chat/pending?since=${T}`,{headers:{Authorization:`Bearer ${l}`}});if(!s.ok)return[{type:"text",text:"No new messages."}];const e=(await s.json()).messages??[];return T=Date.now(),e.length===0?[{type:"text",text:"No new messages from the workspace owner."}]:[{type:"text",text:`Messages from workspace owner:
9
9
 
10
- Tool: ${t}
11
- Status: Pending review by workspace owner
10
+ ${e.map(o=>`${S(o.sender_name)}: ${S(o.content)}`).join(`
12
11
 
13
- The gateway will resume this task automatically once a decision is made. You may continue with other tasks in the meantime.`}]}}}const o=r.result?._meta;if(o?.approval_id&&o?.status==="pending"){const n=o.approval_id;if(!/^[a-zA-Z0-9_-]{4,128}$/.test(n))return r;const a=u?.getLastActiveSession()??null;return i("Permission gate: returning pending, background resolver started",{tool:t,approvalId:n,sessionKey:a}),O({approvalId:n,toolName:t,toolArgs:s,sessionKey:a,gate:"permission_gate",wsUrl:o.ws_url,pollUrl:o.polling_url??`${d}/approval-status/${n}`,authToken:h}),{jsonrpc:"2.0",id:r.id,result:{content:[{type:"text",text:`Permission request submitted for workspace review.
14
-
15
- Action: ${t}
16
- Status: Pending
17
-
18
- I'll be notified when the workspace owner decides. You may continue with other tasks.`}]}}}return r}function O(t){j({approvalId:t.approvalId,wsUrl:t.wsUrl,pollUrl:t.pollUrl,pollIntervalMs:B,timeoutMs:J,token:t.authToken}).then(async s=>{i("Approval resolved",{tool:t.toolName,status:s.status,session:t.sessionKey});const r=ce(t,s),e=t.sessionKey??u?.getLastActiveSession()??null;if((s.status==="approved"||s.status==="executed")&&e&&Y(e).catch(()=>{}),e&&u){if(await u.sendToSession(e,r)){i("Agent resumed via sessions.send",{session:e,status:s.status}),_(t,s.status,!0);return}w("sessions.send failed, session may have ended",{session:e}),_(t,s.status,!1)}else w("No session key available for resume",{tool:t.toolName,approvalId:t.approvalId}),_(t,s.status,!1)}).catch(s=>{w("Background approval resolver error",{error:s instanceof Error?s.message:String(s)})})}function _(t,s,r){const e=typeof t.toolArgs.action=="string"?t.toolArgs.action:null,o=e??t.toolName.replace(/^(?:\w+__)*aerostack__/,"")??t.toolName,n=e?`${o}`:`${o}(${Object.keys(t.toolArgs).join(", ")})`,a=r?"resume sent to agent":"session unavailable \u2014 resume not delivered";let l;s==="approved"||s==="executed"?l=r?`[RESUMED] ${n} \u2014 ${a}`:`[RESUME FAILED] ${n} \u2014 ${a}`:s==="rejected"?l=`[REJECTED] ${n} \u2014 rejection sent to agent`:s==="changes_requested"?l=`[CHANGES REQUESTED] ${n} \u2014 feedback sent to agent`:l=`[EXPIRED] ${n} \u2014 approval timed out, expiry sent to agent`,c("tools/call",{name:"aerostack__guardian_report",arguments:{action:l.slice(0,500),category:"approval",risk_level:"low",details:JSON.stringify({tool:t.toolName,approvalId:t.approvalId,status:s,delivered:r,session:t.sessionKey}).slice(0,500)}}).catch(()=>{})}function ce(t,s){const r=s.reviewer_note?.trim()||null,e=`${t.toolName}(${Object.keys(t.toolArgs).join(", ")})`;switch(s.status){case"approved":case"executed":return t.gate==="tool_gate"?["[APPROVAL RECEIVED] Your pending action has been approved.","",`Action: ${e}`,r?`Reviewer note: "${r}"`:null,"",`Please now execute this action: call ${t.toolName} with the same parameters as before.`].filter(o=>o!==null).join(`
19
- `):["[APPROVAL RECEIVED] Your request has been approved by the workspace owner.",r?`Reviewer note: "${r}"`:null,"","You may now proceed with the action you requested approval for."].filter(o=>o!==null).join(`
12
+ `)}`}]}catch{return[{type:"text",text:"Failed to check messages."}]}}async function _e(t){const s=typeof t.message=="string"?t.message.trim().slice(0,1e4):"";if(!s)return[{type:"text",text:"Error: message is required."}];try{const r=await k(`${u}/chat/messages`,{method:"POST",headers:{Authorization:`Bearer ${l}`,"Content-Type":"application/json"},body:JSON.stringify({content:s})});return r.ok?[{type:"text",text:"Reply sent to workspace owner."}]:[{type:"text",text:`Failed to send reply: ${(await r.json().catch(()=>({error:"Unknown error"}))).error||r.statusText}`}]}catch{return[{type:"text",text:"Failed to send reply."}]}}async function Ee(t,s){we(t,s);const r=await d("tools/call",{name:t,arguments:s});if(r.error?.code===-32050){const o=r.error.data,a=o?.approval_id;if(!a||!/^[a-zA-Z0-9_-]{4,128}$/.test(a))return{jsonrpc:"2.0",id:r.id,error:{code:-32603,message:"Approval required but no approval_id returned"}};c("Tool gate: blocking until approval resolves",{tool:t,approvalId:a});const i=await b({approvalId:a,wsUrl:o?.ws_url,pollUrl:o?.polling_url??`${u}/approval-status/${a}`,pollIntervalMs:P,timeoutMs:I,token:l});c("Tool gate resolved",{tool:t,status:i.status}),v({approvalId:a,toolName:t,toolArgs:s,sessionKey:null,gate:"tool_gate",pollUrl:"",authToken:l},i.status,!0);const w=$({approvalId:a,toolName:t,toolArgs:s,sessionKey:null,gate:"tool_gate",pollUrl:"",authToken:l},i);return{jsonrpc:"2.0",id:r.id,result:{content:[{type:"text",text:w}]}}}const n=r.result?._meta;if(n?.approval_id&&n?.status==="pending"){const o=n.approval_id;if(!/^[a-zA-Z0-9_-]{4,128}$/.test(o))return r;c("Permission gate: blocking until approval resolves",{tool:t,approvalId:o});const a=await b({approvalId:o,wsUrl:n.ws_url,pollUrl:n.polling_url??`${u}/approval-status/${o}`,pollIntervalMs:P,timeoutMs:I,token:l});c("Permission gate resolved",{tool:t,status:a.status}),v({approvalId:o,toolName:t,toolArgs:s,sessionKey:null,gate:"permission_gate",pollUrl:"",authToken:l},a.status,!0);const i=$({approvalId:o,toolName:t,toolArgs:s,sessionKey:null,gate:"permission_gate",pollUrl:"",authToken:l},a);return{jsonrpc:"2.0",id:r.id,result:{content:[{type:"text",text:i}]}}}return r}function De(t){b({approvalId:t.approvalId,wsUrl:t.wsUrl,pollUrl:t.pollUrl,pollIntervalMs:P,timeoutMs:I,token:t.authToken}).then(async s=>{c("Approval resolved",{tool:t.toolName,status:s.status,session:t.sessionKey});const r=$(t,s),e=t.sessionKey??p?.getLastActiveSession()??null;if((s.status==="approved"||s.status==="executed")&&e&&ae(e).catch(()=>{}),e&&p){if(await p.sendToSession(e,r)){c("Agent resumed via sessions.send",{session:e,status:s.status}),v(t,s.status,!0);return}m("sessions.send failed, falling back to channel notification",{session:e})}try{await h.notification({method:"notifications/claude/channel",params:{content:r,meta:{tool_name:t.toolName,approval_id:t.approvalId,status:s.status,gate:t.gate}}}),c("Agent resumed via channel notification",{tool:t.toolName,status:s.status}),v(t,s.status,!0)}catch(n){m("Channel notification failed",{error:n instanceof Error?n.message:String(n)}),v(t,s.status,!1)}}).catch(s=>{m("Background approval resolver error",{error:s instanceof Error?s.message:String(s)})})}function v(t,s,r){const e=typeof t.toolArgs.action=="string"?t.toolArgs.action:null,n=e??t.toolName.replace(/^(?:\w+__)*aerostack__/,"")??t.toolName,o=e?`${n}`:`${n}(${Object.keys(t.toolArgs).join(", ")})`,a=r?"resume sent to agent":"session unavailable \u2014 resume not delivered";let i;s==="approved"||s==="executed"?i=r?`[RESUMED] ${o} \u2014 ${a}`:`[RESUME FAILED] ${o} \u2014 ${a}`:s==="rejected"?i=`[REJECTED] ${o} \u2014 rejection sent to agent`:s==="changes_requested"?i=`[CHANGES REQUESTED] ${o} \u2014 feedback sent to agent`:i=`[EXPIRED] ${o} \u2014 approval timed out, expiry sent to agent`,d("tools/call",{name:"aerostack__guardian_report",arguments:{action:i.slice(0,500),category:"approval",risk_level:"low",details:JSON.stringify({tool:t.toolName,approvalId:t.approvalId,status:s,delivered:r,session:t.sessionKey}).slice(0,500)}}).catch(()=>{})}function $(t,s){const r=s.reviewer_note?.trim()||null,e=`${t.toolName}(${Object.keys(t.toolArgs).join(", ")})`;switch(s.status){case"approved":case"executed":return t.gate==="tool_gate"?["[APPROVAL RECEIVED] Your pending action has been approved.","",`Action: ${e}`,r?`Reviewer note: "${r}"`:null,"",`Please now execute this action: call ${t.toolName} with the same parameters as before.`].filter(n=>n!==null).join(`
13
+ `):["[APPROVAL RECEIVED] Your request has been approved by the workspace owner.",r?`Reviewer note: "${r}"`:null,"","You may now proceed with the action you requested approval for."].filter(n=>n!==null).join(`
20
14
  `);case"changes_requested":return["[CHANGES REQUESTED] The workspace owner reviewed your action and requested changes.","",`Original action: ${e}`,`Reviewer feedback: "${r??"No details provided"}"`,"","Please revise your approach based on this feedback and try again.","Do NOT proceed with the original plan."].join(`
21
15
  `);case"rejected":return["[REJECTED] The workspace owner denied this action.","",`Action: ${e}`,r?`Reason: "${r}"`:"No reason provided.","","Do not retry this action. If you believe this was a mistake, inform the user."].join(`
22
16
  `);case"expired":return[`[EXPIRED] The approval request for "${e}" timed out \u2014 no decision was made.`,"","You may resubmit the request if the action is still needed."].join(`
23
- `);default:return`[APPROVAL UPDATE] Status for "${e}": ${s.status}.`}}const E=`
17
+ `);default:return`[APPROVAL UPDATE] Status for "${e}": ${s.status}.`}}const C=`
24
18
  === AEROSTACK GUARDIAN POLICY (MANDATORY) ===
25
19
 
26
20
  You are operating under Aerostack workspace monitoring. These rules apply to ALL actions:
@@ -41,6 +35,10 @@ the result once the workspace owner approves. Simply wait; do not retry or proce
41
35
  These rules exist to protect the workspace owner. Violating them may result in your session
42
36
  being terminated. Compliance is non-negotiable.
43
37
  === END GUARDIAN POLICY ===
44
- `.trim(),le=["delete","remove","drop","truncate","destroy","wipe","reset","deploy","publish","release","push","exec","shell","bash","run","command","terminal","install","uninstall","send","email","notify","webhook"];function ue(t,s){const r=t.toLowerCase(),e=le.some(n=>r.includes(n)),o=s??"";return e?`[REQUIRES GUARDIAN APPROVAL] ${o}`.trim():o}let T=null;async function m(){if(T)return;const t=await c("initialize",{protocolVersion:"2024-11-05",capabilities:{},clientInfo:{name:"aerostack-gateway",version:"0.15.8"}});if(t.result){const s=t.result,r=s.instructions??"";T={protocolVersion:s.protocolVersion??"2024-11-05",instructions:r?`${r}
38
+ `.trim(),Re=["delete","remove","drop","truncate","destroy","wipe","reset","deploy","publish","release","push","exec","shell","bash","run","command","terminal","install","uninstall","send","email","notify","webhook"];function Te(t,s){const r=t.toLowerCase(),e=Re.some(o=>r.includes(o)),n=s??"";return e?`[REQUIRES GUARDIAN APPROVAL] ${n}`.trim():n}let H=null;async function A(){if(H)return;const t=await d("initialize",{protocolVersion:"2024-11-05",capabilities:{},clientInfo:{name:"aerostack-gateway",version:"0.15.14"}});if(t.result){const s=t.result,r=s.instructions??"";H={protocolVersion:s.protocolVersion??"2024-11-05",instructions:r?`${r}
45
39
 
46
- ${E}`:E}}}const p=new k({name:"aerostack-gateway",version:"0.15.8"},{capabilities:{tools:{},resources:{},prompts:{}},instructions:E});p.setRequestHandler(C,async()=>{await m();const t=await c("tools/list");if(t.error)throw new Error(t.error.message);return{tools:(t.result.tools??[]).filter(e=>!ae.has(e.name)).map(e=>({...e,description:ue(e.name,e.description)}))}}),p.setRequestHandler(N,async t=>{await m();const{name:s,arguments:r}=t.params,e=await ie(s,r??{});return e.error?{content:[{type:"text",text:`Error: ${e.error.message}`}],isError:!0}:{content:e.result.content??[{type:"text",text:JSON.stringify(e.result)}]}});const pe={uri:"aerostack://guardian/policy",name:"Aerostack Guardian Policy",description:"Mandatory workspace monitoring policy. Read this to understand required approval workflows.",mimeType:"text/plain"};p.setRequestHandler(I,async()=>{await m();const t=await c("resources/list");if(t.error)throw new Error(t.error.message);const s=t.result;return{resources:[pe,...s.resources??[]]}}),p.setRequestHandler(b,async t=>{if(await m(),t.params.uri==="aerostack://guardian/policy")return{contents:[{uri:t.params.uri,text:E,mimeType:"text/plain"}]};const s=await c("resources/read",{uri:t.params.uri});if(s.error)throw new Error(s.error.message);return{contents:s.result.contents??[]}}),p.setRequestHandler(L,async()=>{await m();const t=await c("prompts/list");if(t.error)throw new Error(t.error.message);return{prompts:t.result.prompts??[]}}),p.setRequestHandler(U,async t=>{await m();const s=await c("prompts/get",{name:t.params.name,arguments:t.params.arguments});if(s.error)throw new Error(s.error.message);return{messages:s.result.messages??[]}});async function de(){i("Connecting to workspace",{url:d});const t=new P;if(await p.connect(t),i("Ready",{url:d}),F)try{const r=await q(async e=>{try{const o=await fetch(`${d}/guardian-batch`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${h}`,"User-Agent":"aerostack-gateway/0.15.8","X-Agent-Id":"aerostack-gateway"},body:JSON.stringify({events:e})});return o.ok?(await o.json()).config?.hook_tracking??null:null}catch{return null}},Q);X&&await H(r)&&i("Claude Code hook auto-installed",{port:r})}catch(s){w("Hook server failed to start (non-fatal)",{error:s instanceof Error?s.message:String(s)})}if(Z)try{const s=ee??await G();s?(u=new M({port:y,token:s,rpcCall:c}),await u.connect()?i("OpenClaw connector started",{port:y}):(i("OpenClaw gateway not reachable, skipping connector"),u=null)):i("OpenClaw integration skipped (no token found)")}catch(s){w("OpenClaw connector failed (non-fatal)",{error:s instanceof Error?s.message:String(s)})}}function S(){u?.stop(),V(),process.exit(0)}process.on("SIGTERM",()=>{S()}),process.on("SIGINT",()=>{S()}),de().catch(t=>{W("Fatal error",{error:t instanceof Error?t.message:String(t)}),process.exit(1)});
40
+ ${C}`:C}}}const h=new q({name:"aerostack-gateway",version:"0.15.14"},{capabilities:{tools:{},resources:{},prompts:{},experimental:{"claude/channel":{}}},instructions:C});h.setRequestHandler(W,async()=>{await A();const t=await d("tools/list");if(t.error)throw new Error(t.error.message);const r=(t.result.tools??[]).filter(e=>!ge.has(e.name)).map(e=>({...e,description:Te(e.name,e.description)}));return r.push(...j),{tools:r}}),h.setRequestHandler(G,async t=>{await A();const{name:s,arguments:r}=t.params;if(Ae.has(s))return{content:(s==="aerostack__chat_check"?await ye():await _e(r??{})).map(i=>({...i,type:"text"}))};const e=await Ee(s,r??{});if(e.error)return{content:[{type:"text",text:`Error: ${e.error.message}`}],isError:!0};const o=e.result.content??[{type:"text",text:JSON.stringify(e.result)}];if(Date.now()-T>me)try{const i=await k(`${u}/chat/pending?since=${T}`,{headers:{Authorization:`Bearer ${l}`}});if(i.ok){const y=(await i.json()).messages??[];if(T=Date.now(),y.length>0){const g=y.map(f=>`${S(f.sender_name)}: ${S(f.content)}`).join(`
41
+ `);o.unshift({type:"text",text:`--- WORKSPACE OWNER MESSAGE ---
42
+ ${g}
43
+ --- END MESSAGE \u2014 please acknowledge ---`})}}}catch{}return{content:o}});const ve={uri:"aerostack://guardian/policy",name:"Aerostack Guardian Policy",description:"Mandatory workspace monitoring policy. Read this to understand required approval workflows.",mimeType:"text/plain"};h.setRequestHandler(Y,async()=>{await A();const t=await d("resources/list");if(t.error)throw new Error(t.error.message);const s=t.result;return{resources:[ve,...s.resources??[]]}}),h.setRequestHandler(z,async t=>{if(await A(),t.params.uri==="aerostack://guardian/policy")return{contents:[{uri:t.params.uri,text:C,mimeType:"text/plain"}]};const s=await d("resources/read",{uri:t.params.uri});if(s.error)throw new Error(s.error.message);return{contents:s.result.contents??[]}}),h.setRequestHandler(B,async()=>{await A();const t=await d("prompts/list");if(t.error)throw new Error(t.error.message);return{prompts:t.result.prompts??[]}}),h.setRequestHandler(F,async t=>{await A();const s=await d("prompts/get",{name:t.params.name,arguments:t.params.arguments});if(s.error)throw new Error(s.error.message);return{messages:s.result.messages??[]}});async function Oe(){c("Connecting to workspace",{url:u});const t=new V;await h.connect(t),c("Ready",{url:u});const s=()=>{k(`${u}/chat/heartbeat`,{method:"POST",headers:{Authorization:`Bearer ${l}`,"Content-Type":"application/json"},body:JSON.stringify({agent_type:D,user_agent:"aerostack-gateway/0.15.14",bridge_id:K})}).catch(()=>{})};s();const r=setInterval(s,12e4);if(process.on("exit",()=>clearInterval(r)),ie)try{const n=await ee(async o=>{try{const a=await fetch(`${u}/guardian-batch`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${l}`,"User-Agent":"aerostack-gateway/0.15.14","X-Agent-Id":"aerostack-gateway"},body:JSON.stringify({events:o})});return a.ok?(await a.json()).config?.hook_tracking??null:null}catch{return null}},ce);le&&await te(n)&&c("Claude Code hook auto-installed",{port:n})}catch(e){m("Hook server failed to start (non-fatal)",{error:e instanceof Error?e.message:String(e)})}if(ue)try{const e=pe??await oe();e?(p=new re({port:U,token:e,rpcCall:d}),await p.connect()?c("OpenClaw connector started",{port:U}):(c("OpenClaw gateway not reachable, skipping connector"),p=null)):c("OpenClaw integration skipped (no token found)")}catch(e){m("OpenClaw connector failed (non-fatal)",{error:e instanceof Error?e.message:String(e)})}Se()}let O=null;function Se(){O&&O.abort(),O=new AbortController;const t=async()=>{try{const e=await fetch(u,{method:"GET",headers:{Authorization:`Bearer ${l}`,Accept:"text/event-stream"},signal:O.signal});if(!e.ok||!e.body){m("Chat event listener: failed to connect",{status:e.status}),r();return}c("Chat event listener connected");const n=e.body.getReader(),o=new TextDecoder;let a="";for(;;){const{done:i,value:w}=await n.read();if(i)break;a+=o.decode(w,{stream:!0});const y=a.split(`
44
+ `);a=y.pop()??"";let g="",f="";for(const _ of y)_.startsWith("event: ")?g=_.slice(7).trim():_.startsWith("data: ")?f=_.slice(6):_===""&&g&&f&&(g==="aerostack_event"&&ke(f),g="",f="")}c("Chat event listener: stream ended, reconnecting"),r()}catch(e){if(e?.name==="AbortError")return;m("Chat event listener error",{error:e?.message}),r()}};let s=null;const r=()=>{s||(s=setTimeout(()=>{s=null,t()},5e3))};t()}async function ke(t){try{const s=JSON.parse(t);if(s.type!=="chat_message"||!s.content)return;if(c("Chat message received from admin",{sender:s.sender_name,sessionId:s.session_id}),p){const r=p.getLastActiveSession();r?await p.sendToSession(r,`[Workspace Owner \u2014 ${s.sender_name??"Admin"}]: ${s.content}`)?c("Chat message delivered to OpenClaw",{sessionKey:r}):m("Chat message delivery failed \u2014 session not available"):m("Chat message received but no active OpenClaw session")}}catch{}}function M(){O?.abort(),p?.stop(),se(),process.exit(0)}process.on("SIGTERM",()=>{M()}),process.on("SIGINT",()=>{M()}),Oe().catch(t=>{ne("Fatal error",{error:t instanceof Error?t.message:String(t)}),process.exit(1)});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aerostack/gateway",
3
- "version": "0.15.13",
3
+ "version": "0.15.15",
4
4
  "description": "stdio-to-HTTP bridge connecting any MCP client to Aerostack Workspaces",
5
5
  "author": "Aerostack",
6
6
  "license": "MIT",