grix-connector 3.19.4 → 3.19.6

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 g from"node:http";import q from"node:https";import{buildRequestOptions as m}from"../utils/index.js";import{captureBuffer as o}from"../capture/index.js";const y=["authorization","x-api-key","api-key","anthropic-api-key","x-goog-api-key"];function _(e){return Array.isArray(e)?e.join(", "):typeof e=="number"?String(e):e}function p(e,r){if(!e)return;const a=r.toLowerCase();for(const[d,c]of Object.entries(e))if(d.toLowerCase()===a)return _(c)}function H(e){if(!e)return{};for(const r of y){const a=p(e,r);if(!a)continue;const d=r==="authorization"&&a.toLowerCase().startsWith("bearer ")?a.slice(7).trim():a;return{name:r,value:a,apiKey:d}}return{}}function A(e,r){const a=r.headers,d=H(a);e.captureWriter.append({type:"http_upstream_route",requestId:e.requestId,connectionId:e.connectionId,routeSource:e.routeDiagnostics?.routeSource??"direct",routeKey:e.routeDiagnostics?.routeKey,routeSessionId:e.routeDiagnostics?.routeSessionId,routeTargetBaseUrl:e.routeDiagnostics?.routeTargetBaseUrl,routeModel:e.routeDiagnostics?.routeModel,modelOverride:e.modelOverride,upstreamProtocol:e.target.protocol,upstreamHost:e.target.host,upstreamPort:e.target.port,upstreamPath:e.path,upstreamUrl:`${e.target.protocol}//${e.target.host}${e.target.protocol==="https:"&&e.target.port===443||e.target.protocol==="http:"&&e.target.port===80?"":`:${e.target.port}`}${e.path}`,upstreamAuthHeaderName:d.name,upstreamAuthHeaderValue:d.value,upstreamApiKey:d.apiKey,upstreamHeaders:a})}function D(e,r){r.length!==0&&o(e.captureWriter,{type:"http_request_chunk",requestId:e.requestId,connectionId:e.connectionId},r)}function M(e){return/\/models\/?$/.test(e.split("?")[0])}function S(e){try{const r=JSON.parse(e.toString("utf-8"));return r.models&&Array.isArray(r.models)?null:r.object==="list"&&Array.isArray(r.data)?Buffer.from(JSON.stringify({...r,models:r.data}),"utf-8"):null}catch{return null}}async function W(e,r,a){let d=0;const c=m({target:e.target,method:e.request.method??"GET",path:e.path,headers:a?.headers??e.request.headers,headerOverrides:e.headerOverrides});A(e,c);const I=e.target.protocol==="https:"?q:g,l=M(e.path),u=I.request(c,t=>{if(e.captureWriter.append({type:"http_response_start",requestId:e.requestId,connectionId:e.connectionId,statusCode:t.statusCode??0,statusMessage:t.statusMessage??"",headers:t.headers}),e.events.emitResponseHeaders({requestId:e.requestId,status:t.statusCode??0,headers:t.headers}),l&&t.statusCode===200){const s=[];t.on("data",n=>{s.push(n),d+=n.length,o(e.captureWriter,{type:"http_response_chunk",requestId:e.requestId,connectionId:e.connectionId},n)}),t.on("end",()=>{const n=Buffer.concat(s),h=S(n),i=h??n,f={...t.headers};h&&(f["content-length"]=String(i.length)),e.response.writeHead(t.statusCode??200,f),e.response.end(i),e.captureWriter.append({type:"http_response_end",requestId:e.requestId,connectionId:e.connectionId}),e.events.emitRequestEnd({requestId:e.requestId,durationMs:Date.now()-r,responseBytes:d})});return}e.response.writeHead(t.statusCode??502,t.headers),t.on("data",s=>{d+=s.length,o(e.captureWriter,{type:"http_response_chunk",requestId:e.requestId,connectionId:e.connectionId},s)}),t.on("end",()=>{e.captureWriter.append({type:"http_response_end",requestId:e.requestId,connectionId:e.connectionId}),e.events.emitRequestEnd({requestId:e.requestId,durationMs:Date.now()-r,responseBytes:d})}),t.pipe(e.response)});if(u.on("error",t=>{e.captureWriter.append({type:"http_request_error",requestId:e.requestId,connectionId:e.connectionId,error:t.message}),e.events.emitRequestEnd({requestId:e.requestId,durationMs:Date.now()-r,responseBytes:d,error:t.message}),e.response.headersSent||e.response.writeHead(502),e.response.end(t.message)}),e.request.on("aborted",()=>{u.destroy()}),e.response.on("close",()=>{u.destroyed||u.destroy()}),a){u.end(a.body);return}e.request.on("data",t=>{D(e,t)}),e.request.pipe(u)}export{W as forwardPassthrough};
1
+ import p from"node:http";import _ from"node:https";import{buildRequestOptions as H}from"../utils/index.js";import{captureBuffer as h}from"../capture/index.js";import{logProxyRuntimeStderr as S}from"../server/logger.js";const b=["authorization","x-api-key","api-key","anthropic-api-key","x-goog-api-key"];function A(e){return Array.isArray(e)?e.join(", "):typeof e=="number"?String(e):e}function w(e,t){if(!e)return;const a=t.toLowerCase();for(const[d,u]of Object.entries(e))if(d.toLowerCase()===a)return A(u)}function $(e){if(!e)return{};for(const t of b){const a=w(e,t);if(!a)continue;const d=t==="authorization"&&a.toLowerCase().startsWith("bearer ")?a.slice(7).trim():a;return{name:t,value:a,apiKey:d}}return{}}function C(e,t){const a=t.headers,d=$(a);e.captureWriter.append({type:"http_upstream_route",requestId:e.requestId,connectionId:e.connectionId,routeSource:e.routeDiagnostics?.routeSource??"direct",routeKey:e.routeDiagnostics?.routeKey,routeSessionId:e.routeDiagnostics?.routeSessionId,routeTargetBaseUrl:e.routeDiagnostics?.routeTargetBaseUrl,routeModel:e.routeDiagnostics?.routeModel,modelOverride:e.modelOverride,upstreamProtocol:e.target.protocol,upstreamHost:e.target.host,upstreamPort:e.target.port,upstreamPath:e.path,upstreamUrl:`${e.target.protocol}//${e.target.host}${e.target.protocol==="https:"&&e.target.port===443||e.target.protocol==="http:"&&e.target.port===80?"":`:${e.target.port}`}${e.path}`,upstreamAuthHeaderName:d.name,upstreamAuthHeaderValue:d.value,upstreamApiKey:d.apiKey,upstreamHeaders:a})}function D(e,t){t.length!==0&&h(e.captureWriter,{type:"http_request_chunk",requestId:e.requestId,connectionId:e.connectionId},t)}function E(e){return/\/models\/?$/.test(e.split("?")[0])}function M(e){try{const t=JSON.parse(e.toString("utf-8"));return t.models&&Array.isArray(t.models)?null:t.object==="list"&&Array.isArray(t.data)?Buffer.from(JSON.stringify({...t,models:t.data}),"utf-8"):null}catch{return null}}async function P(e,t,a){let d=0,u=!1;const i=H({target:e.target,method:e.request.method??"GET",path:e.path,headers:a?.headers??e.request.headers,headerOverrides:e.headerOverrides});C(e,i);const q=e.target.protocol==="https:"?_:p,y=E(e.path),f=r=>{u||(u=!0,e.events.emitRequestEnd({requestId:e.requestId,durationMs:Date.now()-t,responseBytes:d,...r?{error:r}:{}}))},l=()=>{e.captureWriter.append({type:"http_response_end",requestId:e.requestId,connectionId:e.connectionId}),f()},o=(r,s)=>{u||(e.captureWriter.append({type:r,requestId:e.requestId,connectionId:e.connectionId,error:s,responseBytes:d}),S("warn",`passthrough ${r} requestId=${e.requestId} routeKey=${e.routeDiagnostics?.routeKey??"direct"} upstream=${e.target.protocol}//${e.target.host}${e.path}: ${s}`),f(s))},c=q.request(i,r=>{if(e.captureWriter.append({type:"http_response_start",requestId:e.requestId,connectionId:e.connectionId,statusCode:r.statusCode??0,statusMessage:r.statusMessage??"",headers:r.headers}),e.events.emitResponseHeaders({requestId:e.requestId,status:r.statusCode??0,headers:r.headers}),y&&r.statusCode===200){const s=[];r.on("data",n=>{s.push(n),d+=n.length,h(e.captureWriter,{type:"http_response_chunk",requestId:e.requestId,connectionId:e.connectionId},n)}),r.on("end",()=>{const n=Buffer.concat(s),g=M(n),m=g??n,I={...r.headers};g&&(I["content-length"]=String(m.length)),e.response.writeHead(r.statusCode??200,I),e.response.end(m),l()}),r.on("error",n=>{o("http_response_error",n.message),e.response.headersSent||e.response.writeHead(502),e.response.writableEnded||e.response.end(n.message)});return}e.response.writeHead(r.statusCode??502,r.headers),r.on("data",s=>{d+=s.length,h(e.captureWriter,{type:"http_response_chunk",requestId:e.requestId,connectionId:e.connectionId},s)}),r.on("end",()=>{l()}),r.on("error",s=>{o("http_response_error",s.message),e.response.headersSent||e.response.writeHead(502),e.response.writableEnded||e.response.end(s.message)}),r.pipe(e.response)});if(c.on("error",r=>{o("http_request_error",r.message),e.response.headersSent||e.response.writeHead(502),e.response.writableEnded||e.response.end(r.message)}),e.request.on("aborted",()=>{o("http_request_aborted","client request aborted before upstream completed"),c.destroy()}),e.response.on("close",()=>{u||(o("http_response_close","client response closed before upstream completed"),c.destroyed||c.destroy())}),a){c.end(a.body);return}e.request.on("data",r=>{D(e,r)}),e.request.pipe(c)}export{P as forwardPassthrough};
@@ -1,17 +1,17 @@
1
- import{randomUUID as d}from"node:crypto";import g from"node:http";import S from"node:net";import H from"node:tls";import{GRIX_PROXY_ROUTE_KEY_HEADER as _,parseConnectAuthority as P,resolveAbsoluteRequestTarget as x,resolveMitmRequestTarget as R,resolveRuntimeRoutedRequestTarget as E}from"../routing/index.js";import{forwardHttpRequest as f}from"../forwarding/index.js";import{CODEX_OPENAI_WIRE_COMPAT_HEADER as w,CODEX_OPENAI_WIRE_COMPAT_MODE as I}from"../compat/index.js";import{handleWebSocketUpgrade as C}from"./websocket-handler.js";import{logProxyRuntimeStderr as m}from"./logger.js";const y=1e4;function W(c){if(!c||!c.startsWith("http://")&&!c.startsWith("https://"))return null;try{return new URL(c).hostname}catch{return null}}function b(c){let t;try{t=new URL(c,"http://grix.internal").pathname}catch{t=c}return t.includes("/messages")||t.includes("/chat/completions")||t.includes("/responses")}function M(c){let t;try{t=new URL(c,"http://grix.internal").pathname}catch{t=c}return t.endsWith("/responses")}function v(c){return c?{routeSource:c.pinned?"pinned":"workspace",routeKey:c.routeKey,...c.sessionId?{routeSessionId:c.sessionId}:{},routeTargetBaseUrl:c.route.targetBaseUrl,...c.route.model?{routeModel:c.route.model}:{}}:{routeSource:"direct"}}class X{captureWriter;certificateAuthority;events;runtimeRoutes;interceptHosts;proxyServer;mitmHttpServer;mitmPlainHttpServer;blindTunnelSockets=new Set;constructor(t,r,a,i,n=null){this.captureWriter=t,this.certificateAuthority=r,this.events=a,this.runtimeRoutes=i,this.interceptHosts=n,this.proxyServer=g.createServer((o,e)=>{this.handlePlainHttpRequest(o,e)}),this.proxyServer.on("connect",(o,e,s)=>{this.handleConnectRequest(o,e,s)}),this.proxyServer.on("clientError",(o,e)=>{m("warn","proxy client error:",o.message),e.end(`HTTP/1.1 400 Bad Request\r
1
+ import{randomUUID as l}from"node:crypto";import v from"node:http";import R from"node:net";import x from"node:tls";import{GRIX_PROXY_ROUTE_KEY_HEADER as E,parseConnectAuthority as w,resolveAbsoluteRequestTarget as I,resolveMitmRequestTarget as C,resolveRuntimeRoutedRequestTarget as W}from"../routing/index.js";import{forwardHttpRequest as _}from"../forwarding/index.js";import{CODEX_OPENAI_WIRE_COMPAT_HEADER as b,CODEX_OPENAI_WIRE_COMPAT_MODE as M}from"../compat/index.js";import{handleWebSocketUpgrade as O}from"./websocket-handler.js";import{logProxyRuntimeStderr as m}from"./logger.js";const S=1e4;function U(c){if(!c||!c.startsWith("http://")&&!c.startsWith("https://"))return null;try{return new URL(c).hostname}catch{return null}}function A(c){let t;try{t=new URL(c,"http://grix.internal").pathname}catch{t=c}return t.includes("/messages")||t.includes("/chat/completions")||t.includes("/responses")}function B(c){let t;try{t=new URL(c,"http://grix.internal").pathname}catch{t=c}return t.endsWith("/responses")}function T(c){return c?{routeSource:c.pinned?"pinned":"workspace",routeKey:c.routeKey,...c.sessionId?{routeSessionId:c.sessionId}:{},routeTargetBaseUrl:c.route.targetBaseUrl,...c.route.model?{routeModel:c.route.model}:{}}:{routeSource:"direct"}}class ${captureWriter;certificateAuthority;events;runtimeRoutes;interceptHosts;proxyServer;mitmHttpServer;mitmPlainHttpServer;blindTunnelSockets=new Set;constructor(t,o,a,s,n=null){this.captureWriter=t,this.certificateAuthority=o,this.events=a,this.runtimeRoutes=s,this.interceptHosts=n,this.proxyServer=v.createServer((r,e)=>{this.handlePlainHttpRequest(r,e)}),this.proxyServer.on("connect",(r,e,i)=>{this.handleConnectRequest(r,e,i)}),this.proxyServer.on("clientError",(r,e)=>{m("warn","proxy client error:",r.message),e.end(`HTTP/1.1 400 Bad Request\r
2
2
  \r
3
- `)}),this.mitmHttpServer=g.createServer((o,e)=>{this.handleMitmHttpRequest(o,e)}),this.mitmHttpServer.on("upgrade",(o,e,s)=>{this.handleMitmUpgrade(o,e,s)}),this.mitmHttpServer.on("clientError",(o,e)=>{m("warn","mitm client error:",o.message),e.end(`HTTP/1.1 400 Bad Request\r
3
+ `)}),this.mitmHttpServer=v.createServer((r,e)=>{this.handleMitmHttpRequest(r,e)}),this.mitmHttpServer.on("upgrade",(r,e,i)=>{this.handleMitmUpgrade(r,e,i)}),this.mitmHttpServer.on("clientError",(r,e)=>{m("warn","mitm client error:",r.message),e.end(`HTTP/1.1 400 Bad Request\r
4
4
  \r
5
- `)}),this.mitmPlainHttpServer=g.createServer((o,e)=>{this.handleMitmHttpRequest(o,e)}),this.mitmPlainHttpServer.on("upgrade",(o,e,s)=>{this.handleMitmUpgrade(o,e,s)}),this.mitmPlainHttpServer.on("clientError",(o,e)=>{m("warn","mitm plain client error:",o.message),e.end(`HTTP/1.1 400 Bad Request\r
5
+ `)}),this.mitmPlainHttpServer=v.createServer((r,e)=>{this.handleMitmHttpRequest(r,e)}),this.mitmPlainHttpServer.on("upgrade",(r,e,i)=>{this.handleMitmUpgrade(r,e,i)}),this.mitmPlainHttpServer.on("clientError",(r,e)=>{m("warn","mitm plain client error:",r.message),e.end(`HTTP/1.1 400 Bad Request\r
6
6
  \r
7
- `)})}async start(t,r="0.0.0.0"){await new Promise((i,n)=>{this.proxyServer.once("error",n),this.proxyServer.listen(t??0,r,()=>{this.proxyServer.off("error",n),i()})});const a=this.proxyServer.address();if(!a||typeof a=="string")throw new Error("Proxy server failed to resolve listen port");return{port:a.port}}async stop(){this.mitmHttpServer.close(),this.mitmPlainHttpServer.close(),await new Promise(t=>{this.proxyServer.close(()=>t());for(const r of this.blindTunnelSockets)r.destroy();this.blindTunnelSockets.clear(),this.mitmHttpServer.closeAllConnections?.(),this.mitmPlainHttpServer.closeAllConnections?.(),this.proxyServer.closeAllConnections?.()})}async handlePlainHttpRequest(t,r){const a=d();let i=this.runtimeRoutes.resolveRequestRouteInfo(t);if(i&&this.interceptHosts&&!t.headers[_]){const u=W(t.url);u&&!this.interceptHosts.matches(u)&&(i=null)}if(!i&&t.url&&!t.url.startsWith("http://")&&!t.url.startsWith("https://")){r.writeHead(502,{"content-type":"text/plain"}),r.end("Direct HTTP requests without a matched route are not supported on the MITM port. Use the reverse proxy port instead.");return}const{target:n,path:o,headerOverrides:e,modelOverride:s,modelMapOverride:h,passthrough:p}=i?E(t,i.route):{...x(t),headerOverrides:void 0,modelOverride:void 0,modelMapOverride:void 0,passthrough:void 0};this.captureWriter.append({type:"http_request_start",requestId:a,connectionId:a,targetHost:n.host,targetPort:n.port,protocol:n.protocol,method:t.method??"GET",path:o,headers:t.headers}),await f({requestId:a,connectionId:a,target:n,path:o,headerOverrides:e,modelOverride:s,modelMapOverride:h,passthrough:p,request:t,response:r,captureWriter:this.captureWriter,events:this.events,routeDiagnostics:v(i)})}async handleConnectRequest(t,r,a){const i=d();let n=t.url??"",o=443;try{const e=P(t.url??"");if(n=e.host,o=e.port,this.captureWriter.append({type:"connect_start",connectionId:i,targetHost:e.host,targetPort:e.port}),this.interceptHosts&&!this.interceptHosts.matches(e.host)){this.blindTunnel(r,e,a,i);return}r.write(`HTTP/1.1 200 Connection Established\r
7
+ `)})}async start(t,o="0.0.0.0"){await new Promise((s,n)=>{this.proxyServer.once("error",n),this.proxyServer.listen(t??0,o,()=>{this.proxyServer.off("error",n),s()})});const a=this.proxyServer.address();if(!a||typeof a=="string")throw new Error("Proxy server failed to resolve listen port");return{port:a.port}}async stop(){this.mitmHttpServer.close(),this.mitmPlainHttpServer.close(),await new Promise(t=>{this.proxyServer.close(()=>t());for(const o of this.blindTunnelSockets)o.destroy();this.blindTunnelSockets.clear(),this.mitmHttpServer.closeAllConnections?.(),this.mitmPlainHttpServer.closeAllConnections?.(),this.proxyServer.closeAllConnections?.()})}async handlePlainHttpRequest(t,o){const a=l();let s=this.runtimeRoutes.resolveRequestRouteInfo(t);if(s&&this.interceptHosts&&!t.headers[E]){const u=U(t.url);u&&!this.interceptHosts.matches(u)&&(s=null)}if(!s&&t.url&&!t.url.startsWith("http://")&&!t.url.startsWith("https://")){o.writeHead(502,{"content-type":"text/plain"}),o.end("Direct HTTP requests without a matched route are not supported on the MITM port. Use the reverse proxy port instead.");return}const{target:n,path:r,headerOverrides:e,modelOverride:i,modelMapOverride:d,passthrough:p}=s?W(t,s.route):{...I(t),headerOverrides:void 0,modelOverride:void 0,modelMapOverride:void 0,passthrough:void 0};this.captureWriter.append({type:"http_request_start",requestId:a,connectionId:a,targetHost:n.host,targetPort:n.port,protocol:n.protocol,method:t.method??"GET",path:r,headers:t.headers}),await _({requestId:a,connectionId:a,target:n,path:r,headerOverrides:e,modelOverride:i,modelMapOverride:d,passthrough:p,request:t,response:o,captureWriter:this.captureWriter,events:this.events,routeDiagnostics:T(s)})}async handleConnectRequest(t,o,a){const s=l();let n=t.url??"",r=443;try{const e=w(t.url??"");if(n=e.host,r=e.port,this.captureWriter.append({type:"connect_start",connectionId:s,targetHost:e.host,targetPort:e.port}),this.interceptHosts&&!this.interceptHosts.matches(e.host)){this.blindTunnel(o,e,a,s);return}o.write(`HTTP/1.1 200 Connection Established\r
8
8
  \r
9
- `),a.length>0&&r.unshift(a);const s=p=>{this.captureWriter.append({type:"connect_error",connectionId:i,targetHost:e.host,error:p.message}),this.events.emitConnectError({requestId:i,host:e.host,port:e.port,error:p.message})};r.on("error",s);const h=()=>{const p=r.read(1);if(!p||p.length===0){r.once("readable",h);return}if(r.unshift(p),p[0]===22)this.startTlsMitm({socket:r,target:e,connectionId:i,reportConnectError:s});else{const l=r;l.__grixConnectionId=i,l.__grixProxyTarget={...e,protocol:"http:"},l.on("error",s),this.mitmPlainHttpServer.emit("connection",l)}};h()}catch(e){const s=e instanceof Error?e.message:String(e);this.captureWriter.append({type:"connect_error",connectionId:i,targetHost:n,error:s}),this.events.emitConnectError({requestId:i,host:n,port:o,error:s}),r.end(`HTTP/1.1 502 Bad Gateway\r
9
+ `),a.length>0&&o.unshift(a);const i=p=>{this.captureWriter.append({type:"connect_error",connectionId:s,targetHost:e.host,error:p.message}),this.events.emitConnectError({requestId:s,host:e.host,port:e.port,error:p.message})};o.on("error",i);const d=()=>{const p=o.read(1);if(!p||p.length===0){o.once("readable",d);return}if(o.unshift(p),p[0]===22)this.startTlsMitm({socket:o,target:e,connectionId:s,reportConnectError:i});else{const h=o;h.__grixConnectionId=s,h.__grixProxyTarget={...e,protocol:"http:"},h.on("error",i),this.mitmPlainHttpServer.emit("connection",h)}};d()}catch(e){const i=e instanceof Error?e.message:String(e);this.captureWriter.append({type:"connect_error",connectionId:s,targetHost:n,error:i}),this.events.emitConnectError({requestId:s,host:n,port:r,error:i}),o.end(`HTTP/1.1 502 Bad Gateway\r
10
10
  \r
11
- `)}}blindTunnel(t,r,a,i){const n=S.connect(r.port,r.host);let o=!1,e=!1,s;const h=p=>{o||(o=!0,clearTimeout(s),this.captureWriter.append({type:"connect_error",connectionId:i,targetHost:r.host,error:p.message}),this.events.emitConnectError({requestId:i,host:r.host,port:r.port,error:p.message}),e?t.destroy():t.end(`HTTP/1.1 502 Bad Gateway\r
11
+ `)}}blindTunnel(t,o,a,s){const n=R.connect(o.port,o.host);let r=!1,e=!1,i;const d=p=>{r||(r=!0,clearTimeout(i),this.captureWriter.append({type:"connect_error",connectionId:s,targetHost:o.host,error:p.message}),this.events.emitConnectError({requestId:s,host:o.host,port:o.port,error:p.message}),e?t.destroy():t.end(`HTTP/1.1 502 Bad Gateway\r
12
12
  \r
13
- `),n.destroy())};s=setTimeout(()=>{h(new Error(`blind tunnel connect timeout after ${y}ms`))},y),n.once("connect",()=>{e=!0,clearTimeout(s),t.write(`HTTP/1.1 200 Connection Established\r
13
+ `),n.destroy())};i=setTimeout(()=>{d(new Error(`blind tunnel connect timeout after ${S}ms`))},S),n.once("connect",()=>{e=!0,clearTimeout(i),t.write(`HTTP/1.1 200 Connection Established\r
14
14
  \r
15
- `),a.length>0&&n.write(a),t.pipe(n),n.pipe(t)}),n.on("error",h),t.on("error",h),this.blindTunnelSockets.add(t).add(n),t.on("close",()=>{this.blindTunnelSockets.delete(t),n.destroy()}),n.on("close",()=>{this.blindTunnelSockets.delete(n),t.destroy()})}async startTlsMitm(t){const{socket:r,target:a,connectionId:i,reportConnectError:n}=t;try{const o=await this.certificateAuthority.getLeafCertificate(a.host),e=new H.TLSSocket(r,{isServer:!0,secureContext:o.secureContext,ALPNProtocols:["http/1.1"]});e.__grixConnectionId=i,e.__grixProxyTarget=a,e.once("secure",()=>{this.mitmHttpServer.emit("connection",e)}),e.on("error",n)}catch(o){n(o instanceof Error?o:new Error(String(o))),r.destroy()}}async handleMitmHttpRequest(t,r){const a=t.socket,i=a.__grixProxyTarget,n=a.__grixConnectionId??d();if(!i){r.writeHead(502),r.end("Missing MITM target");return}const o=d(),e=this.runtimeRoutes.resolveRequestRouteInfo(t,i.host);if(!e){this.captureWriter.append({type:"http_request_start",requestId:o,connectionId:n,targetHost:i.host,targetPort:i.port,protocol:i.protocol,method:t.method??"GET",path:t.url??"",headers:t.headers}),m("error",`intercepted ${i.host} but no relay route is configured; refusing to fall back to the real endpoint (that would silently use the agent's own account). Reconfigure Grix relay for this agent.`),t.resume(),r.writeHead(502,{"content-type":"text/plain"}),r.end("grix relay: intercepted host has no route; refusing silent direct connect");return}const{target:s,path:h,headerOverrides:p,modelOverride:u,modelMapOverride:l,passthrough:T}=R({request:t,fallbackTarget:i,route:e.route});if(e?.route.relayOnly&&!b(h)){this.captureWriter.append({type:"http_request_start",requestId:o,connectionId:n,targetHost:s.host,targetPort:s.port,protocol:s.protocol,method:t.method??"GET",path:h,headers:t.headers}),t.resume(),r.writeHead(404,{"content-type":"text/plain"}),r.end("relay: non-inference endpoint blocked");return}e?.route.codexResponsesToChat&&(t.method??"GET").toUpperCase()==="POST"&&M(h)&&(t.headers[w]=I),this.captureWriter.append({type:"http_request_start",requestId:o,connectionId:n,targetHost:s.host,targetPort:s.port,protocol:s.protocol,method:t.method??"GET",path:h,headers:t.headers}),await f({requestId:o,connectionId:n,target:s,path:h,headerOverrides:p,modelOverride:u,modelMapOverride:l,passthrough:T,request:t,response:r,captureWriter:this.captureWriter,events:this.events,routeDiagnostics:v(e)})}handleMitmUpgrade(t,r,a){const i=r,n=i.__grixProxyTarget;if((n?this.runtimeRoutes.resolveRequestRouteInfo(t,n.host):null)?.route.relayOnly){this.captureWriter.append({type:"websocket_upgrade_error",requestId:d(),connectionId:i.__grixConnectionId??d(),targetHost:n.host,error:"relay-only route: refusing direct WebSocket upgrade to origin"}),m("warn",`relay-only route active for ${n.host}; refusing WebSocket upgrade so the client falls back to HTTPS`),r.end(`HTTP/1.1 404 Not Found\r
15
+ `),a.length>0&&n.write(a),t.pipe(n),n.pipe(t)}),n.on("error",d),t.on("error",d),this.blindTunnelSockets.add(t).add(n),t.on("close",()=>{this.blindTunnelSockets.delete(t),n.destroy()}),n.on("close",()=>{this.blindTunnelSockets.delete(n),t.destroy()})}async startTlsMitm(t){const{socket:o,target:a,connectionId:s,reportConnectError:n}=t;try{const r=await this.certificateAuthority.getLeafCertificate(a.host),e=new x.TLSSocket(o,{isServer:!0,secureContext:r.secureContext,ALPNProtocols:["http/1.1"]});e.__grixConnectionId=s,e.__grixProxyTarget=a,e.once("secure",()=>{this.mitmHttpServer.emit("connection",e)}),e.on("error",n)}catch(r){n(r instanceof Error?r:new Error(String(r))),o.destroy()}}async handleMitmHttpRequest(t,o){const a=Date.now(),s=t.socket,n=s.__grixProxyTarget,r=s.__grixConnectionId??l();if(!n){o.writeHead(502),o.end("Missing MITM target");return}const e=l(),i=this.runtimeRoutes.resolveRequestRouteInfo(t,n.host);if(!i){this.captureWriter.append({type:"http_request_start",requestId:e,connectionId:r,targetHost:n.host,targetPort:n.port,protocol:n.protocol,method:t.method??"GET",path:t.url??"",headers:t.headers}),m("error",`intercepted ${n.host} but no relay route is configured; refusing to fall back to the real endpoint (that would silently use the agent's own account). Reconfigure Grix relay for this agent.`),t.resume(),o.writeHead(502,{"content-type":"text/plain"}),o.end("grix relay: intercepted host has no route; refusing silent direct connect");return}const{target:d,path:p,headerOverrides:u,modelOverride:h,modelMapOverride:H,passthrough:P}=C({request:t,fallbackTarget:n,route:i.route});if(i?.route.relayOnly&&!A(p)){const g=T(i),y="relay: non-inference endpoint blocked",f={"content-type":"text/plain"};this.captureWriter.append({type:"http_request_start",requestId:e,connectionId:r,targetHost:d.host,targetPort:d.port,protocol:d.protocol,method:t.method??"GET",path:p,headers:t.headers}),this.captureWriter.append({type:"http_relay_blocked",requestId:e,connectionId:r,routeKey:g.routeKey,routeSource:g.routeSource,routeTargetBaseUrl:g.routeTargetBaseUrl,reason:"relayOnly_non_inference_endpoint",path:p}),this.captureWriter.append({type:"http_response_start",requestId:e,connectionId:r,statusCode:404,statusMessage:"Not Found",headers:f}),this.events.emitResponseHeaders({requestId:e,status:404,headers:f}),this.captureWriter.append({type:"http_response_chunk",requestId:e,connectionId:r,encoding:"utf8",data:y}),this.captureWriter.append({type:"http_response_end",requestId:e,connectionId:r}),this.events.emitRequestEnd({requestId:e,durationMs:Date.now()-a,responseBytes:Buffer.byteLength(y)}),t.resume(),o.writeHead(404,f),o.end(y);return}i?.route.codexResponsesToChat&&(t.method??"GET").toUpperCase()==="POST"&&B(p)&&(t.headers[b]=M),this.captureWriter.append({type:"http_request_start",requestId:e,connectionId:r,targetHost:d.host,targetPort:d.port,protocol:d.protocol,method:t.method??"GET",path:p,headers:t.headers}),await _({requestId:e,connectionId:r,target:d,path:p,headerOverrides:u,modelOverride:h,modelMapOverride:H,passthrough:P,request:t,response:o,captureWriter:this.captureWriter,events:this.events,routeDiagnostics:T(i)})}handleMitmUpgrade(t,o,a){const s=o,n=s.__grixProxyTarget;if((n?this.runtimeRoutes.resolveRequestRouteInfo(t,n.host):null)?.route.relayOnly){this.captureWriter.append({type:"websocket_upgrade_error",requestId:l(),connectionId:s.__grixConnectionId??l(),targetHost:n.host,error:"relay-only route: refusing direct WebSocket upgrade to origin"}),m("warn",`relay-only route active for ${n.host}; refusing WebSocket upgrade so the client falls back to HTTPS`),o.end(`HTTP/1.1 404 Not Found\r
16
16
  \r
17
- `);return}C(this.captureWriter,this.events,t,r,a)}}export{X as AcpTrafficProxyServer};
17
+ `);return}O(this.captureWriter,this.events,t,o,a)}}export{$ as AcpTrafficProxyServer};
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};
@@ -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
- 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.19.4",
3
+ "version": "3.19.6",
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",