grix-connector 3.3.1 → 3.3.3
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/README.md +67 -1
- package/dist/adapter/acp/acp-adapter.js +6 -6
- package/dist/adapter/agy/agy-adapter.js +2 -2
- package/dist/adapter/claude/claude-adapter.js +16 -16
- package/dist/adapter/claude/protocol-contract.js +1 -1
- package/dist/adapter/codewhale/codewhale-adapter.js +2 -2
- package/dist/adapter/codex/codex-bridge.js +1 -1
- package/dist/adapter/cursor/cursor-adapter.js +5 -5
- package/dist/adapter/opencode/opencode-adapter.js +3 -3
- package/dist/adapter/openhuman/openhuman-adapter.js +3 -3
- package/dist/adapter/pi/pi-adapter.js +4 -4
- package/dist/bridge/bridge.js +6 -6
- package/dist/core/access/allowlist-gate.js +1 -1
- package/dist/core/access/allowlist-store.js +1 -1
- package/dist/core/access/index.js +1 -1
- package/dist/core/config/paths.js +1 -1
- package/dist/core/mcp/event-tool-executor.js +1 -1
- package/dist/core/mcp/internal-api-server.js +1 -1
- package/dist/core/mcp/tools.js +1 -1
- package/dist/default-skills/grix-access-control/SKILL.md +27 -14
- package/dist/index.js +1 -1
- package/dist/manager-share-config.js +1 -1
- package/dist/mcp/stream-http/security.js +1 -1
- package/openclaw-plugin/index.js +48 -29
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
import{readAllowlist as
|
|
1
|
+
import{readAllowlist as r,writeAllowlist as s}from"./allowlist-store.js";import{log as a}from"../log/index.js";class h{filePath;writeQueue=Promise.resolve();constructor(e){this.filePath=e}async checkAccess(e){if(!e?.trim())return!1;const t=r(this.filePath);return t.includes(e)?!0:t.length===0?this.autoAdd(e):!1}async addOwner(e){e?.trim()&&(this.writeQueue=this.writeQueue.then(async()=>{const t=r(this.filePath);t.includes(e)||(t.push(e),await s(this.filePath,t))}).catch(t=>{a.error("allowlist-gate",`addOwner failed for ${e}: ${t instanceof Error?t.message:t}`)}),await this.writeQueue)}async removeOwner(e){e?.trim()&&(this.writeQueue=this.writeQueue.then(async()=>{const t=r(this.filePath),i=t.filter(l=>l!==e);i.length!==t.length&&await s(this.filePath,i)}).catch(t=>{a.error("allowlist-gate",`removeOwner failed for ${e}: ${t instanceof Error?t.message:t}`)}),await this.writeQueue)}async listOwners(){return r(this.filePath)}autoAdd(e){return new Promise(t=>{this.writeQueue=this.writeQueue.then(async()=>{const i=r(this.filePath);if(i.includes(e)){t(!0);return}if(i.length>0){t(!1);return}await s(this.filePath,[e]),t(!0)}).catch(i=>{a.error("allowlist-gate",`autoAdd failed for ${e}: ${i instanceof Error?i.message:i}`),t(!1)})})}}export{h as AllowlistGate};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{readJSONFile 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{AllowlistGate as
|
|
1
|
+
import{AllowlistGate as l}from"./allowlist-gate.js";import{readAllowlist as t,writeAllowlist as s}from"./allowlist-store.js";export{l as AllowlistGate,t as readAllowlist,s as writeAllowlist};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{mkdir as r}from"node:fs/promises";import{join as
|
|
1
|
+
import{mkdir as r}from"node:fs/promises";import{join as o}from"node:path";import{homedir as n}from"node:os";const s="GRIX_CONNECTOR_HOME",a=o(n(),".grix");function d(i){const e=i||(process.env[s]?process.env[s]:a);return{rootDir:e,configDir:o(e,"config"),logDir:o(e,"log"),dataDir:o(e,"data"),serviceDir:o(e,"service"),stateFile:o(e,"service.json"),pidFile:o(e,"grix-connector.pid"),daemonLockFile:o(e,"daemon.lock.json"),daemonStatusFile:o(e,"daemon-status.json"),stdoutLogFile:o(e,"service","daemon.out.log"),stderrLogFile:o(e,"service","daemon.err.log"),contextsDir:o(e,"data","session-contexts"),hookSignalsPath:o(e,"data","hook-signals.json"),hookSignalsLogPath:o(e,"log","hook-signals.log"),elicitationRequestsDir:o(e,"data","elicitation-requests"),eventStatesDir:o(e,"data","event-states"),questionRequestsDir:o(e,"data","question-requests"),permissionRequestsDir:o(e,"data","permission-requests"),agentGlobalConfigsFile:o(e,"data","agent-global-configs.json")}}async function m(i){const e=[i.rootDir,i.configDir,i.logDir,i.dataDir,i.serviceDir];await Promise.all(e.map(t=>r(t,{recursive:!0})))}export{a as DEFAULT_GRIX_HOME,s as GRIX_HOME_ENV,m as ensureRuntimeDirs,d as resolveRuntimePaths};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{splitTextForAibotProtocol as
|
|
1
|
+
import{splitTextForAibotProtocol as f}from"../protocol/protocol-text.js";import{PHASE2_TOOL_NAMES as l}from"./tools.js";const g=l;function E(e){return g.has(e)}function q(e,n,t){switch(n){case"grix_reply":return v(e,t);case"grix_complete":return p(e,t);case"grix_event_ack":return m(e,t);case"grix_composing":return x(e,t);case"grix_status":return S(e);default:return o(`Unknown event tool: ${n}`)}}function v(e,n){const t=String(n.event_id??""),s=String(n.session_id??""),d=String(n.text??""),_=n.quoted_message_id;if(!s)return o("Missing required parameter: session_id");if(!d)return o("Missing required parameter: text");const r=f(d),u=`reply_${t||"proactive"}_${Date.now()}`;if(!t){for(let i=0;i<r.length;i++)e.sendMsg({session_id:s,client_msg_id:`${u}_${i+1}`,msg_type:1,content:r[i],quoted_message_id:i===0?_:void 0});return c({ok:!0,direct:!0,chunks:r.length,client_msg_id:u})}for(let i=0;i<r.length;i++){const a=i===r.length-1;e.sendStreamChunk({event_id:t||void 0,session_id:s,delta_content:r[i],chunk_seq:i+1,is_finish:a,client_msg_id:u,quoted_message_id:i===0?_:void 0})}return c({ok:!0,chunks:r.length,client_msg_id:u})}function p(e,n){const t=String(n.event_id??""),s=String(n.status??""),d=n.msg;return t?["responded","canceled","failed"].includes(s)?(e.sendEventResult({event_id:t,status:s,msg:d}),c({ok:!0,event_id:t,status:s})):o("status must be one of: responded, canceled, failed"):o("Missing required parameter: event_id")}function m(e,n){const t=String(n.event_id??""),s=n.session_id;return t?(e.sendEventAck({event_id:t,session_id:s,received_at:Date.now()}),c({ok:!0,event_id:t})):o("Missing required parameter: event_id")}function x(e,n){const t=String(n.session_id??""),s=n.active===!0,d=n.event_id;return t?(e.sendSessionActivitySet({session_id:t,kind:"composing",active:s,ref_event_id:d,ttl_ms:s?3e4:void 0}),c({ok:!0,session_id:t,active:s})):o("Missing required parameter: session_id")}function S(e){const n=e.status,t=e.getStatusSnapshot();return c({connected:n==="ready",status:n,connected_at:t.connectedAt})}function c(e){return{content:[{type:"text",text:JSON.stringify(e)}],isError:!1}}function o(e){return{content:[{type:"text",text:e}],isError:!0}}export{g as EVENT_TOOL_NAMES,q as executeEventTool,E as isEventTool};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import S from"node:http";import{randomBytes as k}from"node:crypto";import{log as h}from"../log/index.js";import{PHASE1_INVOKE_ACTIONS as y}from"./tools.js";import{WebSocketServer as O,WebSocket as w}from"ws";const p=1024*1024,v=50,b=1e3,m=6e4,H=256,N=new Set([...y,"event_tool_call"]);class B{server=null;port=0;authToken;invokeHandler=null;statusLineHandler=null;activeInvokes=0;wss=null;bridgeWs=null;mcpBridgeUpHandler=null;downstreamOutbox=[];constructor(){this.authToken=k(32).toString("hex")}setInvokeHandler(e){this.invokeHandler=e}setStatusLineHandler(e){this.statusLineHandler=e}async start(e=0){return new Promise((t,s)=>{this.server=S.createServer((r,i)=>this.handleRequest(r,i)),this.wss=new O({noServer:!0}),this.server.on("upgrade",(r,i,n)=>{const a=new URL(r.url??"/",`http://127.0.0.1:${this.port}`);if(a.pathname!=="/mcp-bridge"||a.searchParams.get("token")!==this.authToken){i.destroy();return}this.wss.handleUpgrade(r,i,n,o=>{if(this.bridgeWs=o,h.info("internal-api","mcp bridge connected"),this.downstreamOutbox.length>0){const
|
|
1
|
+
import S from"node:http";import{randomBytes as k}from"node:crypto";import{log as h}from"../log/index.js";import{PHASE1_INVOKE_ACTIONS as y}from"./tools.js";import{WebSocketServer as O,WebSocket as w}from"ws";const p=1024*1024,v=50,b=1e3,m=6e4,H=256,N=new Set([...y,"event_tool_call"]);class B{server=null;port=0;authToken;invokeHandler=null;statusLineHandler=null;activeInvokes=0;wss=null;bridgeWs=null;mcpBridgeUpHandler=null;downstreamOutbox=[];constructor(){this.authToken=k(32).toString("hex")}setInvokeHandler(e){this.invokeHandler=e}setStatusLineHandler(e){this.statusLineHandler=e}async start(e=0){return new Promise((t,s)=>{this.server=S.createServer((r,i)=>this.handleRequest(r,i)),this.wss=new O({noServer:!0}),this.server.on("upgrade",(r,i,n)=>{const a=new URL(r.url??"/",`http://127.0.0.1:${this.port}`);if(a.pathname!=="/mcp-bridge"||a.searchParams.get("token")!==this.authToken){i.destroy();return}this.wss.handleUpgrade(r,i,n,o=>{if(this.bridgeWs=o,h.info("internal-api","mcp bridge connected"),this.downstreamOutbox.length>0){const d=this.downstreamOutbox;this.downstreamOutbox=[];for(const l of d)o.send(l);h.info("internal-api",`mcp bridge flushed ${d.length} buffered downstream frame(s)`)}o.on("message",d=>{let l;try{l=JSON.parse(d.toString("utf8"))}catch{return}this.mcpBridgeUpHandler?.(l)}),o.on("close",()=>{this.bridgeWs===o&&(this.bridgeWs=null),h.info("internal-api","mcp bridge disconnected")}),o.on("error",()=>{})})}),this.server.on("error",s),this.server.listen(e,"127.0.0.1",()=>{const r=this.server.address();typeof r=="object"&&r&&(this.port=r.port),this.server.removeListener("error",s),t(this.port)})})}get url(){return`http://127.0.0.1:${this.port}?token=${this.authToken}`}get baseUrl(){return`http://127.0.0.1:${this.port}`}get mcpBridgeWsUrl(){return`ws://127.0.0.1:${this.port}/mcp-bridge?token=${this.authToken}`}setMcpBridgeUpHandler(e){this.mcpBridgeUpHandler=e}sendMcpFrameToBridge(e){const t=JSON.stringify(e);if(this.bridgeWs&&this.bridgeWs.readyState===w.OPEN){this.bridgeWs.send(t);return}this.downstreamOutbox.length>=H&&(this.downstreamOutbox.shift(),h.warn("internal-api","mcp downstream outbox full, dropping oldest frame")),this.downstreamOutbox.push(t)}get token(){return this.authToken}async stop(){if(this.server)return new Promise(e=>{this.server.close(()=>{this.server=null,e()})})}checkAuth(e){return e.headers.authorization===`Bearer ${this.authToken}`||new URL(e.url??"/",`http://127.0.0.1:${this.port}`).searchParams.get("token")===this.authToken}handleRequest(e,t){if(e.method!=="POST"){t.writeHead(405),t.end(JSON.stringify({error:"method not allowed"}));return}if(!this.checkAuth(e)){t.writeHead(401),t.end(JSON.stringify({ok:!1,error:"unauthorized"}));return}const s=new URL(e.url??"/",`http://127.0.0.1:${this.port}`);if(s.pathname==="/api/status-line"){this.handleStatusLine(e,t);return}if(!s.pathname.startsWith("/api/invoke")){t.writeHead(404),t.end(JSON.stringify({error:"not found"}));return}const r=[];let i=0;e.on("data",n=>{if(i+=n.length,i>p){t.writeHead(413),t.end(JSON.stringify({ok:!1,error:"request body too large"})),e.destroy();return}r.push(n)}),e.on("end",async()=>{let n;try{n=JSON.parse(Buffer.concat(r).toString("utf8"))}catch{t.writeHead(400),t.end(JSON.stringify({ok:!1,error:"invalid JSON"}));return}const a=n.action,o=n.params??{};if(!a||!this.invokeHandler){t.writeHead(400),t.end(JSON.stringify({ok:!1,error:"missing action or no handler"}));return}if(!N.has(a)){t.writeHead(400),t.end(JSON.stringify({ok:!1,error:`action '${a}' is not allowed`}));return}if(this.activeInvokes>=v){t.writeHead(429),t.end(JSON.stringify({ok:!1,error:"too many concurrent invokes"}));return}const d=n.timeout_ms!=null?Math.max(b,Math.min(n.timeout_ms,m)):m,l=Date.now();this.activeInvokes++;const c=new AbortController;try{const u=await Promise.race([this.invokeHandler(a,o,c.signal,d),new Promise((f,g)=>setTimeout(()=>{c.abort(),g(new Error("invoke timeout"))},d))]);t.writeHead(200,{"content-type":"application/json"}),t.end(JSON.stringify({ok:!0,data:u})),h.info("internal-api",`invoke ok action=${a} tool=${o.tool_name??"-"} ms=${Date.now()-l}`)}catch(u){c.abort();const f=u instanceof Error?u.message:String(u);t.writeHead(500),t.end(JSON.stringify({ok:!1,error:f})),h.error("internal-api",`invoke error action=${a} tool=${o.tool_name??"-"} ms=${Date.now()-l} err=${f}`)}finally{this.activeInvokes--}})}handleStatusLine(e,t){const s=[];let r=0;e.on("data",i=>{if(r+=i.length,r>p){t.writeHead(413),t.end(JSON.stringify({ok:!1,error:"request body too large"})),e.destroy();return}s.push(i)}),e.on("end",async()=>{let i;try{i=JSON.parse(Buffer.concat(s).toString("utf8"))}catch{t.writeHead(400),t.end(JSON.stringify({ok:!1,error:"invalid JSON"}));return}if(!this.statusLineHandler){t.writeHead(200),t.end(JSON.stringify({ok:!0}));return}try{await this.statusLineHandler(i),t.writeHead(200,{"content-type":"application/json"}),t.end(JSON.stringify({ok:!0}))}catch(n){console.error("[internal-api] statusLine handler error:",n instanceof Error?n.message:n),t.writeHead(200,{"content-type":"application/json"}),t.end(JSON.stringify({ok:!0}))}})}}export{B as InternalApiServer};
|
package/dist/core/mcp/tools.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const r=[{name:"grix_query",description:"Search contacts, sessions, message history, messages, or favorited sessions in the Grix/AIBot platform.",inputSchema:{type:"object",properties:{action:{type:"string",enum:["contact_search","session_search","message_history","message_search","search_favorite_sessions"],description:"Query action type. search_favorite_sessions returns the owner's favorited sessions (supports keyword filter)."},id:{type:"string",description:"Contact ID (contact_search) or Session ID (session_search)."},keyword:{type:"string",description:"Search keyword."},sessionType:{type:"integer",enum:[1,2],description:"Filter by session type (session_search only): 1=private chat, 2=group chat. Omit to return all."},limit:{type:"integer",description:"Max results."},offset:{type:"integer",description:"Result offset."},sessionId:{type:"string",description:"Session ID (message_history, message_search)."},beforeId:{type:"string",description:"Pagination cursor (message_history, message_search)."}},required:["action"]},validation:{required:["action"],properties:{action:{type:"string",enum:["contact_search","session_search","message_history","message_search","search_favorite_sessions"]},id:{type:"string"},keyword:{type:"string",maxLength:200},sessionType:{type:"integer",enum:[1,2]},limit:{type:"integer",minimum:1,maximum:100},offset:{type:"integer",minimum:0},sessionId:{type:"string"},beforeId:{type:"string"}}}},{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"],description:"Group action type."},sessionId:{type:"string",description:"Group session ID."},name:{type:"string",description:"Group name (create)."},memberIds:{type:"array",items:{type:"string"},description:"Member IDs to add/remove."},memberTypes:{type:"array",items:{type:"integer",enum:[1,2]},description:"Member types (1=user, 2=agent)."},memberId:{type:"string",description:"Target member ID."},role:{type:"integer",enum:[1,2],description:"New role (1=admin, 2=member)."},memberType:{type:"integer",description:"Member type."},allMembersMuted:{type:"boolean",description:"Whether to mute all members."},isSpeakMuted:{type:"boolean",description:"Whether member is muted."},canSpeakWhenAllMuted:{type:"boolean",description:"Allow speaking when all muted."}},required:["action"]},validation:{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"}}}},{name:"grix_message_send",description:"Send a message to a session in the Grix/AIBot platform.",inputSchema:{type:"object",properties:{sessionId:{type:"string",description:"Target session ID"},content:{type:"string",description:"Message content"},msgType:{type:"integer",description:"Message type (1=text, default 1)"},quotedMessageId:{type:"string",description:"Message ID to reply to"},threadId:{type:"string",description:"Thread ID for threaded reply"}},required:["sessionId","content"]},validation:{required:["sessionId","content"],properties:{sessionId:{type:"string"},content:{type:"string",maxLength:1e4},msgType:{type:"integer"},quotedMessageId:{type:"string"},threadId:{type:"string"}}}},{name:"grix_message_unsend",description:"Recall/unsend a message in the Grix/AIBot platform.",inputSchema:{type:"object",properties:{sessionId:{type:"string",description:"Session ID"},msgId:{type:"string",description:"Message ID to unsend"}},required:["sessionId","msgId"]},validation:{required:["sessionId","msgId"],properties:{sessionId:{type:"string"},msgId:{type:"string"}}}},{name:"grix_file_link",description:"Create a direct, tailnet-only download link for a local file on this host. Use this whenever the user asks you to send, share, give, or deliver a file that exists on the machine where you run (a report, log, build artifact, export, or any local path). It returns a ready-to-use Markdown link in the `markdown` field \u2014 include that exact Markdown link in your reply so the user can click and download the file directly over the shared Tailscale network. The link is reachable only inside the tailnet, so just send it as-is \u2014 no need to worry about or mention any link lifetime. The download link is HTTPS, served by a built-in self-signed CA. The result also returns `ca_install_url`: the first time you share a link with a user (or whenever their browser warns the cert is untrusted), also give them this CA install link so they can install and trust it once \u2014 after that all download links work without warnings. Requires this host to be on a tailnet (Tailscale running).",inputSchema:{type:"object",properties:{file_path:{type:"string",description:"Absolute path to a local file on this host to share with the user."},ttl_ms:{type:"integer",description:"Optional link lifetime in milliseconds. Leave unset to use the long default; set only if you deliberately want a short-lived link."}},required:["file_path"]},validation:{required:["file_path"],properties:{file_path:{type:"string",maxLength:4096},ttl_ms:{type:"integer",minimum:1e4,maximum:864e5}}}},{name:"grix_file_upload",description:"Upload a local file to the Grix platform and send it as a media message in the target session. Supports images (jpg/png/webp/gif/bmp/heic/heif), videos (mp4/mov/m4v/webm/mkv/avi), documents (pdf/doc/docx/xls/xlsx/ppt/pptx/txt/md/csv/json/xml), and archives (zip/rar/7z/tar/gz). Max 50 MB. Use this instead of grix_file_link when the file should appear as a native attachment in the chat (visible inline for images/videos), rather than a tailnet download link.",inputSchema:{type:"object",properties:{file_path:{type:"string",description:"Absolute path to a local file to upload."},session_id:{type:"string",description:"Target session ID to send the file to."},caption:{type:"string",description:"Optional text caption for the media message."},reply_to_message_id:{type:"string",description:"Optional message ID to quote/reply to."}},required:["file_path","session_id"]},validation:{required:["file_path","session_id"],properties:{file_path:{type:"string",maxLength:4096},session_id:{type:"string"},caption:{type:"string",maxLength:2e3},reply_to_message_id:{type:"string"}}}},{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"],description:"Admin action type."},agentName:{type:"string",description:"Agent name (create_agent)."},introduction:{type:"string",description:"Agent introduction (create_agent)."},isMain:{type:"boolean",description:"Set as main agent (create_agent)."},agentId:{type:"string",description:"Agent ID (assign_category, rotate_api_key)."},categoryId:{type:"string",description:"Category ID (create_agent, update_category, assign_category)."},name:{type:"string",description:"Category name (create_category, update_category)."},parentId:{type:"string",description:"Parent category ID (create_category, update_category)."},sortOrder:{type:"integer",description:"Sort order (create_category, update_category)."}},required:["action"]},validation:{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"}}}},{name:"grix_call_owner",description:"Call your owner into this session to talk by voice. Use this when, during your work, you need to reach your owner \u2014 to discuss something or to get an approval/review. It sends the owner an offline notification; when they tap it they land directly in this conversation and a voice-brain call is started automatically. Requires the owner to have configured a voice brain. Rate-limited per session.",inputSchema:{type:"object",properties:{session_id:{type:"string",description:"The session ID to call the owner into."}},required:["session_id"]},validation:{required:["session_id"],properties:{session_id:{type:"string"}}}},{name:"grix_agent_update",description:"Update the text introduction of one of your owner's agents, identified by its numeric agent ID.",inputSchema:{type:"object",properties:{agent_id:{type:"string",description:"Target agent's numeric ID, passed as a string."},introduction:{type:"string",description:"New text introduction (max 300 characters)."}},required:["agent_id","introduction"]},validation:{required:["agent_id","introduction"],properties:{agent_id:{type:"string"},introduction:{type:"string",maxLength:300}}}},{name:"grix_dispatch_agent",description:`Dispatch one of your owner's agents to do work in a given working directory. Provide the target agent numeric ID, the working directory, and a text description of the task. The backend creates a NEW private session between the owner and that agent for each dispatch (it does not reuse past sessions), binds the working directory when the agent type requires it (claude/codex/etc.), and sends the task into the session AS THE OWNER so the agent starts working. Because the task is delivered as the owner, write it in the owner's first-person voice and tone \u2014 phrase it the way the owner would speak directly to the agent (e.g. "\u5E2E\u6211\u2026", "\u4F60\u53BB\u2026"), NOT as a third-person relay or as yourself narrating on the owner's behalf. Provide a short title summarizing the core of the task \u2014 it becomes the new session's title; if omitted the backend derives one from the task text.`,inputSchema:{type:"object",properties:{agent_id:{type:"string",description:"Target agent's numeric ID, passed as a string."},cwd:{type:"string",description:"Absolute working directory where the agent should do the work."},task:{type:"string",description:"Text description of the task to perform, written in the owner's first-person voice and tone \u2014 it is delivered into the session as the owner, so phrase it as the owner speaking directly to the agent, not as a third-person relay."},title:{type:"string",description:"Optional short title (a few words) summarizing the core of the task; used as the new session's title. If omitted, the backend derives a title from the task text."}},required:["agent_id","cwd","task"]},validation:{required:["agent_id","cwd","task"],properties:{agent_id:{type:"string"},cwd:{type:"string",maxLength:4096},task:{type:"string",maxLength:1e4},title:{type:"string",maxLength:255}}}},{name:"grix_session_send",description:"Send a message into a session AS THE OWNER \u2014 it appears as if the owner sent it, NOT as you (the agent). Use ONLY to relay on the owner's behalf into one of the owner's OTHER sessions that you are not part of (e.g. you were dispatched to work and need to drop a note to the owner elsewhere). NEVER use this to send your own reply in a session you are conversing in \u2014 that would make your words show up as the owner's message. To answer in your current conversation, reply normally (or use grix_message_send to send as yourself). The owner must be a member of the target session, and you (the agent) must NOT be a member of it \u2014 sending into a session you belong to is rejected.",inputSchema:{type:"object",properties:{session_id:{type:"string",description:"Target session ID."},content:{type:"string",description:"Message content to send as the owner."}},required:["session_id","content"]},validation:{required:["session_id","content"],properties:{session_id:{type:"string"},content:{type:"string",maxLength:1e4}}}},{name:"grix_chat_state_query",description:"Query the chat-level task states across all of your owner's sessions (including direct and group chats). Returns one entry per session with a single mutually-exclusive state: running (working), waiting_approval (blocked on your owner to approve/deny), waiting_question (asked the owner a question, awaiting their reply), completed, failed, or idle (no task / stopped). Also returns the session title (task_title) for easy identification. Supports pagination (page/page_size) and optional state filtering. Use this to see at a glance which chats are done, still running, or waiting on the owner.",inputSchema:{type:"object",properties:{session_id:{type:"string",description:"(Optional) Query a single session by its ID. Omit to return all sessions."},page:{type:"number",description:"(Optional) Page number, starting from 1. Defaults to 1 if omitted."},page_size:{type:"number",description:"(Optional) Number of items per page, max 100. Defaults to 10 if omitted."},state:{type:"string",description:"(Optional) Filter by a specific state: running, waiting_approval, waiting_question, completed, failed, or idle. Omit to return all states."}}},validation:{required:[],properties:{}}},{name:"grix_chat_state_update",description:"Manually update the task state of a specific chat session. Use this to mark a chat as completed, failed, idle, or any other state when you need to override it manually. The reason is written to stop_reason and is optional.",inputSchema:{type:"object",properties:{session_id:{type:"string",description:"The session ID whose state to update."},state:{type:"string",enum:["running","waiting_approval","waiting_question","completed","failed","idle"],description:"The new state to set. Must be one of: running, waiting_approval, waiting_question, completed, failed, idle."},reason:{type:"string",description:"(Optional) Reason for the state change, written to stop_reason."}}},validation:{required:["session_id","state"],properties:{}}}],a=[{name:"grix_reply",description:"Send your final reply \u2014 the conclusion the user is waiting for \u2014 to the specified session. This is the message the user treats as your answer; deliver your final result here, and use plain text only for brief progress notes while you work. Supports streaming in chunks; the frontend automatically aggregates them into one complete message. The connector quotes the message being answered automatically, so quoted_message_id is optional (set it only to quote a different earlier message).",inputSchema:{type:"object",properties:{event_id:{type:"string",description:"Associated event ID from the inbound event."},session_id:{type:"string",description:"Target session ID."},text:{type:"string",description:"Reply text content."},quoted_message_id:{type:"string",description:"Quoted message ID (optional)."},is_final:{type:"boolean",description:"Whether this is a stage-final reply. Advisory only \u2014 does not trigger event completion; completion is handled by the complete tool or Stop hook."}},required:["session_id","text"]},validation:{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"}}}},{name:"grix_complete",description:"Mark event processing as complete, notifying the backend that no more replies are expected.",inputSchema:{type:"object",properties:{event_id:{type:"string",description:"The event ID to complete."},status:{type:"string",enum:["responded","canceled","failed"],description:"Completion status."},msg:{type:"string",description:"Additional note (optional)."}},required:["event_id","status"]},validation:{required:["event_id","status"],properties:{event_id:{type:"string"},status:{type:"string",enum:["responded","canceled","failed"]},msg:{type:"string",maxLength:500}}}},{name:"grix_event_ack",description:"Acknowledge event receipt (usually done automatically by the Dispatcher; agents typically do not need to call this manually).",inputSchema:{type:"object",properties:{event_id:{type:"string",description:"The event ID to acknowledge."},session_id:{type:"string",description:"Session ID."}},required:["event_id"]},validation:{required:["event_id"],properties:{event_id:{type:"string"},session_id:{type:"string"}}}},{name:"grix_composing",description:'Set the "typing" indicator status for a session.',inputSchema:{type:"object",properties:{session_id:{type:"string",description:"Session ID."},active:{type:"boolean",description:"true = typing, false = stopped."},event_id:{type:"string",description:"Associated event ID (optional)."}},required:["session_id","active"]},validation:{required:["session_id","active"],properties:{session_id:{type:"string"},active:{type:"boolean"},event_id:{type:"string"}}}},{name:"grix_access_control",description:"Manage sender access control: pair approval, allow/remove senders, set policy.",inputSchema:{type:"object",properties:{action:{type:"string",enum:["pair_approve","pair_deny","allow_sender","remove_sender","set_policy"],description:"Access control action type."},code:{type:"string",description:"Pairing code (required for pair_approve/pair_deny)."},sender_id:{type:"string",description:"Sender ID (required for allow_sender/remove_sender)."},policy:{type:"string",enum:["allowlist","open","disabled"],description:"Access policy (required for set_policy)."}},required:["action"]},validation:{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"]}}}},{name:"grix_status",description:"Query the Grix connection status of the current MCP session.",inputSchema:{type:"object",properties:{}},validation:{required:[],properties:{}}}],v={grix_dispatch_agent:"grix-agent-dispatch",grix_agent_update:"grix-agent-dispatch",grix_query:"grix-query",grix_group:"grix-group",grix_message_send:"message-send",grix_message_unsend:"message-unsend",grix_admin:"grix-admin",grix_call_owner:"grix-owner-relay",grix_session_send:"grix-owner-relay",grix_chat_state_query:"grix-chat-state",grix_chat_state_update:"grix-chat-state",grix_status:"grix-chat-state",grix_access_control:"grix-access-control",grix_file_link:"tailnet-file-share"};function b(e){return` Before calling this tool, follow the \`${e}\` skill's procedure first; do not invoke this tool directly without going through that skill's guidance.`}for(const e of[...r,...a]){const t=v[e.name];t&&(e.description+=b(t))}const p=[{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."},final:{type:"boolean",description:"Advisory flag only. It does not complete the event; completion is handled by complete tool or Stop hook."}},required:["chat_id","event_id","text"]}},{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"]},msg:{type:"string"},code:{type:"string"}},required:["event_id","status"]}}],c=new Set(p.map(e=>e.name)),w=new Set(r.map(e=>e.name)),x=new Set(a.map(e=>e.name)),I=/([A-Za-z0-9._-]+:[A-Za-z0-9._-]+:[A-Za-z0-9._-]+:[A-Za-z0-9._-]+)/,k=/[A-Za-z0-9._-]+/;function R(e){return!!(w.has(e)||x.has(e)||c.has(e)||e.startsWith("mcp__grix"))}const S=[...r,...a],A=[...S,...p],G=new Map(A.map(e=>[e.name,e]));function W(e,t){return e==="reply"?{name:"grix_reply",args:m("grix_reply",{event_id:t.event_id,session_id:t.chat_id,text:t.text,quoted_message_id:t.reply_to,is_final:t.final})}:e==="complete"?{name:"grix_complete",args:m("grix_complete",{event_id:t.event_id,status:t.status,msg:t.msg,code:t.code})}:{name:e,args:t}}function l(e){const t=String(e??"").trim();return t?t.match(I)?.[1]:void 0}function u(e){const t=String(e??"").trim();if(!t)return;const n=l(t);if(n)return _(n);const i=t.match(/(?:chat_id|session_id)\s*=\s*"([A-Za-z0-9._-]+)"/)?.[1];if(i)return i;const o=t.match(/[A-Za-z0-9._-]+/g)??[];for(const s of o)if(!(s==="event_id"||s==="chat_id"||s==="session_id")&&s.length>0)return s;return t.match(k)?.[0]}function _(e){if(!e)return;const t=e.split(":",1)[0]?.trim();if(t)return u(t)}function m(e,t){if(e!=="grix_reply"&&e!=="grix_complete")return t;const n={...t},i=l(n.event_id);if(i&&(n.event_id=i),e==="grix_reply"){const o=_(i),d=String(n.session_id??""),s=u(n.session_id),f=/\bevent_id\b|["'<>\s]/.test(d);s&&!(f&&o)?n.session_id=s:o&&(n.session_id=o)}return n}function B(e){return c.has(e)}function Q(e,t){switch(e){case"grix_query":return T(t);case"grix_group":return q(t);case"grix_message_send":return M(t);case"grix_message_unsend":return O(t);case"grix_file_link":return D(t);case"grix_file_upload":return E(t);case"grix_admin":return U(t);case"grix_call_owner":return L(t);case"grix_agent_update":return j(t);case"grix_dispatch_agent":return N(t);case"grix_session_send":return z(t);case"grix_chat_state_query":return C(t);case"grix_chat_state_update":return P(t);default:throw new Error(`Unknown tool: ${e}`)}}const g={contact_search:"contact_search",session_search:"session_search",message_history:"message_history",message_search:"message_search",search_favorite_sessions:"search_favorite_sessions"};function T(e){const t=String(e.action??""),n=g[t];if(!n)throw new Error(`Unknown grix_query action: ${t}`);const i={};return e.id!=null&&(i.id=e.id),e.keyword!=null&&(i.keyword=e.keyword),e.sessionType!=null&&(i.session_type=e.sessionType),e.limit!=null&&(i.limit=e.limit),e.offset!=null&&(i.offset=e.offset),e.sessionId!=null&&(i.session_id=e.sessionId),e.beforeId!=null&&(i.before_id=e.beforeId),{action:n,params:i}}const y={create:"group_create",detail:"group_detail_read",leave:"group_leave_self",add_members:"group_member_add",remove_members:"group_member_remove",update_member_role:"group_member_role_update",update_all_members_muted:"group_all_members_muted_update",update_member_speaking:"group_member_speaking_update",dissolve:"group_dissolve"};function q(e){const t=String(e.action??""),n=y[t];if(!n)throw new Error(`Unknown grix_group action: ${t}`);const i={};return e.sessionId!=null&&(i.session_id=e.sessionId),e.name!=null&&(i.name=e.name),e.memberIds!=null&&(i.member_ids=e.memberIds),e.memberTypes!=null&&(i.member_types=e.memberTypes),e.memberId!=null&&(i.member_id=e.memberId),e.role!=null&&(i.role=e.role),e.memberType!=null&&(i.member_type=e.memberType),e.allMembersMuted!=null&&(i.all_members_muted=e.allMembersMuted),e.isSpeakMuted!=null&&(i.is_speak_muted=e.isSpeakMuted),e.canSpeakWhenAllMuted!=null&&(i.can_speak_when_all_muted=e.canSpeakWhenAllMuted),{action:n,params:i}}function M(e){const t={session_id:e.sessionId,msg_type:e.msgType??1,content:e.content};return e.quotedMessageId!=null&&(t.quoted_message_id=e.quotedMessageId),e.threadId!=null&&(t.thread_id=e.threadId),{action:"send_msg",params:t}}function O(e){return{action:"delete_msg",params:{session_id:e.sessionId,msg_id:e.msgId}}}function D(e){const t={file_path:e.file_path};return e.ttl_ms!=null&&(t.ttl_ms=e.ttl_ms),{action:"file_link",params:t}}function E(e){const t={file_path:e.file_path,session_id:e.session_id};return e.caption!=null&&(t.caption=e.caption),e.reply_to_message_id!=null&&(t.reply_to_message_id=e.reply_to_message_id),{action:"file_upload",params:t,timeoutMs:6e4}}const h={create_agent:"agent_api_create",list_categories:"agent_category_list",create_category:"agent_category_create",update_category:"agent_category_update",assign_category:"agent_category_assign",rotate_api_key:"agent_api_key_rotate"};function L(e){return{action:"call_owner",params:{session_id:e.session_id}}}function j(e){return{action:"agent_introduction_update",params:{agent_id:e.agent_id,introduction:e.introduction}}}function N(e){return{action:"dispatch_agent",params:{agent_id:e.agent_id,cwd:e.cwd,task:e.task,title:e.title}}}function z(e){return{action:"session_send",params:{session_id:e.session_id,content:e.content}}}function C(e){const t={};return e.session_id!=null&&(t.session_id=e.session_id),e.page!=null&&(t.page=e.page),e.page_size!=null&&(t.page_size=e.page_size),e.state!=null&&(t.state=e.state),{action:"chat_state_query",params:t}}function P(e){const t={session_id:e.session_id,state:e.state};return e.reason!=null&&(t.reason=e.reason),{action:"chat_state_update",params:t}}function U(e){const t=String(e.action??""),n=h[t];if(!n)throw new Error(`Unknown grix_admin action: ${t}`);const i={};return e.agentName!=null&&(i.agent_name=e.agentName),e.introduction!=null&&(i.introduction=e.introduction),e.isMain!=null&&(i.is_main=e.isMain),e.agentId!=null&&(i.agent_id=e.agentId),e.categoryId!=null&&(i.category_id=e.categoryId),e.name!=null&&(i.name=e.name),e.parentId!=null&&(i.parent_id=e.parentId),e.sortOrder!=null&&(i.sort_order=e.sortOrder),{action:n,params:i}}const F=new Set([...Object.values(g),...Object.values(y),...Object.values(h),"send_msg","delete_msg","file_link","file_upload","call_owner","agent_introduction_update","dispatch_agent","session_send","chat_state_query","chat_state_update"]),H={pair_approve:"pair_approve",pair_deny:"pair_deny",allow_sender:"sender_allow",remove_sender:"sender_remove",set_policy:"policy_set"};export{H as ACCESS_CONTROL_ACTION_MAP,S as ALL_TOOLS,a as EVENT_TOOLS,A as EXPOSED_TOOLS,F as PHASE1_INVOKE_ACTIONS,w as PHASE1_TOOL_NAMES,x as PHASE2_TOOL_NAMES,r as TOOLS,p as TOOL_ALIASES,G as TOOL_MAP,B as isAlias,R as isGrixInternalToolName,W as mapToolAlias,m as normalizeEventToolArgs,Q as toolCallToInvoke};
|
|
1
|
+
const r=[{name:"grix_query",description:"Search contacts, sessions, message history, messages, or favorited sessions in the Grix/AIBot platform.",inputSchema:{type:"object",properties:{action:{type:"string",enum:["contact_search","session_search","message_history","message_search","search_favorite_sessions"],description:"Query action type. search_favorite_sessions returns the owner's favorited sessions (supports keyword filter)."},id:{type:"string",description:"Contact ID (contact_search) or Session ID (session_search)."},keyword:{type:"string",description:"Search keyword."},sessionType:{type:"integer",enum:[1,2],description:"Filter by session type (session_search only): 1=private chat, 2=group chat. Omit to return all."},limit:{type:"integer",description:"Max results."},offset:{type:"integer",description:"Result offset."},sessionId:{type:"string",description:"Session ID (message_history, message_search)."},beforeId:{type:"string",description:"Pagination cursor (message_history, message_search)."}},required:["action"]},validation:{required:["action"],properties:{action:{type:"string",enum:["contact_search","session_search","message_history","message_search","search_favorite_sessions"]},id:{type:"string"},keyword:{type:"string",maxLength:200},sessionType:{type:"integer",enum:[1,2]},limit:{type:"integer",minimum:1,maximum:100},offset:{type:"integer",minimum:0},sessionId:{type:"string"},beforeId:{type:"string"}}}},{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"],description:"Group action type."},sessionId:{type:"string",description:"Group session ID."},name:{type:"string",description:"Group name (create)."},memberIds:{type:"array",items:{type:"string"},description:"Member IDs to add/remove."},memberTypes:{type:"array",items:{type:"integer",enum:[1,2]},description:"Member types (1=user, 2=agent)."},memberId:{type:"string",description:"Target member ID."},role:{type:"integer",enum:[1,2],description:"New role (1=admin, 2=member)."},memberType:{type:"integer",description:"Member type."},allMembersMuted:{type:"boolean",description:"Whether to mute all members."},isSpeakMuted:{type:"boolean",description:"Whether member is muted."},canSpeakWhenAllMuted:{type:"boolean",description:"Allow speaking when all muted."}},required:["action"]},validation:{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"}}}},{name:"grix_message_send",description:"Send a message to a session in the Grix/AIBot platform.",inputSchema:{type:"object",properties:{sessionId:{type:"string",description:"Target session ID"},content:{type:"string",description:"Message content"},msgType:{type:"integer",description:"Message type (1=text, default 1)"},quotedMessageId:{type:"string",description:"Message ID to reply to"},threadId:{type:"string",description:"Thread ID for threaded reply"}},required:["sessionId","content"]},validation:{required:["sessionId","content"],properties:{sessionId:{type:"string"},content:{type:"string",maxLength:1e4},msgType:{type:"integer"},quotedMessageId:{type:"string"},threadId:{type:"string"}}}},{name:"grix_message_unsend",description:"Recall/unsend a message in the Grix/AIBot platform.",inputSchema:{type:"object",properties:{sessionId:{type:"string",description:"Session ID"},msgId:{type:"string",description:"Message ID to unsend"}},required:["sessionId","msgId"]},validation:{required:["sessionId","msgId"],properties:{sessionId:{type:"string"},msgId:{type:"string"}}}},{name:"grix_file_link",description:"Create a direct, tailnet-only download link for a local file on this host. Use this whenever the user asks you to send, share, give, or deliver a file that exists on the machine where you run (a report, log, build artifact, export, or any local path). It returns a ready-to-use Markdown link in the `markdown` field \u2014 include that exact Markdown link in your reply so the user can click and download the file directly over the shared Tailscale network. The link is reachable only inside the tailnet, so just send it as-is \u2014 no need to worry about or mention any link lifetime. The download link is HTTPS, served by a built-in self-signed CA. The result also returns `ca_install_url`: the first time you share a link with a user (or whenever their browser warns the cert is untrusted), also give them this CA install link so they can install and trust it once \u2014 after that all download links work without warnings. Requires this host to be on a tailnet (Tailscale running).",inputSchema:{type:"object",properties:{file_path:{type:"string",description:"Absolute path to a local file on this host to share with the user."},ttl_ms:{type:"integer",description:"Optional link lifetime in milliseconds. Leave unset to use the long default; set only if you deliberately want a short-lived link."}},required:["file_path"]},validation:{required:["file_path"],properties:{file_path:{type:"string",maxLength:4096},ttl_ms:{type:"integer",minimum:1e4,maximum:864e5}}}},{name:"grix_file_upload",description:"Upload a local file to the Grix platform and send it as a media message in the target session. Supports images (jpg/png/webp/gif/bmp/heic/heif), videos (mp4/mov/m4v/webm/mkv/avi), documents (pdf/doc/docx/xls/xlsx/ppt/pptx/txt/md/csv/json/xml), and archives (zip/rar/7z/tar/gz). Max 50 MB. Use this instead of grix_file_link when the file should appear as a native attachment in the chat (visible inline for images/videos), rather than a tailnet download link.",inputSchema:{type:"object",properties:{file_path:{type:"string",description:"Absolute path to a local file to upload."},session_id:{type:"string",description:"Target session ID to send the file to."},caption:{type:"string",description:"Optional text caption for the media message."},reply_to_message_id:{type:"string",description:"Optional message ID to quote/reply to."}},required:["file_path","session_id"]},validation:{required:["file_path","session_id"],properties:{file_path:{type:"string",maxLength:4096},session_id:{type:"string"},caption:{type:"string",maxLength:2e3},reply_to_message_id:{type:"string"}}}},{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"],description:"Admin action type."},agentName:{type:"string",description:"Agent name (create_agent)."},introduction:{type:"string",description:"Agent introduction (create_agent)."},isMain:{type:"boolean",description:"Set as main agent (create_agent)."},agentId:{type:"string",description:"Agent ID (assign_category, rotate_api_key)."},categoryId:{type:"string",description:"Category ID (create_agent, update_category, assign_category)."},name:{type:"string",description:"Category name (create_category, update_category)."},parentId:{type:"string",description:"Parent category ID (create_category, update_category)."},sortOrder:{type:"integer",description:"Sort order (create_category, update_category)."}},required:["action"]},validation:{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"}}}},{name:"grix_call_owner",description:"Call your owner into this session to talk by voice. Use this when, during your work, you need to reach your owner \u2014 to discuss something or to get an approval/review. It sends the owner an offline notification; when they tap it they land directly in this conversation and a voice-brain call is started automatically. Requires the owner to have configured a voice brain. Rate-limited per session.",inputSchema:{type:"object",properties:{session_id:{type:"string",description:"The session ID to call the owner into."}},required:["session_id"]},validation:{required:["session_id"],properties:{session_id:{type:"string"}}}},{name:"grix_agent_update",description:"Update the text introduction of one of your owner's agents, identified by its numeric agent ID.",inputSchema:{type:"object",properties:{agent_id:{type:"string",description:"Target agent's numeric ID, passed as a string."},introduction:{type:"string",description:"New text introduction (max 300 characters)."}},required:["agent_id","introduction"]},validation:{required:["agent_id","introduction"],properties:{agent_id:{type:"string"},introduction:{type:"string",maxLength:300}}}},{name:"grix_dispatch_agent",description:`Dispatch one of your owner's agents to do work in a given working directory. Provide the target agent numeric ID, the working directory, and a text description of the task. The backend creates a NEW private session between the owner and that agent for each dispatch (it does not reuse past sessions), binds the working directory when the agent type requires it (claude/codex/etc.), and sends the task into the session AS THE OWNER so the agent starts working. Because the task is delivered as the owner, write it in the owner's first-person voice and tone \u2014 phrase it the way the owner would speak directly to the agent (e.g. "\u5E2E\u6211\u2026", "\u4F60\u53BB\u2026"), NOT as a third-person relay or as yourself narrating on the owner's behalf. Provide a short title summarizing the core of the task \u2014 it becomes the new session's title; if omitted the backend derives one from the task text.`,inputSchema:{type:"object",properties:{agent_id:{type:"string",description:"Target agent's numeric ID, passed as a string."},cwd:{type:"string",description:"Absolute working directory where the agent should do the work."},task:{type:"string",description:"Text description of the task to perform, written in the owner's first-person voice and tone \u2014 it is delivered into the session as the owner, so phrase it as the owner speaking directly to the agent, not as a third-person relay."},title:{type:"string",description:"Optional short title (a few words) summarizing the core of the task; used as the new session's title. If omitted, the backend derives a title from the task text."}},required:["agent_id","cwd","task"]},validation:{required:["agent_id","cwd","task"],properties:{agent_id:{type:"string"},cwd:{type:"string",maxLength:4096},task:{type:"string",maxLength:1e4},title:{type:"string",maxLength:255}}}},{name:"grix_session_send",description:"Send a message into a session AS THE OWNER \u2014 it appears as if the owner sent it, NOT as you (the agent). Use ONLY to relay on the owner's behalf into one of the owner's OTHER sessions that you are not part of (e.g. you were dispatched to work and need to drop a note to the owner elsewhere). NEVER use this to send your own reply in a session you are conversing in \u2014 that would make your words show up as the owner's message. To answer in your current conversation, reply normally (or use grix_message_send to send as yourself). The owner must be a member of the target session, and you (the agent) must NOT be a member of it \u2014 sending into a session you belong to is rejected.",inputSchema:{type:"object",properties:{session_id:{type:"string",description:"Target session ID."},content:{type:"string",description:"Message content to send as the owner."}},required:["session_id","content"]},validation:{required:["session_id","content"],properties:{session_id:{type:"string"},content:{type:"string",maxLength:1e4}}}},{name:"grix_chat_state_query",description:"Query the chat-level task states across all of your owner's sessions (including direct and group chats). Returns one entry per session with a single mutually-exclusive state: running (working), waiting_approval (blocked on your owner to approve/deny), waiting_question (asked the owner a question, awaiting their reply), completed, failed, or idle (no task / stopped). Also returns the session title (task_title) for easy identification. Supports pagination (page/page_size) and optional state filtering. Use this to see at a glance which chats are done, still running, or waiting on the owner.",inputSchema:{type:"object",properties:{session_id:{type:"string",description:"(Optional) Query a single session by its ID. Omit to return all sessions."},page:{type:"number",description:"(Optional) Page number, starting from 1. Defaults to 1 if omitted."},page_size:{type:"number",description:"(Optional) Number of items per page, max 100. Defaults to 10 if omitted."},state:{type:"string",description:"(Optional) Filter by a specific state: running, waiting_approval, waiting_question, completed, failed, or idle. Omit to return all states."}}},validation:{required:[],properties:{}}},{name:"grix_chat_state_update",description:"Manually update the task state of a specific chat session. Use this to mark a chat as completed, failed, idle, or any other state when you need to override it manually. The reason is written to stop_reason and is optional.",inputSchema:{type:"object",properties:{session_id:{type:"string",description:"The session ID whose state to update."},state:{type:"string",enum:["running","waiting_approval","waiting_question","completed","failed","idle"],description:"The new state to set. Must be one of: running, waiting_approval, waiting_question, completed, failed, idle."},reason:{type:"string",description:"(Optional) Reason for the state change, written to stop_reason."}}},validation:{required:["session_id","state"],properties:{}}},{name:"grix_access_control",description:"Manage sender access control: pair approval, allow/remove senders, set policy.",inputSchema:{type:"object",properties:{action:{type:"string",enum:["pair_approve","pair_deny","allow_sender","remove_sender","set_policy"],description:"Access control action type."},code:{type:"string",description:"Pairing code (required for pair_approve/pair_deny)."},sender_id:{type:"string",description:"Sender ID (required for allow_sender/remove_sender)."},policy:{type:"string",enum:["allowlist","open","disabled"],description:"Access policy (required for set_policy)."}},required:["action"]},validation:{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"]}}}}],a=[{name:"grix_reply",description:"Send your final reply \u2014 the conclusion the user is waiting for \u2014 to the specified session. This is the message the user treats as your answer; deliver your final result here, and use plain text only for brief progress notes while you work. Supports streaming in chunks; the frontend automatically aggregates them into one complete message. The connector quotes the message being answered automatically, so quoted_message_id is optional (set it only to quote a different earlier message).",inputSchema:{type:"object",properties:{event_id:{type:"string",description:"Associated event ID from the inbound event."},session_id:{type:"string",description:"Target session ID."},text:{type:"string",description:"Reply text content."},quoted_message_id:{type:"string",description:"Quoted message ID (optional)."},is_final:{type:"boolean",description:"Whether this is a stage-final reply. Advisory only \u2014 does not trigger event completion; completion is handled by the complete tool or Stop hook."}},required:["session_id","text"]},validation:{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"}}}},{name:"grix_complete",description:"Mark event processing as complete, notifying the backend that no more replies are expected.",inputSchema:{type:"object",properties:{event_id:{type:"string",description:"The event ID to complete."},status:{type:"string",enum:["responded","canceled","failed"],description:"Completion status."},msg:{type:"string",description:"Additional note (optional)."}},required:["event_id","status"]},validation:{required:["event_id","status"],properties:{event_id:{type:"string"},status:{type:"string",enum:["responded","canceled","failed"]},msg:{type:"string",maxLength:500}}}},{name:"grix_event_ack",description:"Acknowledge event receipt (usually done automatically by the Dispatcher; agents typically do not need to call this manually).",inputSchema:{type:"object",properties:{event_id:{type:"string",description:"The event ID to acknowledge."},session_id:{type:"string",description:"Session ID."}},required:["event_id"]},validation:{required:["event_id"],properties:{event_id:{type:"string"},session_id:{type:"string"}}}},{name:"grix_composing",description:'Set the "typing" indicator status for a session.',inputSchema:{type:"object",properties:{session_id:{type:"string",description:"Session ID."},active:{type:"boolean",description:"true = typing, false = stopped."},event_id:{type:"string",description:"Associated event ID (optional)."}},required:["session_id","active"]},validation:{required:["session_id","active"],properties:{session_id:{type:"string"},active:{type:"boolean"},event_id:{type:"string"}}}},{name:"grix_status",description:"Query the Grix connection status of the current MCP session.",inputSchema:{type:"object",properties:{}},validation:{required:[],properties:{}}}],v={grix_dispatch_agent:"grix-agent-dispatch",grix_agent_update:"grix-agent-dispatch",grix_query:"grix-query",grix_group:"grix-group",grix_message_send:"message-send",grix_message_unsend:"message-unsend",grix_admin:"grix-admin",grix_call_owner:"grix-owner-relay",grix_session_send:"grix-owner-relay",grix_chat_state_query:"grix-chat-state",grix_chat_state_update:"grix-chat-state",grix_status:"grix-chat-state",grix_access_control:"grix-access-control",grix_file_link:"tailnet-file-share"};function b(e){return` Before calling this tool, follow the \`${e}\` skill's procedure first; do not invoke this tool directly without going through that skill's guidance.`}for(const e of[...r,...a]){const t=v[e.name];t&&(e.description+=b(t))}const p=[{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."},final:{type:"boolean",description:"Advisory flag only. It does not complete the event; completion is handled by complete tool or Stop hook."}},required:["chat_id","event_id","text"]}},{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"]},msg:{type:"string"},code:{type:"string"}},required:["event_id","status"]}}],c=new Set(p.map(e=>e.name)),w=new Set(r.map(e=>e.name)),x=new Set(a.map(e=>e.name)),I=/([A-Za-z0-9._-]+:[A-Za-z0-9._-]+:[A-Za-z0-9._-]+:[A-Za-z0-9._-]+)/,k=/[A-Za-z0-9._-]+/;function W(e){return!!(w.has(e)||x.has(e)||c.has(e)||e.startsWith("mcp__grix"))}const S=[...r,...a],A=[...S,...p],B=new Map(A.map(e=>[e.name,e]));function Q(e,t){return e==="reply"?{name:"grix_reply",args:m("grix_reply",{event_id:t.event_id,session_id:t.chat_id,text:t.text,quoted_message_id:t.reply_to,is_final:t.final})}:e==="complete"?{name:"grix_complete",args:m("grix_complete",{event_id:t.event_id,status:t.status,msg:t.msg,code:t.code})}:{name:e,args:t}}function l(e){const t=String(e??"").trim();return t?t.match(I)?.[1]:void 0}function u(e){const t=String(e??"").trim();if(!t)return;const n=l(t);if(n)return _(n);const i=t.match(/(?:chat_id|session_id)\s*=\s*"([A-Za-z0-9._-]+)"/)?.[1];if(i)return i;const o=t.match(/[A-Za-z0-9._-]+/g)??[];for(const s of o)if(!(s==="event_id"||s==="chat_id"||s==="session_id")&&s.length>0)return s;return t.match(k)?.[0]}function _(e){if(!e)return;const t=e.split(":",1)[0]?.trim();if(t)return u(t)}function m(e,t){if(e!=="grix_reply"&&e!=="grix_complete")return t;const n={...t},i=l(n.event_id);if(i&&(n.event_id=i),e==="grix_reply"){const o=_(i),d=String(n.session_id??""),s=u(n.session_id),f=/\bevent_id\b|["'<>\s]/.test(d);s&&!(f&&o)?n.session_id=s:o&&(n.session_id=o)}return n}function F(e){return c.has(e)}function H(e,t){switch(e){case"grix_query":return T(t);case"grix_group":return q(t);case"grix_message_send":return M(t);case"grix_message_unsend":return O(t);case"grix_file_link":return D(t);case"grix_file_upload":return E(t);case"grix_admin":return R(t);case"grix_call_owner":return L(t);case"grix_agent_update":return j(t);case"grix_dispatch_agent":return N(t);case"grix_session_send":return z(t);case"grix_chat_state_query":return C(t);case"grix_chat_state_update":return U(t);case"grix_access_control":return P(t);default:throw new Error(`Unknown tool: ${e}`)}}const g={contact_search:"contact_search",session_search:"session_search",message_history:"message_history",message_search:"message_search",search_favorite_sessions:"search_favorite_sessions"};function T(e){const t=String(e.action??""),n=g[t];if(!n)throw new Error(`Unknown grix_query action: ${t}`);const i={};return e.id!=null&&(i.id=e.id),e.keyword!=null&&(i.keyword=e.keyword),e.sessionType!=null&&(i.session_type=e.sessionType),e.limit!=null&&(i.limit=e.limit),e.offset!=null&&(i.offset=e.offset),e.sessionId!=null&&(i.session_id=e.sessionId),e.beforeId!=null&&(i.before_id=e.beforeId),{action:n,params:i}}const y={create:"group_create",detail:"group_detail_read",leave:"group_leave_self",add_members:"group_member_add",remove_members:"group_member_remove",update_member_role:"group_member_role_update",update_all_members_muted:"group_all_members_muted_update",update_member_speaking:"group_member_speaking_update",dissolve:"group_dissolve"};function q(e){const t=String(e.action??""),n=y[t];if(!n)throw new Error(`Unknown grix_group action: ${t}`);const i={};return e.sessionId!=null&&(i.session_id=e.sessionId),e.name!=null&&(i.name=e.name),e.memberIds!=null&&(i.member_ids=e.memberIds),e.memberTypes!=null&&(i.member_types=e.memberTypes),e.memberId!=null&&(i.member_id=e.memberId),e.role!=null&&(i.role=e.role),e.memberType!=null&&(i.member_type=e.memberType),e.allMembersMuted!=null&&(i.all_members_muted=e.allMembersMuted),e.isSpeakMuted!=null&&(i.is_speak_muted=e.isSpeakMuted),e.canSpeakWhenAllMuted!=null&&(i.can_speak_when_all_muted=e.canSpeakWhenAllMuted),{action:n,params:i}}function M(e){const t={session_id:e.sessionId,msg_type:e.msgType??1,content:e.content};return e.quotedMessageId!=null&&(t.quoted_message_id=e.quotedMessageId),e.threadId!=null&&(t.thread_id=e.threadId),{action:"send_msg",params:t}}function O(e){return{action:"delete_msg",params:{session_id:e.sessionId,msg_id:e.msgId}}}function D(e){const t={file_path:e.file_path};return e.ttl_ms!=null&&(t.ttl_ms=e.ttl_ms),{action:"file_link",params:t}}function E(e){const t={file_path:e.file_path,session_id:e.session_id};return e.caption!=null&&(t.caption=e.caption),e.reply_to_message_id!=null&&(t.reply_to_message_id=e.reply_to_message_id),{action:"file_upload",params:t,timeoutMs:6e4}}const h={create_agent:"agent_api_create",list_categories:"agent_category_list",create_category:"agent_category_create",update_category:"agent_category_update",assign_category:"agent_category_assign",rotate_api_key:"agent_api_key_rotate"};function L(e){return{action:"call_owner",params:{session_id:e.session_id}}}function j(e){return{action:"agent_introduction_update",params:{agent_id:e.agent_id,introduction:e.introduction}}}function N(e){return{action:"dispatch_agent",params:{agent_id:e.agent_id,cwd:e.cwd,task:e.task,title:e.title}}}function z(e){return{action:"session_send",params:{session_id:e.session_id,content:e.content}}}function C(e){const t={};return e.session_id!=null&&(t.session_id=e.session_id),e.page!=null&&(t.page=e.page),e.page_size!=null&&(t.page_size=e.page_size),e.state!=null&&(t.state=e.state),{action:"chat_state_query",params:t}}function P(e){const t=String(e.action??""),n=G[t];if(!n)throw new Error(`Unknown grix_access_control action: ${t}`);const i={};return e.code!=null&&(i.code=e.code),e.sender_id!=null&&(i.sender_id=e.sender_id),e.policy!=null&&(i.policy=e.policy),{action:"claude_access_control",params:{verb:n,payload:i},timeoutMs:3e4}}function U(e){const t={session_id:e.session_id,state:e.state};return e.reason!=null&&(t.reason=e.reason),{action:"chat_state_update",params:t}}function R(e){const t=String(e.action??""),n=h[t];if(!n)throw new Error(`Unknown grix_admin action: ${t}`);const i={};return e.agentName!=null&&(i.agent_name=e.agentName),e.introduction!=null&&(i.introduction=e.introduction),e.isMain!=null&&(i.is_main=e.isMain),e.agentId!=null&&(i.agent_id=e.agentId),e.categoryId!=null&&(i.category_id=e.categoryId),e.name!=null&&(i.name=e.name),e.parentId!=null&&(i.parent_id=e.parentId),e.sortOrder!=null&&(i.sort_order=e.sortOrder),{action:n,params:i}}const Z=new Set([...Object.values(g),...Object.values(y),...Object.values(h),"send_msg","delete_msg","file_link","file_upload","call_owner","agent_introduction_update","dispatch_agent","session_send","chat_state_query","chat_state_update","claude_access_control"]),G={pair_approve:"pair_approve",pair_deny:"pair_deny",allow_sender:"sender_allow",remove_sender:"sender_remove",set_policy:"policy_set"};export{G as ACCESS_CONTROL_ACTION_MAP,S as ALL_TOOLS,a as EVENT_TOOLS,A as EXPOSED_TOOLS,Z as PHASE1_INVOKE_ACTIONS,w as PHASE1_TOOL_NAMES,x as PHASE2_TOOL_NAMES,r as TOOLS,p as TOOL_ALIASES,B as TOOL_MAP,F as isAlias,W as isGrixInternalToolName,Q as mapToolAlias,m as normalizeEventToolArgs,H as toolCallToInvoke};
|
|
@@ -1,31 +1,44 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: grix-access-control
|
|
3
|
-
description: Manage sender access control with the typed `grix_access_control` tool —
|
|
4
|
-
trigger:
|
|
3
|
+
description: Manage sender access control with the typed `grix_access_control` tool — allow/remove a sender on the group-chat allowlist, or set the access policy. Approval of group visitors normally happens via the owner's one-tap approval card (no agent involvement); this tool is the auxiliary management entry. Trigger when the user asks to allowlist or block a sender, or change who is allowed to use the agent.
|
|
4
|
+
trigger: 当用户要手动允许或移除某个发送者、或调整谁可以在群聊使用 Agent 的访问策略时
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Grix Access Control
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Access enforcement is server-side and mostly automatic:
|
|
10
|
+
|
|
11
|
+
- **Private chats are the owner's line.** Only the owner (and users the agent is
|
|
12
|
+
shared with) can DM the agent — this is a hard platform rule. The allowlist
|
|
13
|
+
does NOT grant private-chat access.
|
|
14
|
+
- **Group visitors are approved by card.** When someone not on the allowlist
|
|
15
|
+
@-mentions the agent in a group, the platform silently sends the owner a
|
|
16
|
+
one-tap approval card (allow/deny). Approving adds them to the allowlist for
|
|
17
|
+
group use. The agent is not involved in that flow at all.
|
|
18
|
+
|
|
19
|
+
Use the `grix_access_control` tool only as the auxiliary management entry.
|
|
10
20
|
|
|
11
21
|
## Tool contract
|
|
12
22
|
|
|
13
23
|
Call `grix_access_control` with one `action`:
|
|
14
24
|
|
|
15
|
-
- `
|
|
16
|
-
|
|
17
|
-
- `allow_sender` — add a sender to the allowlist. Requires `sender_id`.
|
|
18
|
-
- `remove_sender` — remove a sender. Requires `sender_id`.
|
|
25
|
+
- `allow_sender` — manually add a sender to the group-chat allowlist. Requires `sender_id`.
|
|
26
|
+
- `remove_sender` — remove a sender from the allowlist. Requires `sender_id`.
|
|
19
27
|
- `set_policy` — set the access policy. Requires `policy`, one of:
|
|
20
|
-
- `allowlist` —
|
|
21
|
-
|
|
22
|
-
- `
|
|
28
|
+
- `allowlist` (default) — group visitors need the owner's approval; allowlisted
|
|
29
|
+
senders may use the agent in group chats
|
|
30
|
+
- `open` — anyone may use the agent in group chats (private chat stays owner-only)
|
|
31
|
+
- `disabled` — block all senders
|
|
32
|
+
- `pair_approve` / `pair_deny` — approve or deny a pending access request by its
|
|
33
|
+
internal `code`. Rarely needed: the owner's approval card is the normal path.
|
|
34
|
+
|
|
35
|
+
The agent owner is always exempt from the gate and can never be locked out.
|
|
23
36
|
|
|
24
37
|
## Rules
|
|
25
38
|
|
|
26
|
-
1. Pick exactly one `action` and supply only the field it needs (`
|
|
27
|
-
|
|
39
|
+
1. Pick exactly one `action` and supply only the field it needs (`sender_id`
|
|
40
|
+
for allow/remove, `policy` for set_policy, `code` for pairing actions).
|
|
28
41
|
2. These actions change who can reach the agent — confirm with the user before
|
|
29
|
-
|
|
30
|
-
3. On failure, report the exact reason (e.g. expired/invalid
|
|
42
|
+
removing someone or switching the policy to `open`.
|
|
43
|
+
3. On failure, report the exact reason (e.g. expired/invalid request) instead of
|
|
31
44
|
retrying with a guessed value.
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{JsonRpcTransport as t}from"./core/transport/index.js";import{AcpClient as
|
|
1
|
+
import{JsonRpcTransport as t}from"./core/transport/index.js";import{AcpClient as n}from"./protocol/index.js";import{AgentProcess as a}from"./agent/index.js";import{AgentEventType as p}from"./types/index.js";import{AibotClient as x}from"./core/aibot/index.js";import{AgentInstance as A}from"./bridge/index.js";import{CodexAdapter as S}from"./adapter/codex/index.js";import{Manager as E}from"./manager.js";import{GRIX_PATHS as I,ensureGrixDirs as T,initLogger as O,log as g}from"./core/log/index.js";import{HealthServer as c,writePidFile as d,removePidFile as C}from"./core/runtime/index.js";import{readJSONFile as N,writeJSONFileAtomic as v,EventBuffer as P,QuotedMessageStream as M}from"./core/util/index.js";import{SafeMarkdownStreamSegmenter as G}from"./core/text-segmentation/index.js";import{ConversationLog as y,DEFAULT_LOG_MAX_BYTES as H,DEFAULT_LOG_MAX_FILES as X,LOG_MAX_BYTES_ENV as h,LOG_MAX_FILES_ENV as k,resolveLogRotationOptions as D}from"./core/log/index.js";import{TOOLS as U,toolCallToInvoke as V}from"./core/mcp/index.js";import{resolveRuntimePaths as J,ensureRuntimeDirs as Y,GRIX_HOME_ENV as q,DEFAULT_GRIX_HOME as K}from"./core/config/index.js";import{splitTextForAibotProtocol as j,resolveOutboundTextChunkLimit as z,uploadReplyFileToAgentMedia as W,resolveAgentAPIPresignURL as Z}from"./core/protocol/index.js";import{CHANNEL_NAME as ee,INTERACTION_KINDS as oe,LOCAL_ACTION_TYPES as te,SESSION_CONTROL_VERBS as re}from"./adapter/claude/protocol-contract.js";import{buildInteractionRequestInvokeParams as ie,buildPermissionInteractionPayload as ae,buildElicitationInteractionPayload as le,parseInteractionReplyAction as pe,buildInteractionReplyResult as me}from"./adapter/claude/interaction-protocol.js";import{ChannelContextStore as se,extractLatestGrixChannelTag as Ae,resolveHookChannelContext as fe}from"./core/context/index.js";import{HookSignalStore as _e}from"./core/hooks/index.js";import{ElicitationStore as Le}from"./core/persistence/elicitation-store.js";import{ActivityStatusManager as Te}from"./adapter/claude/activity-status-manager.js";export{n as AcpClient,Te as ActivityStatusManager,p as AgentEventType,A as AgentInstance,a as AgentProcess,x as AibotClient,ee as CHANNEL_NAME,se as ChannelContextStore,S as CodexAdapter,y as ConversationLog,K as DEFAULT_GRIX_HOME,H as DEFAULT_LOG_MAX_BYTES,X as DEFAULT_LOG_MAX_FILES,Le as ElicitationStore,P as EventBuffer,q as GRIX_HOME_ENV,I as GRIX_PATHS,c as HealthServer,_e as HookSignalStore,oe as INTERACTION_KINDS,t as JsonRpcTransport,te as LOCAL_ACTION_TYPES,h as LOG_MAX_BYTES_ENV,k as LOG_MAX_FILES_ENV,E as Manager,M as QuotedMessageStream,re as SESSION_CONTROL_VERBS,G as SafeMarkdownStreamSegmenter,U as TOOLS,le as buildElicitationInteractionPayload,me as buildInteractionReplyResult,ie as buildInteractionRequestInvokeParams,ae as buildPermissionInteractionPayload,T as ensureGrixDirs,Y as ensureRuntimeDirs,Ae as extractLatestGrixChannelTag,O as initLogger,g as log,pe as parseInteractionReplyAction,N as readJSONFile,C as removePidFile,Z as resolveAgentAPIPresignURL,fe as resolveHookChannelContext,D as resolveLogRotationOptions,z as resolveOutboundTextChunkLimit,J as resolveRuntimePaths,j as splitTextForAibotProtocol,V as toolCallToInvoke,W as uploadReplyFileToAgentMedia,v as writeJSONFileAtomic,d as writePidFile};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{readFileSync as f,writeFileSync as d,unlinkSync as u}from"node:fs";import{join as S}from"node:path";function l(e,t){return`${e}#shared:${t}`}function o(e,t){if(!e)return e;const r=e.lastIndexOf("."),i=Math.max(e.lastIndexOf("/"),e.lastIndexOf("\\"));return r>i?`${e.slice(0,r)}.shared.${t}${e.slice(r)}`:`${e}.shared.${t}`}function y(e,t){return{...e,name:l(e.name,t),aibot:{...e.aibot,sharedOwnerId:t},eventResultsPath:o(e.eventResultsPath,t),bindingsPath:o(e.bindingsPath,t),activeEventStorePath:o(e.activeEventStorePath,t),logDir:e.logDir?`${e.logDir}/shared-${t}`:void 0,
|
|
1
|
+
import{readFileSync as f,writeFileSync as d,unlinkSync as u}from"node:fs";import{join as S}from"node:path";function l(e,t){return`${e}#shared:${t}`}function o(e,t){if(!e)return e;const r=e.lastIndexOf("."),i=Math.max(e.lastIndexOf("/"),e.lastIndexOf("\\"));return r>i?`${e.slice(0,r)}.shared.${t}${e.slice(r)}`:`${e}.shared.${t}`}function y(e,t){return{...e,name:l(e.name,t),aibot:{...e.aibot,sharedOwnerId:t},eventResultsPath:o(e.eventResultsPath,t),bindingsPath:o(e.bindingsPath,t),activeEventStorePath:o(e.activeEventStorePath,t),logDir:e.logDir?`${e.logDir}/shared-${t}`:void 0,connectMaxRetries:e.connectMaxRetries??3}}function P(e,t,r){const i=new Set(r.map(n=>String(n).trim()).filter(n=>n.length>0)),s=[...t],a=`${e}#shared:`,c=[...i].filter(n=>!s.includes(l(e,n))),h=s.filter(n=>n.startsWith(a)).map(n=>({key:n,sharedOwnerId:n.slice(a.length)})).filter(n=>!i.has(n.sharedOwnerId));return{toAdd:c,toRemove:h}}class ${dataDir;constructor(t){this.dataDir=t}filePath(t){const r=t.replace(/[^a-zA-Z0-9_-]/g,"_");return S(this.dataDir,`shared-owners-${r}.json`)}load(t){try{const r=f(this.filePath(t),"utf-8"),i=JSON.parse(r);if(Array.isArray(i))return i.filter(s=>typeof s=="string"&&s.length>0)}catch{}return[]}save(t,r){const i=[...new Set(r.filter(s=>s.length>0))];if(i.length===0){this.delete(t);return}try{d(this.filePath(t),JSON.stringify(i),"utf-8")}catch{}}remove(t,r){const i=this.load(t).filter(s=>s!==r);i.length>0?this.save(t,i):this.delete(t)}delete(t){try{u(this.filePath(t))}catch{}}}export{$ as SharedOwnersCache,y as buildSharedInstanceConfig,P as diffSharedOwners,l as sharedInstanceKey,o as suffixSharedPath};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
function a(
|
|
1
|
+
function a(e){const t=new Set([`http://127.0.0.1:${e.serverPort}`,`http://localhost:${e.serverPort}`,...e.allowedOrigins]),o=new Set([`127.0.0.1:${e.serverPort}`,`localhost:${e.serverPort}`,...e.allowedHosts]);return{validateRequest(s){const r=i(s,t);if(!r.ok)return r;const n=l(s,o);return n.ok?{ok:!0}:n}}}function i(e,t){const o=e.headers.origin;return o?t.has(o)?{ok:!0}:{ok:!1,statusCode:403,message:`Origin not allowed: ${o}`}:{ok:!0}}function l(e,t){const o=e.headers.host;return o?t.has(o)?{ok:!0}:{ok:!1,statusCode:403,message:`Host not allowed: ${o}`}:{ok:!1,statusCode:403,message:"Missing Host header"}}export{a as createSecurityPolicy};
|
package/openclaw-plugin/index.js
CHANGED
|
@@ -6027,7 +6027,6 @@ function resolveRuntimePaths(runtimeDir) {
|
|
|
6027
6027
|
logDir: join5(rootDir, "log"),
|
|
6028
6028
|
dataDir: join5(rootDir, "data"),
|
|
6029
6029
|
serviceDir: join5(rootDir, "service"),
|
|
6030
|
-
allowlistFile: join5(rootDir, "allowlist.json"),
|
|
6031
6030
|
stateFile: join5(rootDir, "service.json"),
|
|
6032
6031
|
pidFile: join5(rootDir, "grix-connector.pid"),
|
|
6033
6032
|
daemonLockFile: join5(rootDir, "daemon.lock.json"),
|
|
@@ -9196,6 +9195,33 @@ var TOOLS = [
|
|
|
9196
9195
|
required: ["session_id", "state"],
|
|
9197
9196
|
properties: {}
|
|
9198
9197
|
}
|
|
9198
|
+
},
|
|
9199
|
+
{
|
|
9200
|
+
name: "grix_access_control",
|
|
9201
|
+
description: "Manage sender access control: pair approval, allow/remove senders, set policy.",
|
|
9202
|
+
inputSchema: {
|
|
9203
|
+
type: "object",
|
|
9204
|
+
properties: {
|
|
9205
|
+
action: {
|
|
9206
|
+
type: "string",
|
|
9207
|
+
enum: ["pair_approve", "pair_deny", "allow_sender", "remove_sender", "set_policy"],
|
|
9208
|
+
description: "Access control action type."
|
|
9209
|
+
},
|
|
9210
|
+
code: { type: "string", description: "Pairing code (required for pair_approve/pair_deny)." },
|
|
9211
|
+
sender_id: { type: "string", description: "Sender ID (required for allow_sender/remove_sender)." },
|
|
9212
|
+
policy: { type: "string", enum: ["allowlist", "open", "disabled"], description: "Access policy (required for set_policy)." }
|
|
9213
|
+
},
|
|
9214
|
+
required: ["action"]
|
|
9215
|
+
},
|
|
9216
|
+
validation: {
|
|
9217
|
+
required: ["action"],
|
|
9218
|
+
properties: {
|
|
9219
|
+
action: { type: "string", enum: ["pair_approve", "pair_deny", "allow_sender", "remove_sender", "set_policy"] },
|
|
9220
|
+
code: { type: "string" },
|
|
9221
|
+
sender_id: { type: "string" },
|
|
9222
|
+
policy: { type: "string", enum: ["allowlist", "open", "disabled"] }
|
|
9223
|
+
}
|
|
9224
|
+
}
|
|
9199
9225
|
}
|
|
9200
9226
|
];
|
|
9201
9227
|
var EVENT_TOOLS = [
|
|
@@ -9285,33 +9311,6 @@ var EVENT_TOOLS = [
|
|
|
9285
9311
|
}
|
|
9286
9312
|
}
|
|
9287
9313
|
},
|
|
9288
|
-
{
|
|
9289
|
-
name: "grix_access_control",
|
|
9290
|
-
description: "Manage sender access control: pair approval, allow/remove senders, set policy.",
|
|
9291
|
-
inputSchema: {
|
|
9292
|
-
type: "object",
|
|
9293
|
-
properties: {
|
|
9294
|
-
action: {
|
|
9295
|
-
type: "string",
|
|
9296
|
-
enum: ["pair_approve", "pair_deny", "allow_sender", "remove_sender", "set_policy"],
|
|
9297
|
-
description: "Access control action type."
|
|
9298
|
-
},
|
|
9299
|
-
code: { type: "string", description: "Pairing code (required for pair_approve/pair_deny)." },
|
|
9300
|
-
sender_id: { type: "string", description: "Sender ID (required for allow_sender/remove_sender)." },
|
|
9301
|
-
policy: { type: "string", enum: ["allowlist", "open", "disabled"], description: "Access policy (required for set_policy)." }
|
|
9302
|
-
},
|
|
9303
|
-
required: ["action"]
|
|
9304
|
-
},
|
|
9305
|
-
validation: {
|
|
9306
|
-
required: ["action"],
|
|
9307
|
-
properties: {
|
|
9308
|
-
action: { type: "string", enum: ["pair_approve", "pair_deny", "allow_sender", "remove_sender", "set_policy"] },
|
|
9309
|
-
code: { type: "string" },
|
|
9310
|
-
sender_id: { type: "string" },
|
|
9311
|
-
policy: { type: "string", enum: ["allowlist", "open", "disabled"] }
|
|
9312
|
-
}
|
|
9313
|
-
}
|
|
9314
|
-
},
|
|
9315
9314
|
{
|
|
9316
9315
|
name: "grix_status",
|
|
9317
9316
|
description: "Query the Grix connection status of the current MCP session.",
|
|
@@ -9413,6 +9412,8 @@ function toolCallToInvoke(toolName, args) {
|
|
|
9413
9412
|
return buildChatStateQueryInvoke(args);
|
|
9414
9413
|
case "grix_chat_state_update":
|
|
9415
9414
|
return buildChatStateUpdateInvoke(args);
|
|
9415
|
+
case "grix_access_control":
|
|
9416
|
+
return buildAccessControlInvoke(args);
|
|
9416
9417
|
default:
|
|
9417
9418
|
throw new Error(`Unknown tool: ${toolName}`);
|
|
9418
9419
|
}
|
|
@@ -9530,6 +9531,16 @@ function buildChatStateQueryInvoke(args) {
|
|
|
9530
9531
|
if (args.state != null) params.state = args.state;
|
|
9531
9532
|
return { action: "chat_state_query", params };
|
|
9532
9533
|
}
|
|
9534
|
+
function buildAccessControlInvoke(args) {
|
|
9535
|
+
const action = String(args.action ?? "");
|
|
9536
|
+
const verb = ACCESS_CONTROL_ACTION_MAP[action];
|
|
9537
|
+
if (!verb) throw new Error(`Unknown grix_access_control action: ${action}`);
|
|
9538
|
+
const payload = {};
|
|
9539
|
+
if (args.code != null) payload.code = args.code;
|
|
9540
|
+
if (args.sender_id != null) payload.sender_id = args.sender_id;
|
|
9541
|
+
if (args.policy != null) payload.policy = args.policy;
|
|
9542
|
+
return { action: "claude_access_control", params: { verb, payload }, timeoutMs: 3e4 };
|
|
9543
|
+
}
|
|
9533
9544
|
function buildChatStateUpdateInvoke(args) {
|
|
9534
9545
|
const params = {
|
|
9535
9546
|
session_id: args.session_id,
|
|
@@ -9566,8 +9577,16 @@ var PHASE1_INVOKE_ACTIONS = /* @__PURE__ */ new Set([
|
|
|
9566
9577
|
"dispatch_agent",
|
|
9567
9578
|
"session_send",
|
|
9568
9579
|
"chat_state_query",
|
|
9569
|
-
"chat_state_update"
|
|
9580
|
+
"chat_state_update",
|
|
9581
|
+
"claude_access_control"
|
|
9570
9582
|
]);
|
|
9583
|
+
var ACCESS_CONTROL_ACTION_MAP = {
|
|
9584
|
+
pair_approve: "pair_approve",
|
|
9585
|
+
pair_deny: "pair_deny",
|
|
9586
|
+
allow_sender: "sender_allow",
|
|
9587
|
+
remove_sender: "sender_remove",
|
|
9588
|
+
set_policy: "policy_set"
|
|
9589
|
+
};
|
|
9571
9590
|
|
|
9572
9591
|
// src/openclaw/admin/json-result.ts
|
|
9573
9592
|
function jsonToolResult(payload) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "grix-connector",
|
|
3
|
-
"version": "3.3.
|
|
3
|
+
"version": "3.3.3",
|
|
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",
|