grix-connector 4.3.1 → 4.3.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.
- package/dist/adapter/claude/claude-bridge-server.js +1 -1
- package/dist/adapter/claude/claude-tools.js +1 -1
- package/dist/adapter/claude/claude-worker-client.js +1 -1
- package/dist/adapter/claude/mcp-http-launcher.js +2 -2
- package/dist/adapter/claude/result-timeout.js +1 -1
- package/dist/adapter/deepseek-harness/bridge-installer.js +5 -5
- package/dist/adapter/deepseek-harness/profile-supervisor.js +2 -2
- package/dist/assets/dsh-bridge/{grix-dsh-bridge-4.3.1.tgz → grix-dsh-bridge-4.3.2.tgz} +0 -0
- package/dist/assets/dsh-bridge/manifest.json +5 -5
- package/dist/core/access/allowlist-store.js +1 -1
- package/dist/core/file-ops/list-files.js +1 -1
- package/dist/log.js +2 -2
- package/dist/mcp/stream-http/config.js +1 -1
- package/dist/mcp/stream-http/connection-binding.js +1 -1
- package/dist/mcp/stream-http/security.js +1 -1
- package/dist/mcp/stream-http/tool-executor.js +1 -1
- package/dist/mcp/stream-http/tool-registry.js +1 -1
- package/dist/mcp/stream-http/tool-schemas.js +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
import c from"node:http";import{randomUUID as d}from"node:crypto";import{log as o}from"../../core/log/index.js";function l(t){t.writeHead(401,{"content-type":"application/json"}),t.end(JSON.stringify({error:"unauthorized"}))}function u(t){t.writeHead(404,{"content-type":"application/json"}),t.end(JSON.stringify({error:"not_found"}))}function h(t,e){t.writeHead(400,{"content-type":"application/json"}),t.end(JSON.stringify({error:e}))}function p(t,e={ok:!0}){t.writeHead(200,{"content-type":"application/json"}),t.end(JSON.stringify(e))}async function v(t){const e=[];for await(const
|
|
1
|
+
import c from"node:http";import{randomUUID as d}from"node:crypto";import{log as o}from"../../core/log/index.js";function l(t){t.writeHead(401,{"content-type":"application/json"}),t.end(JSON.stringify({error:"unauthorized"}))}function u(t){t.writeHead(404,{"content-type":"application/json"}),t.end(JSON.stringify({error:"not_found"}))}function h(t,e){t.writeHead(400,{"content-type":"application/json"}),t.end(JSON.stringify({error:e}))}function p(t,e={ok:!0}){t.writeHead(200,{"content-type":"application/json"}),t.end(JSON.stringify(e))}async function v(t){const e=[];for await(const n of t)e.push(n);const r=Buffer.concat(e).toString("utf8").trim();return r?JSON.parse(r):{}}function k(t){const e=(t.headers.authorization??"").trim();return e.toLowerCase().startsWith("bearer ")?e.slice(7).trim():""}class w{host="127.0.0.1";port=0;token;callbacks;server=null;address=null;constructor(e){this.token=d(),this.callbacks=e}getToken(){return this.token}getURL(){return this.address?`http://${this.address.address}:${this.address.port}`:""}async start(){this.server||(this.server=c.createServer(async(e,r)=>{try{await this.handleRequest(e,r)}catch(n){h(r,n instanceof Error?n.message:String(n))}}),await new Promise((e,r)=>{this.server.once("error",r),this.server.listen(this.port,this.host,()=>{this.server.off("error",r),e()})}),this.address=this.server.address(),o.info("claude-bridge",`Bridge server listening on ${this.getURL()}`))}async stop(){if(!this.server)return;const e=this.server;this.server=null,this.address=null,e.closeIdleConnections?.(),e.closeAllConnections?.(),await new Promise((r,n)=>{e.close(s=>s?n(s):r())})}async handleRequest(e,r){if(k(e)!==this.token){l(r);return}if(e.method!=="POST"){r.writeHead(405,{"content-type":"application/json"}),r.end(JSON.stringify({error:"method_not_allowed"}));return}const n=new URL(e.url,"http://localhost").pathname,s=await v(e),i=f.get(n);if(!i){u(r);return}const a=await i(this.callbacks,s);p(r,a??{ok:!0})}}const f=new Map([["/v1/worker/register",async(t,e)=>(o.info("claude-bridge",`Worker registered: ${e.worker_id} (pid=${e.pid})`),t.onRegisterWorker(e))],["/v1/worker/status",async(t,e)=>(o.info("claude-bridge",`Worker status: ${e.status}`),t.onStatusUpdate(e))],["/v1/worker/send-text",async(t,e)=>t.onSendText(e)],["/v1/worker/send-stream-chunk",async(t,e)=>t.onSendStreamChunk(e)],["/v1/worker/send-media",async(t,e)=>t.onSendMedia(e)],["/v1/worker/delete-message",async(t,e)=>t.onDeleteMessage(e)],["/v1/worker/ack-event",async(t,e)=>t.onAckEvent(e)],["/v1/worker/event-result",async(t,e)=>t.onSendEventResult(e)],["/v1/worker/event-stop-ack",async(t,e)=>t.onSendEventStopAck(e)],["/v1/worker/event-stop-result",async(t,e)=>t.onSendEventStopResult(e)],["/v1/worker/session-composing",async(t,e)=>t.onSetSessionComposing(e)],["/v1/worker/agent-invoke",async(t,e)=>t.onAgentInvoke(e)],["/v1/worker/local-action-result",async(t,e)=>t.onLocalActionResult(e)]]);export{w as ClaudeBridgeServer};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{randomUUID as x}from"node:crypto";import{CallToolRequestSchema as S,ListToolsRequestSchema as w}from"@modelcontextprotocol/sdk/types.js";import{log as f}from"../../core/log/index.js";import{toolCallToInvoke as k}from"../../core/mcp/tools.js";const E=new Set(["contact_search","session_search","message_history","message_search","group_create","group_detail_read","group_leave_self","group_member_add","group_member_remove","group_member_role_update","group_all_members_muted_update","group_member_speaking_update","group_dissolve","send_msg","delete_msg","agent_api_create","agent_category_list","agent_category_create","agent_category_update","agent_category_assign","agent_api_key_rotate"]),y=3e4,I=[{name:"reply",description:"Send a visible message back to the chat for this grix-claude event.",inputSchema:{type:"object",properties:{text:{type:"string",description:"The visible reply text to send."},chat_id:{type:"string",description:"The target chat/session id from the <channel> tag."},event_id:{type:"string",description:"The Aibot event_id from the <channel> tag."},reply_to:{type:"string",description:"Optional message_id to quote instead of the inbound trigger message."},files:{type:"array",items:{type:"string"},description:"Optional absolute local file paths. Each file is uploaded through Agent API OSS presign before sending."},final:{type:"boolean",description:"Whether this is the final reply for the event. Defaults to false \u2014 the event stays open while Claude continues working, and auto-completes after inactivity. Set true only when this is definitively the last message for the event."}},required:["chat_id","event_id"]}},{name:"complete",description:"Finish an event without sending a visible reply so the backend does not time out.",inputSchema:{type:"object",properties:{event_id:{type:"string",description:"The Aibot event_id from the <channel> tag."},status:{type:"string",enum:["responded","canceled","failed"]},code:{type:"string"},msg:{type:"string"}},required:["event_id","status"]}},{name:"delete_message",description:"Delete a previously sent message in the same grix-claude chat.",inputSchema:{type:"object",properties:{chat_id:{type:"string"},message_id:{type:"string"}},required:["chat_id","message_id"]}},{name:"status",description:"Show grix-claude runtime status, upstream access state, bridge health, and startup hints.",inputSchema:{type:"object",properties:{}}},{name:"send",description:"Send a message to a chat session proactively, without requiring an inbound event. Use for notifications or scheduled reports.",inputSchema:{type:"object",properties:{chat_id:{type:"string",description:"The target chat/session id."},text:{type:"string",description:"The message text to send."}},required:["chat_id","text"]}},{name:"access_pair",description:"Forward a sender pairing approval code to upstream access control.",inputSchema:{type:"object",properties:{code:{type:"string"}},required:["code"]}},{name:"access_deny",description:"Forward a sender pairing denial code to upstream access control.",inputSchema:{type:"object",properties:{code:{type:"string"}},required:["code"]}},{name:"allow_sender",description:"Ask upstream access control to allow a sender_id.",inputSchema:{type:"object",properties:{sender_id:{type:"string"}},required:["sender_id"]}},{name:"remove_sender",description:"Ask upstream access control to remove a sender_id.",inputSchema:{type:"object",properties:{sender_id:{type:"string"}},required:["sender_id"]}},{name:"access_policy",description:"Ask upstream access control to update the sender access policy.",inputSchema:{type:"object",properties:{policy:{type:"string",enum:["allowlist","open","disabled"]}},required:["policy"]}},{name:"grix_query",description:"Search contacts, sessions, message history, or messages by keyword in the Grix/AIBot platform.",inputSchema:{type:"object",properties:{action:{type:"string",enum:["contact_search","session_search","message_history","message_search"]},keyword:{type:"string"},id:{type:"string"},sessionId:{type:"string"},limit:{type:"number"},offset:{type:"number"},beforeId:{type:"string"}},required:["action"]}},{name:"grix_group",description:"Manage groups in the Grix/AIBot platform: create, get details, leave, dissolve, manage members and permissions.",inputSchema:{type:"object",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"},memberIds:{type:"array",items:{type:"string"}},memberTypes:{type:"array",items:{type:"integer",enum:[1,2]}},memberId:{type:"string"},role:{type:"integer",enum:[1,2]},memberType:{type:"integer",description:"Member type (for update_member_role / update_member_speaking)."},allMembersMuted:{type:"boolean"},isSpeakMuted:{type:"boolean"},canSpeakWhenAllMuted:{type:"boolean",description:"Allow speaking when all muted (for update_member_speaking)."}},required:["action"]}},{name:"grix_message_send",description:"Send a message to a session in the Grix/AIBot platform.",inputSchema:{type:"object",properties:{sessionId:{type:"string"},content:{type:"string"},msgType:{type:"number"},quotedMessageId:{type:"string"},threadId:{type:"string"}},required:["sessionId","content"]}},{name:"grix_message_unsend",description:"Recall/unsend a message in the Grix/AIBot platform.",inputSchema:{type:"object",properties:{sessionId:{type:"string"},msgId:{type:"string"}},required:["sessionId","msgId"]}},{name:"grix_admin",description:"Agent and category management in the Grix/AIBot platform: create agents, manage categories, rotate API keys.",inputSchema:{type:"object",properties:{action:{type:"string",enum:["create_agent","list_categories","create_category","update_category","assign_category","rotate_api_key"]},agentId:{type:"string"},agentName:{type:"string"},introduction:{type:"string"},isMain:{type:"boolean"},categoryId:{type:"string"},name:{type:"string"},parentId:{type:"string"},sortOrder:{type:"number"}},required:["action"]}}];function q(r,e){r.setRequestHandler(w,async()=>({tools:I})),r.setRequestHandler(S,async i=>{const{name:n,arguments:t}=i.params,s=t??{};try{switch(n){case"reply":return await A(s,e);case"complete":return await $(s,e);case"delete_message":return await T(s,e);case"status":return j(e);case"send":return await M(s,e);case"access_pair":case"access_deny":case"allow_sender":case"remove_sender":case"access_policy":return await R(n,s,e);case"grix_query":case"grix_group":case"grix_message_send":case"grix_message_unsend":case"grix_admin":return await O(n,s,e);default:return{content:[{type:"text",text:`Unknown tool: ${n}`}],isError:!0}}}catch(a){return f.error("claude-tools",`Tool ${n} error: ${a}`),{content:[{type:"text",text:`Error: ${a instanceof Error?a.message:String(a)}`}],isError:!0}}})}async function A(r,e){const i=e.getActiveEvent();if(!i)return{content:[{type:"text",text:"No active event to reply to"}],isError:!0};const n=String(r.text??""),t=String(r.chat_id??""),s=String(r.event_id??i.eventId),a=r.reply_to,d=r.files,_=r.final===!0;if(!t||!s)return{content:[{type:"text",text:"reply requires chat_id and event_id"}],isError:!0};if(!n.trim()&&(!d||d.length===0))return{content:[{type:"text",text:"reply requires at least one of text or files"}],isError:!0};const{text:g,quotedMessageId:v}=e.resolveQuotedMessageId(a,n),b=[];let m=0;const h=`reply_${s}_${Date.now()}`;try{if(g){const p=e.splitText(g);for(let o=0;o<p.length;o++){if(!e.isEventActive(s))return c("ignored: event no longer active");m++,e.bridge.sendStreamChunk(s,t,p[o],++i.chunkSeq,!1,h)}}if(d&&d.length>0)for(const p of d){if(!e.isEventActive(s))return c("ignored: event no longer active");m++;const o=await e.uploadFile(p,t),l=`${x()}_${m}`;e.bridge.sendMedia(s,t,o.access_url,o.file_name,v,l,o.extra),f.info("claude-tools",`File sent: ${o.file_name}`)}e.bridge.sendStreamChunk(s,t,"",++i.chunkSeq,!0,h)}catch(p){if(g&&b.length===0)try{const o=`fallback_${s}_${Date.now()}`,l=e.splitText(g);for(let u=0;u<l.length;u++)e.bridge.sendStreamChunk("",t,l[u],u+1,!1,o);return e.bridge.sendStreamChunk("",t,"",l.length+1,!0,o),e.markReplySent(s),_&&e.finalizeEvent(s,"responded"),c("sent via fallback")}catch{}if(!e.isEventActive(s))return c("ignored: event no longer active");throw e.bridge.sendEventResult(s,"failed",String(p),"send_msg_failed"),p}return e.markReplySent(s),_?e.finalizeEvent(s,"responded"):(i.responded=!0,e.clearActiveEvent("completed")),c("Reply sent")}async function $(r,e){const i=e.getActiveEvent(),n=String(r.event_id??""),t=r.status??"",s=r.code,a=r.msg;if(!n||!t)return{content:[{type:"text",text:"complete requires event_id and status"}],isError:!0};const d=["responded","canceled","failed"];return d.includes(t)?e.isEventActive(n)?(e.bridge.sendEventResult(n,t,a,s),e.clearActiveEvent(t),c("Event completed")):c("ignored: event no longer active"):{content:[{type:"text",text:`status must be one of: ${d.join(", ")}`}],isError:!0}}async function T(r,e){const i=String(r.chat_id??""),n=String(r.message_id??"");if(!i||!n)return{content:[{type:"text",text:"chat_id and message_id are required"}],isError:!0};try{return await e.bridge.agentInvoke("grix_message_unsend",{sessionId:i,msgId:n},y),c(`deleted (${n})`)}catch(t){return{content:[{type:"text",text:`Delete failed: ${t}`}],isError:!0}}}function j(r){const e=r.getStatusInfo();return{content:[{type:"text",text:JSON.stringify({alive:e.alive,active_event:e.activeEvent,pending_approvals:e.pendingPermissions,pending_questions:e.pendingElicitations})}]}}async function M(r,e){const i=String(r.chat_id??""),n=String(r.text??"");if(!i||!n)return{content:[{type:"text",text:"chat_id and text are required"}],isError:!0};try{const t=e.splitText(n),s=`send_${i}_${Date.now()}`;for(let a=0;a<t.length;a++)e.bridge.sendStreamChunk("",i,t[a],a+1,!1,s);return e.bridge.sendStreamChunk("",i,"",t.length+1,!0,s),c("sent")}catch(t){return{content:[{type:"text",text:`Send failed: ${t}`}],isError:!0}}}const C={access_pair:{verb:"pair_approve",payloadKey:"code"},access_deny:{verb:"pair_deny",payloadKey:"code"},allow_sender:{verb:"sender_allow",payloadKey:"sender_id"},remove_sender:{verb:"sender_remove",payloadKey:"sender_id"},access_policy:{verb:"policy_set",payloadKey:"policy"}};async function R(r,e,i){try{const n=C[r];if(!n)throw new Error(`Unknown access control tool: ${r}`);const t={};e.code!=null&&(t.code=e.code),e.sender_id!=null&&(t.sender_id=e.sender_id),e.policy!=null&&(t.policy=e.policy);const s=await i.bridge.agentInvoke("claude_access_control",{verb:n.verb,payload:t},y);return{content:[{type:"text",text:typeof s=="string"?s:JSON.stringify(s)}]}}catch(n){return{content:[{type:"text",text:`${r} failed: ${n}`}],isError:!0}}}async function O(r,e,i){try{const n=k(r,e);if(!E.has(n.action))throw new Error(`Action not allowed: ${n.action}`);const t=await i.bridge.agentInvoke(n.action,n.params,y);if(t&&Number(t.code??0)!==0)throw new Error(String(t.msg??"invoke failed"));return{content:[{type:"text",text:t?.data!=null?typeof t.data=="string"?t.data:JSON.stringify(t.data):JSON.stringify(t)}]}}catch(n){return{content:[{type:"text",text:`${r} failed: ${n}`}],isError:!0}}}function c(r){return{content:[{type:"text",text:r}]}}export{q as registerClaudeTools};
|
|
1
|
+
import{randomUUID as x}from"node:crypto";import{CallToolRequestSchema as S,ListToolsRequestSchema as w}from"@modelcontextprotocol/sdk/types.js";import{log as f}from"../../core/log/index.js";import{toolCallToInvoke as k}from"../../core/mcp/tools.js";const E=new Set(["contact_search","session_search","message_history","message_search","group_create","group_detail_read","group_leave_self","group_member_add","group_member_remove","group_member_role_update","group_all_members_muted_update","group_member_speaking_update","group_dissolve","send_msg","delete_msg","agent_api_create","agent_category_list","agent_category_create","agent_category_update","agent_category_assign","agent_api_key_rotate"]),y=3e4,I=[{name:"reply",description:"Send a visible message back to the chat for this grix-claude event.",inputSchema:{type:"object",properties:{text:{type:"string",description:"The visible reply text to send."},chat_id:{type:"string",description:"The target chat/session id from the <channel> tag."},event_id:{type:"string",description:"The Aibot event_id from the <channel> tag."},reply_to:{type:"string",description:"Optional message_id to quote instead of the inbound trigger message."},files:{type:"array",items:{type:"string"},description:"Optional absolute local file paths. Each file is uploaded through Agent API OSS presign before sending."},final:{type:"boolean",description:"Whether this is the final reply for the event. Defaults to false \u2014 the event stays open while Claude continues working, and auto-completes after inactivity. Set true only when this is definitively the last message for the event."}},required:["chat_id","event_id"]}},{name:"complete",description:"Finish an event without sending a visible reply so the backend does not time out.",inputSchema:{type:"object",properties:{event_id:{type:"string",description:"The Aibot event_id from the <channel> tag."},status:{type:"string",enum:["responded","canceled","failed"]},code:{type:"string"},msg:{type:"string"}},required:["event_id","status"]}},{name:"delete_message",description:"Delete a previously sent message in the same grix-claude chat.",inputSchema:{type:"object",properties:{chat_id:{type:"string"},message_id:{type:"string"}},required:["chat_id","message_id"]}},{name:"status",description:"Show grix-claude runtime status, upstream access state, bridge health, and startup hints.",inputSchema:{type:"object",properties:{}}},{name:"send",description:"Send a message to a chat session proactively, without requiring an inbound event. Use for notifications or scheduled reports.",inputSchema:{type:"object",properties:{chat_id:{type:"string",description:"The target chat/session id."},text:{type:"string",description:"The message text to send."}},required:["chat_id","text"]}},{name:"access_pair",description:"Forward a sender pairing approval code to upstream access control.",inputSchema:{type:"object",properties:{code:{type:"string"}},required:["code"]}},{name:"access_deny",description:"Forward a sender pairing denial code to upstream access control.",inputSchema:{type:"object",properties:{code:{type:"string"}},required:["code"]}},{name:"allow_sender",description:"Ask upstream access control to allow a sender_id.",inputSchema:{type:"object",properties:{sender_id:{type:"string"}},required:["sender_id"]}},{name:"remove_sender",description:"Ask upstream access control to remove a sender_id.",inputSchema:{type:"object",properties:{sender_id:{type:"string"}},required:["sender_id"]}},{name:"access_policy",description:"Ask upstream access control to update the sender access policy.",inputSchema:{type:"object",properties:{policy:{type:"string",enum:["allowlist","open","disabled"]}},required:["policy"]}},{name:"grix_query",description:"Search contacts, sessions, message history, or messages by keyword in the Grix/AIBot platform.",inputSchema:{type:"object",properties:{action:{type:"string",enum:["contact_search","session_search","message_history","message_search"]},keyword:{type:"string"},id:{type:"string"},sessionId:{type:"string"},limit:{type:"number"},offset:{type:"number"},beforeId:{type:"string"}},required:["action"]}},{name:"grix_group",description:"Manage groups in the Grix/AIBot platform: create, get details, leave, dissolve, manage members and permissions.",inputSchema:{type:"object",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"},memberIds:{type:"array",items:{type:"string"}},memberTypes:{type:"array",items:{type:"integer",enum:[1,2]}},memberId:{type:"string"},role:{type:"integer",enum:[1,2]},memberType:{type:"integer",description:"Member type (for update_member_role / update_member_speaking)."},allMembersMuted:{type:"boolean"},isSpeakMuted:{type:"boolean"},canSpeakWhenAllMuted:{type:"boolean",description:"Allow speaking when all muted (for update_member_speaking)."}},required:["action"]}},{name:"grix_message_send",description:"Send a message to a session in the Grix/AIBot platform.",inputSchema:{type:"object",properties:{sessionId:{type:"string"},content:{type:"string"},msgType:{type:"number"},quotedMessageId:{type:"string"},threadId:{type:"string"}},required:["sessionId","content"]}},{name:"grix_message_unsend",description:"Recall/unsend a message in the Grix/AIBot platform.",inputSchema:{type:"object",properties:{sessionId:{type:"string"},msgId:{type:"string"}},required:["sessionId","msgId"]}},{name:"grix_admin",description:"Agent and category management in the Grix/AIBot platform: create agents, manage categories, rotate API keys.",inputSchema:{type:"object",properties:{action:{type:"string",enum:["create_agent","list_categories","create_category","update_category","assign_category","rotate_api_key"]},agentId:{type:"string"},agentName:{type:"string"},introduction:{type:"string"},isMain:{type:"boolean"},categoryId:{type:"string"},name:{type:"string"},parentId:{type:"string"},sortOrder:{type:"number"}},required:["action"]}}];function q(n,e){n.setRequestHandler(w,async()=>({tools:I})),n.setRequestHandler(S,async i=>{const{name:r,arguments:t}=i.params,s=t??{};try{switch(r){case"reply":return await A(s,e);case"complete":return await $(s,e);case"delete_message":return await T(s,e);case"status":return j(e);case"send":return await M(s,e);case"access_pair":case"access_deny":case"allow_sender":case"remove_sender":case"access_policy":return await R(r,s,e);case"grix_query":case"grix_group":case"grix_message_send":case"grix_message_unsend":case"grix_admin":return await O(r,s,e);default:return{content:[{type:"text",text:`Unknown tool: ${r}`}],isError:!0}}}catch(a){return f.error("claude-tools",`Tool ${r} error: ${a}`),{content:[{type:"text",text:`Error: ${a instanceof Error?a.message:String(a)}`}],isError:!0}}})}async function A(n,e){const i=e.getActiveEvent();if(!i)return{content:[{type:"text",text:"No active event to reply to"}],isError:!0};const r=String(n.text??""),t=String(n.chat_id??""),s=String(n.event_id??i.eventId),a=n.reply_to,d=n.files,_=n.final===!0;if(!t||!s)return{content:[{type:"text",text:"reply requires chat_id and event_id"}],isError:!0};if(!r.trim()&&(!d||d.length===0))return{content:[{type:"text",text:"reply requires at least one of text or files"}],isError:!0};const{text:g,quotedMessageId:v}=e.resolveQuotedMessageId(a,r),b=[];let m=0;const h=`reply_${s}_${Date.now()}`;try{if(g){const p=e.splitText(g);for(let o=0;o<p.length;o++){if(!e.isEventActive(s))return c("ignored: event no longer active");m++,e.bridge.sendStreamChunk(s,t,p[o],++i.chunkSeq,!1,h)}}if(d&&d.length>0)for(const p of d){if(!e.isEventActive(s))return c("ignored: event no longer active");m++;const o=await e.uploadFile(p,t),l=`${x()}_${m}`;e.bridge.sendMedia(s,t,o.access_url,o.file_name,v,l,o.extra),f.info("claude-tools",`File sent: ${o.file_name}`)}e.bridge.sendStreamChunk(s,t,"",++i.chunkSeq,!0,h)}catch(p){if(g&&b.length===0)try{const o=`fallback_${s}_${Date.now()}`,l=e.splitText(g);for(let u=0;u<l.length;u++)e.bridge.sendStreamChunk("",t,l[u],u+1,!1,o);return e.bridge.sendStreamChunk("",t,"",l.length+1,!0,o),e.markReplySent(s),_&&e.finalizeEvent(s,"responded"),c("sent via fallback")}catch{}if(!e.isEventActive(s))return c("ignored: event no longer active");throw e.bridge.sendEventResult(s,"failed",String(p),"send_msg_failed"),p}return e.markReplySent(s),_?e.finalizeEvent(s,"responded"):(i.responded=!0,e.clearActiveEvent("completed")),c("Reply sent")}async function $(n,e){const i=e.getActiveEvent(),r=String(n.event_id??""),t=n.status??"",s=n.code,a=n.msg;if(!r||!t)return{content:[{type:"text",text:"complete requires event_id and status"}],isError:!0};const d=["responded","canceled","failed"];return d.includes(t)?e.isEventActive(r)?(e.bridge.sendEventResult(r,t,a,s),e.clearActiveEvent(t),c("Event completed")):c("ignored: event no longer active"):{content:[{type:"text",text:`status must be one of: ${d.join(", ")}`}],isError:!0}}async function T(n,e){const i=String(n.chat_id??""),r=String(n.message_id??"");if(!i||!r)return{content:[{type:"text",text:"chat_id and message_id are required"}],isError:!0};try{return await e.bridge.agentInvoke("grix_message_unsend",{sessionId:i,msgId:r},y),c(`deleted (${r})`)}catch(t){return{content:[{type:"text",text:`Delete failed: ${t}`}],isError:!0}}}function j(n){const e=n.getStatusInfo();return{content:[{type:"text",text:JSON.stringify({alive:e.alive,active_event:e.activeEvent,pending_approvals:e.pendingPermissions,pending_questions:e.pendingElicitations})}]}}async function M(n,e){const i=String(n.chat_id??""),r=String(n.text??"");if(!i||!r)return{content:[{type:"text",text:"chat_id and text are required"}],isError:!0};try{const t=e.splitText(r),s=`send_${i}_${Date.now()}`;for(let a=0;a<t.length;a++)e.bridge.sendStreamChunk("",i,t[a],a+1,!1,s);return e.bridge.sendStreamChunk("",i,"",t.length+1,!0,s),c("sent")}catch(t){return{content:[{type:"text",text:`Send failed: ${t}`}],isError:!0}}}const C={access_pair:{verb:"pair_approve",payloadKey:"code"},access_deny:{verb:"pair_deny",payloadKey:"code"},allow_sender:{verb:"sender_allow",payloadKey:"sender_id"},remove_sender:{verb:"sender_remove",payloadKey:"sender_id"},access_policy:{verb:"policy_set",payloadKey:"policy"}};async function R(n,e,i){try{const r=C[n];if(!r)throw new Error(`Unknown access control tool: ${n}`);const t={};e.code!=null&&(t.code=e.code),e.sender_id!=null&&(t.sender_id=e.sender_id),e.policy!=null&&(t.policy=e.policy);const s=await i.bridge.agentInvoke("claude_access_control",{verb:r.verb,payload:t},y);return{content:[{type:"text",text:typeof s=="string"?s:JSON.stringify(s)}]}}catch(r){return{content:[{type:"text",text:`${n} failed: ${r}`}],isError:!0}}}async function O(n,e,i){try{const r=k(n,e);if(!E.has(r.action))throw new Error(`Action not allowed: ${r.action}`);const t=await i.bridge.agentInvoke(r.action,r.params,y);if(t&&Number(t.code??0)!==0)throw new Error(String(t.msg??"invoke failed"));return{content:[{type:"text",text:t?.data!=null?typeof t.data=="string"?t.data:JSON.stringify(t.data):JSON.stringify(t)}]}}catch(r){return{content:[{type:"text",text:`${n} failed: ${r}`}],isError:!0}}}function c(n){return{content:[{type:"text",text:n}]}}export{q as registerClaudeTools};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{log as l}from"../../core/log/index.js";class c{controlURL="";token="";isConfigured(){return!!(this.controlURL&&this.token)}configure(
|
|
1
|
+
import{log as l}from"../../core/log/index.js";class c{controlURL="";token="";isConfigured(){return!!(this.controlURL&&this.token)}configure(t,e){this.controlURL=t.replace(/\/+$/,""),this.token=e.trim(),l.info("claude-worker-client",`Configured with control URL: ${this.controlURL}`)}async post(t,e,s){if(!this.isConfigured())throw new Error("worker control not configured");const i=new AbortController,o=setTimeout(()=>i.abort(),s);try{const r=await fetch(`${this.controlURL}${t}`,{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${this.token}`},body:JSON.stringify(e),signal:i.signal}),n=await r.text(),a=n.trim()?JSON.parse(n):{};if(!r.ok)throw new Error(a.error||`worker control failed ${r.status}`);return a}finally{clearTimeout(o)}}isRetryableError(t){const e=t instanceof Error?t.message:String(t);return/fetch failed|network|ECONNRESET|ETIMEDOUT|EAI_AGAIN|socket hang up|aborted/i.test(e)}async postWithRetry(t,e,s,i=1){let o;for(let r=0;r<=i;r++)try{return r>0&&l.info("claude-worker-client",`Retrying ${t} attempt=${r+1}`),await this.post(t,e,s)}catch(n){if(o=n,r>=i||!this.isRetryableError(n))break;await new Promise(a=>setTimeout(a,150))}throw o instanceof Error?o:new Error(String(o))}async deliverEvent(t){return this.postWithRetry("/v1/worker/deliver-event",{payload:t},1e4,1)}async deliverStop(t){return this.postWithRetry("/v1/worker/deliver-stop",{payload:t},1e4,1)}async deliverLocalAction(t){return this.postWithRetry("/v1/worker/deliver-local-action",{payload:t},1e4,1)}async ping(){try{return await this.postWithRetry("/v1/worker/ping",{},5e3,1),!0}catch{return!1}}}export{c as ClaudeWorkerClient};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{spawn as x,execSync as y}from"node:child_process";import{randomUUID as v}from"node:crypto";import{mkdir as S}from"node:fs/promises";import{readFileSync as C}from"node:fs";import{join as d}from"node:path";import{homedir as T,tmpdir as I}from"node:os";import{log as o}from"../../core/log/index.js";import{MCP_HTTP_CHANNEL_NAME as
|
|
2
|
-
`),"utf8"),{expectPath:a,pidPath:i}}function h(
|
|
1
|
+
import{spawn as x,execSync as y}from"node:child_process";import{randomUUID as v}from"node:crypto";import{mkdir as S}from"node:fs/promises";import{readFileSync as C}from"node:fs";import{join as d}from"node:path";import{homedir as T,tmpdir as I}from"node:os";import{log as o}from"../../core/log/index.js";import{MCP_HTTP_CHANNEL_NAME as l}from"./protocol-contract.js";function P(e){let t=null,r=0,n=!1,i=!1;const a=v(),s=e.gatewayUrl??"http://127.0.0.1:19580/mcp";return{async start(){await F(e.command,s,e.env);const c=E(e.grix),u=[...e.args??[],"--name",`grix-mcp-${e.name}`,"--session-id",a];e.fullAuto&&u.push("--dangerously-skip-permissions"),u.push("--dangerously-load-development-channels",`server:${l}`,"--append-system-prompt",c);const f=d(I(),`grix-mcp-claude-${e.name}`);await S(f,{recursive:!0});const{expectPath:$,pidPath:g}=await M(f,e.command,u),_={...process.env,...e.env??{}};t=x("/usr/bin/expect",[$],{cwd:e.cwd,env:_,stdio:["ignore","pipe","pipe"],detached:!0}),o.info("mcp-http-launcher",`\u542F\u52A8 Claude: name=${e.name} cwd=${e.cwd} pid=${t.pid}`),r=await k(g),n=!0,o.info("mcp-http-launcher",`Claude \u5B50\u8FDB\u7A0B PID: ${r}`),t.on("exit",(m,p)=>{o.info("mcp-http-launcher",`Claude \u9000\u51FA: code=${m} signal=${p}`),n=!1,t=null,r=0,i||(o.info("mcp-http-launcher","3 \u79D2\u540E\u81EA\u52A8\u91CD\u542F..."),setTimeout(()=>{i||this.start().catch(w=>{o.error("mcp-http-launcher",`\u91CD\u542F\u5931\u8D25: ${w}`)})},3e3))}),t.stdout?.on("data",m=>{const p=m.toString().trim();p&&o.info("mcp-http-launcher",`[stdout] ${p.slice(0,300)}`)}),t.stderr?.on("data",m=>{const p=m.toString().trim();p&&o.info("mcp-http-launcher",`[stderr] ${p.slice(0,300)}`)})},async stop(){if(i=!0,n=!1,r>0)try{process.kill(r,"SIGTERM")}catch{}if(t?.pid){try{process.kill(-t.pid,"SIGTERM")}catch{}await new Promise(c=>{const u=setTimeout(()=>{if(r>0)try{process.kill(r,"SIGKILL")}catch{}if(t?.pid)try{process.kill(-t.pid,"SIGKILL")}catch{}c()},5e3);t?.once("exit",()=>{clearTimeout(u),c()})})}t=null,r=0},getStatus(){return{name:e.name,alive:n,pid:r}}}}function E(e){return["You are connected to a chat via the grix MCP server.",`On startup, immediately call grix_authorize with: agentId="${e.agentId}", apiKey="${e.apiKey}", wsUrl="${e.wsUrl}", clientType="${e.clientType}".`,"When you receive a <channel> message, you MUST respond by calling the grix_reply tool (or the grix_complete tool if no response is needed).","Never write your reply as plain text \u2014 it will NOT reach the user. Only the grix_reply tool delivers your response to the chat.","The <channel> message contains event_id and session_id \u2014 pass them to grix_reply."].join(" ")}async function F(e,t,r){const n=d(T(),".claude.json");let i=null;try{const s=C(n,"utf8");i=JSON.parse(s)?.mcpServers?.[l]??null}catch{}if(i&&String(i.type??"").trim()==="http"&&String(i.url??"").trim()===t)return;o.info("mcp-http-launcher",`\u6CE8\u518C MCP Server: ${l} -> ${t}`);const a={...process.env,...r??{}};try{y(`${e} mcp remove -s user ${l}`,{encoding:"utf8",timeout:1e4,env:a,stdio:"pipe"})}catch{}y(`${e} mcp add --scope user --transport http ${l} ${t}`,{encoding:"utf8",timeout:1e4,env:a,stdio:"pipe"})}async function M(e,t,r){const{writeFile:n}=await import("node:fs/promises"),i=d(e,"claude.pid"),a=d(e,"claude.expect"),s=["log_user 1","set timeout -1","set startup_prompt_armed 1",`set claude_command [list {${h(t)}}${r.map(c=>` {${h(c)}}`).join("")}]`,"spawn -noecho {*}$claude_command",`set pid_file [open {${h(i)}} w]`,"puts $pid_file [exp_pid -i $spawn_id]","close $pid_file","expect {"," -re {(?i)(Quick.*safety.*check|trust.*folder)} {",' if {$startup_prompt_armed} { send -- "1\\r"; after 300 }; exp_continue'," }"," -re {(?i)I am using this for local development} {",' if {$startup_prompt_armed} { send -- "1\\r"; after 300 }; exp_continue'," }"," -re {(?i)(Enter.*confirm|Press.*Enter|Hit.*Enter)} {",' if {$startup_prompt_armed} { send -- "\\r"; after 300 }; exp_continue'," }"," -re {Listening for channel} {"," set startup_prompt_armed 0"," after 1000",' send -- "Call grix_authorize now as instructed in your system prompt.\\r"'," }"," -re {bypass permissions} {"," set startup_prompt_armed 0"," after 1000",' send -- "Call grix_authorize now as instructed in your system prompt.\\r"'," }"," eof {}","}","expect eof",""];return await n(i,"","utf8"),await n(a,s.join(`
|
|
2
|
+
`),"utf8"),{expectPath:a,pidPath:i}}function h(e){return e.replace(/[\\{}$\[\]"]/g,"\\$&")}async function k(e,t=1e4){const{readFile:r}=await import("node:fs/promises"),n=Math.ceil(t/100);for(let i=0;i<n;i++){try{const a=await r(e,"utf8"),s=parseInt(String(a).trim(),10);if(Number.isFinite(s)&&s>0)return s}catch{}await new Promise(a=>setTimeout(a,100))}return 0}export{P as createMcpHttpLauncher};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
class m{defaultTimeoutMs;onTimeout;timers=new Map;constructor(
|
|
1
|
+
class m{defaultTimeoutMs;onTimeout;timers=new Map;constructor(t){this.defaultTimeoutMs=t.defaultTimeoutMs??9e4,this.onTimeout=t.onTimeout}arm(t,e){this.cancel(t);const s=e?.timeoutMs??this.defaultTimeoutMs,i=Date.now()+s,o=setTimeout(()=>{this.timers.delete(t),this.onTimeout(t).catch(()=>{})},s);return this.timers.set(t,o),i}cancel(t){const e=this.timers.get(t);e&&(clearTimeout(e),this.timers.delete(t))}has(t){return this.timers.has(t)}close(){for(const t of this.timers.values())clearTimeout(t);this.timers.clear()}}export{m as ResultTimeoutManager};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import{execFile as V}from"node:child_process";import{createHash as N,randomUUID as O}from"node:crypto";import{closeSync as G,copyFileSync as p,cpSync as
|
|
2
|
-
${Y(a)}`),s<k&&await new Promise(l=>setTimeout(l,q))}throw n}bridgePackageRoot(){return o(this.identity.profileRoot,"node_modules","@grix","dsh-bridge")}profilePackageJsonPath(){return o(this.identity.profileRoot,"package.json")}captureInstallBackup(e,r){const t=o(e,"rollback",`${Date.now()}-${O()}`);y(t,{recursive:!0,mode:448});const n=this.bridgePackageRoot(),s=d(n);s&&
|
|
3
|
-
`,{mode:384}),{root:t,priorVersion:r,hadBridge:s,hadPackageJson:l}}async rollbackInstall(e,r,t,n){const s=n?o(r,`grix-dsh-bridge-${n}.tgz`):null;if(s&&d(s)&&await g(e,["plugin","--profile",this.identity.profileName,"add",s],this.options.dshHome).then(()=>!0).catch(()=>!1)){this.discardInstallBackup(t);return}if(t.hadBridge&&d(o(t.root,"dsh-bridge"))){this.restoreFilesystemBackup(t),this.discardInstallBackup(t);return}n?t.hadPackageJson&&this.restoreFilesystemBackup(t):(await g(e,["plugin","--profile",this.identity.profileName,"remove",u],this.options.dshHome).catch(()=>{}),this.cleanHalfInstalledBridge()),this.discardInstallBackup(t)}restoreFilesystemBackup(e){const r=this.bridgePackageRoot(),t=o(e.root,"dsh-bridge");e.hadBridge&&d(t)&&(f(r,{recursive:!0,force:!0}),y(o(this.identity.profileRoot,"node_modules","@grix"),{recursive:!0,mode:448}),
|
|
1
|
+
import{execFile as V}from"node:child_process";import{createHash as N,randomUUID as O}from"node:crypto";import{closeSync as G,copyFileSync as p,cpSync as P,existsSync as d,mkdirSync as y,openSync as T,readFileSync as h,rmSync as f,statSync as m,writeFileSync as b}from"node:fs";import{join as o,resolve as I}from"node:path";import{fileURLToPath as D}from"node:url";import{promisify as C}from"node:util";import{log as $}from"../../core/log/index.js";import{resolveShimInvocation as M}from"../../core/runtime/spawn.js";import{resolveCliPath as B}from"../../core/util/cli-probe.js";import{bridgeDiscoveryDirectory as J,discoverDshBridgeEndpoints as L,resolveDshProfileIdentity as F}from"./profile-resolver.js";const z=C(V),U=2*1024*1024,u="@grix/dsh-bridge",x="deepseek-harness-bridge-installer",k=2,q=1e3;class oe{options;command;identity;constructor(e={}){this.options=e,this.command=e.command?.trim()||"dsh",this.identity=F({dshHome:e.dshHome,profileName:e.profileName})}async status(){const e=await B(this.command),r=this.bridgeAsset(),t=this.readInstalledBridgeVersion(),n=r.version,s=L(this.identity),a=s[0]?.bridgeVersion??null,l=s[0]?.dshVersion??null,_=e?await R(e,this.options.dshHome):null;let c;return e?d(this.identity.profileRoot)?t?t!==n||this.installedBridgeContentStale(r)?c="bridge_update_required":s.length===0||a!==t||this.runningBridgePredatesInstalledContent(s[0])?c="profile_restart_required":c="ready":c="bridge_missing":c="profile_missing":c="dsh_missing",{readiness:c,command:this.command,commandPath:e,dshVersion:_,runningDshVersion:l,identity:this.identity,installedBridgeVersion:t,bundledBridgeVersion:n,runningBridgeVersion:a,endpointCount:s.length}}async install(){const e=await B(this.command);if(!e)throw Object.assign(new Error(`${this.command} not found`),{code:"dsh_missing"});const r=this.bridgeAsset();return A(this.identity,async()=>{await g(e,["--profile",this.identity.profileName,"--dump-config"],this.options.dshHome);const t=this.readInstalledBridgeVersion();if(t===r.version&&!this.installedBridgeContentStale(r))return this.status();const n=o(this.identity.dshHome,"assets","grix-dsh-bridge");y(n,{recursive:!0,mode:448});const s=this.captureInstallBackup(n,t),a=o(n,`grix-dsh-bridge-${r.version}.tgz`);p(r.tarball,a);try{if(t===r.version&&d(this.bridgePackageRoot())&&(await g(e,["plugin","--profile",this.identity.profileName,"remove",u],this.options.dshHome),d(this.bridgePackageRoot())))throw Object.assign(new Error(`dsh plugin remove left ${u} in place; refusing to attest an unverified reinstall`),{code:"bridge_install_failed"});await this.runPluginAdd(e,a),await g(e,["--profile",this.identity.profileName,"--dump-config"],this.options.dshHome);const l=this.readInstalledBridgeVersion();if(l!==r.version)throw Object.assign(new Error(`Bridge package did not land on disk (installed ${l??"none"}, expected ${r.version})`),{code:"bridge_install_failed"});this.writeInstalledBridgeIntegrity(r.integrity)}catch(l){await this.rollbackInstall(e,n,s,t);try{f(a,{force:!0})}catch{}throw Object.assign(new Error(`Bridge install failed: ${w(l)}`),{code:"bridge_install_failed"})}return this.discardInstallBackup(s),this.status()})}async uninstall(){const e=await B(this.command);if(!e)throw Object.assign(new Error(`${this.command} not found`),{code:"dsh_missing"});return A(this.identity,async()=>(this.readInstalledBridgeVersion()&&await g(e,["plugin","--profile",this.identity.profileName,"remove",u],this.options.dshHome),this.status()))}async runPluginAdd(e,r){const t=["plugin","--profile",this.identity.profileName,"add",r];let n;for(let s=1;s<=k;s++)try{await g(e,t,this.options.dshHome);return}catch(a){n=a,$.warn(x,`dsh plugin add attempt ${s}/${k} failed for profile ${this.identity.profileName}
|
|
2
|
+
${Y(a)}`),s<k&&await new Promise(l=>setTimeout(l,q))}throw n}bridgePackageRoot(){return o(this.identity.profileRoot,"node_modules","@grix","dsh-bridge")}profilePackageJsonPath(){return o(this.identity.profileRoot,"package.json")}captureInstallBackup(e,r){const t=o(e,"rollback",`${Date.now()}-${O()}`);y(t,{recursive:!0,mode:448});const n=this.bridgePackageRoot(),s=d(n);s&&P(n,o(t,"dsh-bridge"),{recursive:!0});const a=this.profilePackageJsonPath(),l=d(a);l&&p(a,o(t,"package.json"));const _=["pnpm-lock.yaml","package-lock.json","yarn.lock"];for(const c of _){const S=o(this.identity.profileRoot,c);d(S)&&p(S,o(t,c))}return b(o(t,"manifest.json"),`${JSON.stringify({priorVersion:r,hadBridge:s,hadPackageJson:l})}
|
|
3
|
+
`,{mode:384}),{root:t,priorVersion:r,hadBridge:s,hadPackageJson:l}}async rollbackInstall(e,r,t,n){const s=n?o(r,`grix-dsh-bridge-${n}.tgz`):null;if(s&&d(s)&&await g(e,["plugin","--profile",this.identity.profileName,"add",s],this.options.dshHome).then(()=>!0).catch(()=>!1)){this.discardInstallBackup(t);return}if(t.hadBridge&&d(o(t.root,"dsh-bridge"))){this.restoreFilesystemBackup(t),this.discardInstallBackup(t);return}n?t.hadPackageJson&&this.restoreFilesystemBackup(t):(await g(e,["plugin","--profile",this.identity.profileName,"remove",u],this.options.dshHome).catch(()=>{}),this.cleanHalfInstalledBridge()),this.discardInstallBackup(t)}restoreFilesystemBackup(e){const r=this.bridgePackageRoot(),t=o(e.root,"dsh-bridge");e.hadBridge&&d(t)&&(f(r,{recursive:!0,force:!0}),y(o(this.identity.profileRoot,"node_modules","@grix"),{recursive:!0,mode:448}),P(t,r,{recursive:!0})),e.hadPackageJson&&d(o(e.root,"package.json"))&&p(o(e.root,"package.json"),this.profilePackageJsonPath());for(const n of["pnpm-lock.yaml","package-lock.json","yarn.lock"]){const s=o(e.root,n);d(s)&&p(s,o(this.identity.profileRoot,n))}}cleanHalfInstalledBridge(){try{f(this.bridgePackageRoot(),{recursive:!0,force:!0})}catch{}const e=this.profilePackageJsonPath();if(d(e))try{const r=JSON.parse(h(e,"utf8"));let t=!1;for(const n of["dependencies","devDependencies","optionalDependencies","peerDependencies"]){const s=r[n];s&&typeof s=="object"&&!Array.isArray(s)&&u in s&&(delete s[u],t=!0)}t&&b(e,`${JSON.stringify(r,null,2)}
|
|
4
4
|
`,{mode:384})}catch{}}discardInstallBackup(e){try{f(e.root,{recursive:!0,force:!0})}catch{}}readInstalledBridgeVersion(){const e=o(this.bridgePackageRoot(),"package.json");try{const r=JSON.parse(h(e,"utf8"));return typeof r.version=="string"?r.version:null}catch{return null}}installedIntegrityPath(){return o(this.bridgePackageRoot(),".grix-bridge-integrity")}readInstalledBridgeIntegrity(){try{const e=h(this.installedIntegrityPath(),"utf8").trim();return e.startsWith("sha512-")?e:null}catch{return null}}writeInstalledBridgeIntegrity(e){b(this.installedIntegrityPath(),`${e}
|
|
5
|
-
`,{encoding:"utf8",mode:384})}installedBridgeContentStale(e=this.bridgeAsset()){return d(this.bridgePackageRoot())?this.readInstalledBridgeIntegrity()!==e.integrity:!0}runningBridgePredatesInstalledContent(e){try{const r=m(this.installedIntegrityPath()).mtimeMs,t=Date.parse(e.startedAt);return Number.isFinite(t)?r>t:!0}catch{return!1}}bridgeAsset(){return this.options.tarballPath?
|
|
6
|
-
`,"utf8"),await e()}finally{G(n);try{f(t,{force:!0})}catch{}}}function j(i){try{return
|
|
5
|
+
`,{encoding:"utf8",mode:384})}installedBridgeContentStale(e=this.bridgeAsset()){return d(this.bridgePackageRoot())?this.readInstalledBridgeIntegrity()!==e.integrity:!0}runningBridgePredatesInstalledContent(e){try{const r=m(this.installedIntegrityPath()).mtimeMs,t=Date.parse(e.startedAt);return Number.isFinite(t)?r>t:!0}catch{return!1}}bridgeAsset(){return this.options.tarballPath?E(I(this.options.tarballPath)):H()}}function ae(i){if(i.readiness==="ready")return null;const e=i.identity.profileName,r=`dsh --profile ${e}`,t=`dsh does not hot-load plugin changes: stop the running profile process, then start it again with: ${r}`;let n;i.endpointCount===0?n=`Grix Bridge is installed in DSH profile "${e}", but no live Bridge endpoint is available. Start the profile with: ${r}`:i.runningBridgeVersion&&i.installedBridgeVersion&&i.runningBridgeVersion!==i.installedBridgeVersion?n=`Grix Bridge in DSH profile "${e}" is running version ${i.runningBridgeVersion}, but ${i.installedBridgeVersion} is installed on disk. ${t}`:n=`The live Grix Bridge in DSH profile "${e}" predates the Bridge content installed on disk. ${t}`;const s={dsh_missing:"dsh is not installed or not on PATH. Install DeepSeek Harness (dsh), then retry.",profile_missing:`DSH profile "${e}" does not exist yet. Create/boot it once with: ${r}`,bridge_missing:`Grix Bridge is not installed in DSH profile "${e}". Install it from the DeepSeek Harness toolbar (or the dsh_install_bridge action), then restart the profile if it is already running.`,bridge_update_required:`Grix Bridge in DSH profile "${e}" needs an update (installed ${i.installedBridgeVersion??"none"}, expected ${i.bundledBridgeVersion}${i.installedBridgeVersion===i.bundledBridgeVersion?", content hash mismatch":""}). Update from the toolbar, then restart the profile \u2014 dsh does not hot-load plugin upgrades.`,profile_restart_required:n};return Object.assign(new Error(s[i.readiness]),{code:i.readiness})}function de(){return H().tarball}function H(){const i=[D(new URL("../../assets/dsh-bridge/",import.meta.url)),D(new URL("../../../dist/assets/dsh-bridge/",import.meta.url))].find(t=>d(o(t,"manifest.json")));if(!i)throw Object.assign(new Error("Bundled Grix DSH Bridge asset is unavailable; run the Connector build first"),{code:"bridge_asset_missing"});const e=JSON.parse(h(o(i,"manifest.json"),"utf8"));if(typeof e.tarball!="string"||!e.tarball||typeof e.bridgeVersion!="string"||!e.bridgeVersion)throw Object.assign(new Error("Invalid Grix DSH Bridge asset manifest"),{code:"bridge_asset_invalid"});const r=E(o(i,e.tarball),e.bridgeVersion);if(Number(e.size)!==m(r.tarball).size)throw Object.assign(new Error("Grix DSH Bridge asset size mismatch"),{code:"bridge_asset_invalid"});if(typeof e.integrity=="string"&&e.integrity.startsWith("sha512-")&&e.integrity!==r.integrity)throw Object.assign(new Error("Grix DSH Bridge asset integrity mismatch"),{code:"bridge_asset_invalid"});return r}function E(i,e){if(!d(i)||!m(i).isFile())throw Object.assign(new Error(`Grix DSH Bridge asset is missing: ${i}`),{code:"bridge_asset_missing"});if(m(i).size>U)throw Object.assign(new Error("Grix DSH Bridge asset exceeds the 2 MiB safety limit"),{code:"bridge_asset_invalid"});const r=e??/grix-dsh-bridge-([0-9A-Za-z.+-]+)\.tgz$/.exec(i)?.[1];if(!r)throw Object.assign(new Error("Cannot determine Grix DSH Bridge version"),{code:"bridge_asset_invalid"});const t=`sha512-${N("sha512").update(h(i)).digest("base64")}`;return{tarball:i,version:r,integrity:t}}async function A(i,e){const r=J(i);y(r,{recursive:!0,mode:448});const t=o(r,"install.lock");let n=j(t);if(n===null){let s=0;try{s=Number(h(t,"utf8").trim())}catch{}let a=!1;try{a=s===0&&Date.now()-m(t).mtimeMs>5*6e4}catch{}if(a||Number.isSafeInteger(s)&&s>0&&!X(s)){try{f(t,{force:!0})}catch{}n=j(t)}}if(n===null)throw Object.assign(new Error(`Another Grix DSH Bridge install is active for profile ${i.profileName}`),{code:"bridge_install_busy"});try{return b(n,`${process.pid}
|
|
6
|
+
`,"utf8"),await e()}finally{G(n);try{f(t,{force:!0})}catch{}}}function j(i){try{return T(i,"wx",384)}catch(e){if(e.code==="EEXIST")return null;throw Object.assign(new Error(`Cannot acquire Grix DSH Bridge install lock: ${w(e)}`),{code:"bridge_install_failed"})}}function X(i){try{return process.kill(i,0),!0}catch(e){return e.code==="EPERM"}}async function le(i,e){const r=await B(i?.trim()||"dsh");return r?R(r,e):null}async function R(i,e){try{return(await g(i,["--version"],e)).trim().split(/\s+/)[0]||null}catch(r){return $.warn(x,`dsh --version \u63A2\u6D4B\u5931\u8D25\uFF08${i}\uFF09\uFF1A${w(r)}`),null}}async function g(i,e,r){try{const t=M(i,e);return(await z(t.command,t.args,{env:{...process.env,...r?{DSH_HOME:I(r)}:{}},timeout:6e4,maxBuffer:8*1024*1024,windowsVerbatimArguments:t.windowsVerbatimArguments})).stdout}catch(t){const n=t&&typeof t=="object"?t:{};throw Object.assign(new Error(w(t)),{code:"dsh_command_failed",stderr:typeof n.stderr=="string"?v(n.stderr):void 0})}}function v(i){return i.replace(/(api[_-]?key|authorization|bearer)\s*[:=]\s*\S+/gi,"$1=[REDACTED]")}function w(i){const e=i&&typeof i=="object"?i:{};return v(String(e.stderr??e.message??i??"unknown error")).replace(/[\r\n]+/g," ").slice(0,1024)}function Y(i){const e=i&&typeof i=="object"?i:{};return v(String(e.stderr??e.message??i??"unknown error")).slice(0,8*1024)}export{oe as DshBridgeInstaller,de as defaultBridgeTarball,ae as errorForDshBridgeReadiness,le as readDshCliVersion};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{createWriteStream as
|
|
2
|
-
`,{mode:384}),
|
|
1
|
+
import{createWriteStream as H,existsSync as j,mkdirSync as v,readFileSync as U,renameSync as q,rmSync as J,writeFileSync as W}from"node:fs";import{join as K}from"node:path";import{log as d}from"../../core/log/index.js";import{resolveCliPath as Y}from"../../core/util/cli-probe.js";import{killProcessGroup as O,spawnCommand as z}from"../../core/runtime/spawn.js";import{errorForDshBridgeReadiness as A}from"./bridge-installer.js";import{bridgeDiscoveryDirectory as M,discoverDshBridgeEndpoints as g,profileHasWebApp as Q}from"./profile-resolver.js";const c="deepseek-harness-profile-supervisor",I=45e3,T=5*6e4,X=500;function Z(e){return{...process.env,DSH_HOME:e}}const w=new Map,m=new Map;function $(e,r,n="coalesce"){const t=w.get(e.profileKey);if(t&&n==="coalesce")return t.promise;const i=(t?t.promise.catch(()=>{}).then(()=>r()):r()).finally(()=>{w.get(e.profileKey)?.promise===i&&w.delete(e.profileKey)});return w.set(e.profileKey,{promise:i}),i}async function $e(e){return $(e.installer.identity,()=>F(e))}async function Pe(e){return $(e.installer.identity,()=>ee(e))}async function ee(e){const r=e.autoInstall!==!1;let n=await e.installer.status();return r&&["profile_missing","bridge_missing","bridge_update_required"].includes(n.readiness)&&(n=await e.installer.install()),n.readiness==="ready"&&!e.reloadManaged?(E(n.identity),n):F(e)}async function F(e){const r=e.installer,n=e.autoStart!==!1,t=e.readyTimeoutMs??I,o=e.bootGraceMs??T;let i=await r.status();if(e.reloadManaged&&(i=await re(r,i,t,o)),i.readiness==="ready")return E(i.identity),i;if(!n){const a=A(i);if(a)throw a;return i}if(i.readiness==="profile_restart_required"&&(i.endpointCount>0&&await ne(i),await _(r,i.identity,o),i=await N(r,t),S(i)||await L(i.identity,o,"bridge endpoint did not appear")),i.readiness==="ready")return te(i),i;const s=A(i);if(s)throw s;return i}async function re(e,r,n,t){const o=r.identity,i=p(o);if(!i||!l(i.pid))return r;const s=g(o);if(!s.some(D=>y(i,D))&&s.length>0)return r;d.info(c,`reloading connector-managed profile=${o.profileName} pid=${i.pid} after settings write`),await h(o,i.pid),await _(e,o,t);const u=await N(e,n);return S(u)||await L(o,t,"bridge endpoint did not appear after settings reload"),u}async function ne(e){const r=e.identity,n=p(r),t=g(r),o=e.runningBridgeVersion&&e.installedBridgeVersion&&e.runningBridgeVersion!==e.installedBridgeVersion?"bridge upgrade":"bridge content refresh";if(n&&l(n.pid)&&t.some(i=>y(n,i))){d.info(c,`restarting connector-managed profile=${r.profileName} pid=${n.pid} for ${o}`),await h(r,n.pid);return}d.info(c,`starting connector-managed profile=${r.profileName} for ${o}; external profile processes are left untouched`)}async function N(e,r){const n=await se(S,e,r);return S(n)&&E(e.identity),n}async function L(e,r,n){const t=de(e,r);if(t)throw d.info(c,`managed profile=${e.profileName} pid=${t.pid} still booting; left running (${n})`),ie(e,t);await oe(e,n)}function ie(e,r){return Object.assign(new Error(`DSH profile "${e.profileName}" is still starting (pid ${r.pid}); a first boot installs the profile dependencies before Grix Bridge can listen. The Connector keeps it running \u2014 send the message again in a moment and it will attach automatically.`),{code:"profile_starting"})}function te(e){d.info(c,`bridge ready profile=${e.identity.profileName} endpoints=${e.endpointCount} bridge=${e.runningBridgeVersion}`)}async function _(e,r,n){const t=p(r);if(t&&l(t.pid)){if(g(r).some(x=>y(t,x))){d.info(c,`reusing connector-managed profile=${r.profileName} pid=${t.pid}`);return}if(k(t,n)){d.info(c,`waiting on booting connector-managed profile=${r.profileName} pid=${t.pid}`);return}d.warn(c,`restarting stuck connector-managed profile=${r.profileName} pid=${t.pid} (alive without endpoint)`),await h(r,t.pid)}else t&&b(r);const o=await Y(e.command);if(!o)throw Object.assign(new Error("dsh is not installed or not on PATH"),{code:"dsh_missing"});const i=ae(r),s=M(r);v(s,{recursive:!0,mode:448});const a=K(s,"managed-profile.log"),u=H(a,{flags:"a",mode:384}),D=Z(r.dshHome),f=z(o,i,{cwd:r.profileRoot,env:D,stdio:["ignore","pipe","pipe"],detached:process.platform!=="win32"}).process;if(!f.pid)throw u.end(),Object.assign(new Error(`Failed to start DSH profile "${r.profileName}"`),{code:"profile_start_failed"});f.stdout?.pipe(u,{end:!1}),f.stderr?.pipe(u,{end:!1}),f.once("exit",(B,R)=>{d.warn(c,`managed profile exited profile=${r.profileName} pid=${f.pid} code=${B} signal=${R}`),m.get(r.profileKey)===f&&m.delete(r.profileKey),p(r)?.pid===f.pid&&b(r);try{u.end()}catch{}}),f.unref(),m.set(r.profileKey,f),V(r,{schemaVersion:1,profileKey:r.profileKey,profileName:r.profileName,pid:f.pid,startedAt:new Date().toISOString(),command:o,args:i}),d.info(c,`started connector-managed profile=${r.profileName} pid=${f.pid} args=${i.join(" ")}`)}async function oe(e,r){const n=p(e);n&&(d.warn(c,`stopping stuck connector-managed profile=${e.profileName} pid=${n.pid} reason=${r}`),await h(e,n.pid))}async function h(e,r){const n=p(e)?.endpointPid,t=n&&n!==r&&g(e).some(a=>a.pid===n)?n:null,o=t?[r,t]:[r],i=m.get(e.profileKey);if(i?.pid===r){try{O(i,"SIGTERM")}catch{}m.delete(e.profileKey)}else for(const a of o)if(l(a))try{process.kill(a,"SIGTERM")}catch{}const s=Date.now()+5e3;for(;Date.now()<s&&o.some(l);)await G(100);for(const a of o)if(l(a))try{process.kill(a,"SIGKILL")}catch{}b(e)}function ae(e){return Q(e.profileRoot,e.profileName)?["--profile",e.profileName,"--port","0"]:["--profile",e.profileName]}async function se(e,r,n){const t=Date.now()+n;let o=await r.status();for(;Date.now()<t;){if(e(o))return o;const i=p(r.identity);if(i&&!l(i.pid))throw b(r.identity),Object.assign(new Error(`Connector-managed DSH profile "${r.identity.profileName}" exited before Bridge became ready`),{code:"profile_start_failed"});await G(X),o=await r.status()}return o}function P(e){return K(M(e),"managed-profile.json")}function p(e){try{const r=JSON.parse(U(P(e),"utf8"));return r.schemaVersion!==1||typeof r.pid!="number"||r.profileKey!==e.profileKey?null:r}catch{return null}}function V(e,r){const n=M(e);v(n,{recursive:!0,mode:448});const t=P(e),o=`${t}.${process.pid}.tmp`;W(o,`${JSON.stringify(r,null,2)}
|
|
2
|
+
`,{mode:384}),q(o,t)}function y(e,r){return r.pid===e.pid||r.pid===e.endpointPid}function k(e,r){const n=Date.parse(e.startedAt);return Number.isFinite(n)?Date.now()-n<r:!1}function de(e,r){const n=p(e);return!n||!l(n.pid)||g(e).some(t=>y(n,t))?null:k(n,r)?n:null}function E(e){const r=p(e);if(!r||typeof r.endpointPid=="number"||!l(r.pid))return;const n=g(e);if(n.some(s=>s.pid===r.pid))return;const t=Date.parse(r.startedAt);if(!Number.isFinite(t))return;const o=n.filter(s=>{const a=Date.parse(s.startedAt);return Number.isFinite(a)&&a>=t});if(o.length!==1)return;const i=o[0];V(e,{...r,endpointPid:i.pid}),d.info(c,`adopted managed bridge endpoint profile=${e.profileName} wrapper=${r.pid} pid=${i.pid}`)}function b(e){try{J(P(e),{force:!0})}catch{}}function l(e){try{return process.kill(e,0),!0}catch(r){return r.code==="EPERM"}}function G(e){return new Promise(r=>setTimeout(r,e))}function be(){w.clear();for(const e of m.values())try{e.pid&&O(e,"SIGKILL")}catch{}m.clear()}function Se(e){return j(P(e))}async function De(e){const r=w.get(e.profileKey);r&&await r.promise.catch(()=>{})}async function Me(e,r=I){let n="failed";return await $(e.identity,async()=>(n=await ce(e,r),C(e,n==="restarted"?"ready":"profile_restart_required")),"queue"),n}async function ce(e,r){const n=e.identity,t=p(n);if(!t||!l(t.pid))return"not_managed";const o=g(n);if(o.length>0&&!o.some(i=>y(t,i)))return"not_managed";try{return d.info(c,`restarting connector-managed profile=${n.profileName} pid=${t.pid} for plugin toggle`),await h(n,t.pid),await _(e,n,T),(await N(e,r)).readiness==="ready"?"restarted":"failed"}catch(i){return d.warn(c,`plugin-toggle restart failed profile=${n.profileName}: ${i instanceof Error?i.message:String(i)}`),"failed"}}function S(e){return e.readiness==="ready"}function Ne(e,r){return $(e.identity,async()=>(await r,C(e,"ready")))}function C(e,r){return{readiness:r,command:e.command,commandPath:null,dshVersion:null,runningDshVersion:null,identity:e.identity,installedBridgeVersion:null,bundledBridgeVersion:"",runningBridgeVersion:null,endpointCount:0}}export{De as awaitDshProfileIdle,ae as buildDshProfileBootArgs,$e as ensureDshBridgeReady,Ne as holdDshProfileInflightForTests,Se as managedProfileRecordExists,Z as managedProfileSpawnEnv,Pe as prepareDshProfileBridge,be as resetDshProfileSupervisorForTests,Me as restartManagedProfileIfOwned};
|
|
Binary file
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"bridgeVersion": "4.3.
|
|
4
|
-
"tarball": "grix-dsh-bridge-4.3.
|
|
5
|
-
"size":
|
|
3
|
+
"bridgeVersion": "4.3.2",
|
|
4
|
+
"tarball": "grix-dsh-bridge-4.3.2.tgz",
|
|
5
|
+
"size": 22532,
|
|
6
6
|
"unpackedSize": 89865,
|
|
7
|
-
"shasum": "
|
|
8
|
-
"integrity": "sha512-
|
|
7
|
+
"shasum": "14a642d07854f5b6c6fd68aa4f180bbf485cc77e",
|
|
8
|
+
"integrity": "sha512-Zz9BTYdKa95fWjhZP2e6Y8FhA0AI21ql1h9VSYpFMGbL53kiAIGszQBx+HAxn2m8Xr18DxsJ85ePxAIhsRa9dA=="
|
|
9
9
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{readJSONFile as o,writeJSONFileAtomic as
|
|
1
|
+
import{readJSONFile as o,writeJSONFileAtomic as e}from"../util/json-file.js";function n(t){const r=o(t);return r&&typeof r=="object"&&"owners"in r&&Array.isArray(r.owners)?r.owners.filter(i=>typeof i=="string"&&i.trim().length>0):[]}async function s(t,r){await e(t,{owners:r})}export{n as readAllowlist,s as writeAllowlist};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{readdir as r,stat as m}from"node:fs/promises";import{join as l,extname as d}from"node:path";const x={pdf:"application/pdf",doc:"application/msword",docx:"application/vnd.openxmlformats-officedocument.wordprocessingml.document",xls:"application/vnd.ms-excel",xlsx:"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",ppt:"application/vnd.ms-powerpoint",pptx:"application/vnd.openxmlformats-officedocument.presentationml.presentation",txt:"text/plain",md:"text/markdown",csv:"text/csv",json:"application/json",xml:"application/xml",yaml:"text/yaml",yml:"text/yaml",html:"text/html",css:"text/css",js:"text/javascript",ts:"text/typescript",zip:"application/zip",rar:"application/x-rar-compressed","7z":"application/x-7z-compressed",tar:"application/x-tar",gz:"application/gzip",jpg:"image/jpeg",jpeg:"image/jpeg",png:"image/png",gif:"image/gif",webp:"image/webp",svg:"image/svg+xml",mp4:"video/mp4",mov:"video/quicktime",avi:"video/x-msvideo",mkv:"video/x-matroska",webm:"video/webm",mp3:"audio/mpeg",wav:"audio/wav",flac:"audio/flac",aac:"audio/aac"};function n(a){const p=d(a).slice(1).toLowerCase();return x[p]}async function f(a,p=!1){const c=await r(a,{withFileTypes:!0}),s=[];for(const
|
|
1
|
+
import{readdir as r,stat as m}from"node:fs/promises";import{join as l,extname as d}from"node:path";const x={pdf:"application/pdf",doc:"application/msword",docx:"application/vnd.openxmlformats-officedocument.wordprocessingml.document",xls:"application/vnd.ms-excel",xlsx:"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",ppt:"application/vnd.ms-powerpoint",pptx:"application/vnd.openxmlformats-officedocument.presentationml.presentation",txt:"text/plain",md:"text/markdown",csv:"text/csv",json:"application/json",xml:"application/xml",yaml:"text/yaml",yml:"text/yaml",html:"text/html",css:"text/css",js:"text/javascript",ts:"text/typescript",zip:"application/zip",rar:"application/x-rar-compressed","7z":"application/x-7z-compressed",tar:"application/x-tar",gz:"application/gzip",jpg:"image/jpeg",jpeg:"image/jpeg",png:"image/png",gif:"image/gif",webp:"image/webp",svg:"image/svg+xml",mp4:"video/mp4",mov:"video/quicktime",avi:"video/x-msvideo",mkv:"video/x-matroska",webm:"video/webm",mp3:"audio/mpeg",wav:"audio/wav",flac:"audio/flac",aac:"audio/aac"};function n(a){const p=d(a).slice(1).toLowerCase();return x[p]}async function f(a,p=!1){const c=await r(a,{withFileTypes:!0}),s=[];for(const t of c){if(!p&&t.name.startsWith("."))continue;const i=l(a,t.name),e={id:i,name:t.name,is_directory:t.isDirectory()};try{if(t.isDirectory()){const o=await m(i);e.modified_at=o.mtime.toISOString()}else{const o=await m(i);e.size=o.size,e.modified_at=o.mtime.toISOString(),e.mime_type=n(t.name)}}catch{}s.push(e)}return s.sort((t,i)=>t.is_directory!==i.is_directory?t.is_directory?-1:1:t.name.localeCompare(i.name)),s}export{f as listFiles,n as resolveMimeType};
|
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
|
|
2
|
-
`)},error(o,r,...
|
|
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+`
|
|
3
3
|
`)}};export{s as GRIX_PATHS,S as ensureGrixDirs,$ as initLogger,u as log};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import*as
|
|
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 +1 @@
|
|
|
1
|
-
const
|
|
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 +1 @@
|
|
|
1
|
-
function a(
|
|
1
|
+
function a(t){const o=new Set([`http://127.0.0.1:${t.serverPort}`,`http://localhost:${t.serverPort}`,...t.allowedOrigins]),e=new Set([`127.0.0.1:${t.serverPort}`,`localhost:${t.serverPort}`,...t.allowedHosts]);return{validateRequest(s){const r=i(s,o);if(!r.ok)return r;const n=l(s,e);return n.ok?{ok:!0}:n}}}function i(t,o){const e=t.headers.origin;return e?o.has(e)?{ok:!0}:{ok:!1,statusCode:403,message:`Origin not allowed: ${e}`}:{ok:!0}}function l(t,o){const e=t.headers.host;return e?o.has(e)?{ok:!0}:{ok:!1,statusCode:403,message:`Host not allowed: ${e}`}:{ok:!1,statusCode:403,message:"Missing Host header"}}export{a as createSecurityPolicy};
|
|
@@ -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(
|
|
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 +1 @@
|
|
|
1
|
-
import{TOOLS as
|
|
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 +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(
|
|
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};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "grix-connector",
|
|
3
|
-
"version": "4.3.
|
|
3
|
+
"version": "4.3.2",
|
|
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",
|