UNPKG

grix-connector

Version:

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.

10 lines (8 loc) 7.09 kB
import{SESSION_CONTROL_VERBS as l,SESSION_CONTROL_ERROR_CODES as g}from"../adapter/claude/protocol-contract.js";import{parseInteractionMessage as _,readCardSubmissionParam as b}from"../core/protocol/interaction-parser.js";import{log as S}from"../core/log/index.js";function w(t){const o=String(t.content??"").trim();if(!o.startsWith("/grix ")){const s=_(o);for(const i of s.cardSubmissions){if(i.cardId!=="agent_open_session")continue;const e=b(i,"cwd")??"";return e?{verb:l.open,args:e}:null}return null}const d=o.slice(6).trim().split(/\s+/),c=d[0]?.toLowerCase(),m=d.slice(1).join(" ");return Object.values(l).includes(c)?{verb:c,args:m}:null}function k(t,o,d,c){const{verb:m,args:r}=t,s=o.session_id,i=()=>c.sendEventAck(o.event_id,s),e=(n,a)=>c.sendEventResult(o.event_id,n,a);switch(m){case l.open:{const n=r.trim();if(!n){i(),e("failed",{msg:"Usage: /grix open <working-directory>",code:g.cwdRequired});return}d.getSessionBindings().set(s,n),d.onSessionBound?.(s,n),i(),e("responded",{msg:`Session bound to ${n}`});break}case l.where:{const n=d.getSessionBindings().get(s)||d.getCwd()||"not set";i(),e("responded",{msg:`Working directory: ${n}`});break}case l.status:{const n=d.getStatus(),a=d.getAcpSessionOptions(),f=a?` Mode: ${a.currentModeId} (${a.modes.map(p=>p.id).join(", ")})`:"",u=a?` Model: ${a.currentModelId} (${a.models.map(p=>p.modelId).join(", ")})`:"";i(),e("responded",{msg:`Status: alive=${n.alive} busy=${n.busy} adapter=${n.adapterType}${f}${u}`});break}case l.stop:{if(!d.getSessionBindings().get(s)){i(),e("failed",{msg:"No session binding found for this session",code:g.bindingMissing});return}d.cancelActiveRun?.().catch(()=>{}),i(),e("responded",{msg:`Session ${s} stopped`});break}case l.restart:{const n=d.getSessionBindings().get(s);if(!n){i(),e("failed",{msg:"No session binding found for this session",code:g.bindingMissing});return}d.cancelActiveRun?.().catch(()=>{}),i(),e("responded",{msg:`Session ${s} restarted for ${n}`});break}case"set_mode":{const n=r.trim();if(!n){i(),e("failed",{msg:"Usage: /grix set_mode <mode-id>"});return}d.setMode(n).then(a=>{a&&d.onModeSet?.(n),i(),e(a?"responded":"failed",{msg:a?`Mode set to ${n}`:`Failed to set mode ${n}`})});break}case"set_model":{const n=r.trim();if(!n){i(),e("failed",{msg:"Usage: /grix set_model <model-id>"});return}d.setModel(n).then(a=>{a&&d.onModelSet?.(n),i(),e(a?"responded":"failed",{msg:a?`Model set to ${n}`:`Failed to set model ${n}`})});break}case"list_options":{const n=d.getAcpSessionOptions();if(!n){i(),e("failed",{msg:"No ACP session active"});return}const a=n.modes.map(u=>`${u.id}: ${u.name}`).join(` `),f=n.models.map(u=>`${u.modelId}: ${u.name}`).join(` `);i(),e("responded",{msg:`Modes (current: ${n.currentModeId}): ${a} Models (current: ${n.currentModelId}): ${f}`});break}case"approve":{const n=r.trim();if(!n||!d.isAcpAlive){i(),e("failed",{msg:"Usage: /grix approve <tool-call-id>"});return}const a=d.getPendingApproval(n);a&&(d.deletePendingApproval(n),d.respondPermission(a.requestId,{behavior:"allow"}).catch(()=>{})),i(),e("responded",{msg:a?`Approved: ${n}`:`No pending approval: ${n}`});break}case"reject":{const n=r.trim();if(!n||!d.isAcpAlive){i(),e("failed",{msg:"Usage: /grix reject <tool-call-id>"});return}const a=d.getPendingApproval(n);a&&(d.deletePendingApproval(n),d.respondPermission(a.requestId,{behavior:"deny"}).catch(()=>{})),i(),e("responded",{msg:a?`Rejected: ${n}`:`No pending approval: ${n}`});break}case l.listSessions:{i(),e("responded",{msg:"list_sessions is handled at bridge level"});break}default:i(),e("failed",{msg:`Unknown command: /grix ${m}`,code:g.verbInvalid})}}function A(t,o,d){return v(t,o,d)}async function v(t,o,d){const c=t.params??{},m=String(c.verb??"").toLowerCase(),r=d.sendLocalActionResult;switch(m){case l.open:{const s=String(c.session_id??""),i=String(c.cwd??"");if(!i){r(t.action_id,"failed",void 0,g.cwdRequired,"session cwd is required");return}s&&o.getSessionBindings().set(s,i),s&&o.onSessionBound?.(s,i),r(t.action_id,"ok",{outcome:"opened",binding:{cwd:i}});break}case l.stop:{const s=String(c.session_id??"");if(!s){r(t.action_id,"failed",void 0,"session_id_required","session_id is required for stop");return}o.cancelActiveRun?.().catch(()=>{}),r(t.action_id,"ok",{outcome:"stopped",sessionId:s});break}case l.status:{const s=String(c.session_id??""),e=o.getSessionBindings().get(s)??"",n=o.getStatus();r(t.action_id,"ok",{outcome:"status",verb:"status",binding:{aibotSessionId:s,cwd:e,workerStatus:n.alive?n.busy?"busy":"ready":"stopped"}});break}case l.where:{const s=String(c.session_id??""),e=o.getSessionBindings().get(s)??"";r(t.action_id,"ok",{outcome:"where",verb:"where",binding:{aibotSessionId:s,cwd:e}});break}case"restart":{const s=String(c.session_id??"");if(!s){r(t.action_id,"failed",void 0,"session_id_required","session_id is required for restart");return}o.cancelActiveRun?.().catch(()=>{}),r(t.action_id,"ok",{outcome:"restarted",sessionId:s});break}case l.setMode:{const s=String(c.mode_id??""),i=String(c.session_id??"");if(o.acpSetMode){const e=await o.acpSetMode(i,s);e.status==="failed"?r(t.action_id,"failed",void 0,e.errorCode,e.errorMsg):(e.result?.outcome==="mode_set"&&s&&o.onModeSet?.(s),r(t.action_id,"ok",e.result));return}if(!s||!o.isAcpAlive){r(t.action_id,"failed",void 0,"set_mode_invalid","mode_id is required and ACP session must be active");return}o.setMode(s).then(e=>{e&&o.onModeSet?.(s),r(t.action_id,e?"ok":"failed",e?{outcome:"mode_set",modeId:s}:void 0,e?void 0:"set_mode_failed",e?void 0:`failed to set mode ${s}`)});break}case l.setModel:{const s=String(c.model_id??""),i=String(c.session_id??"");if(o.acpSetModel){const e=await o.acpSetModel(i,s);e.status==="failed"?r(t.action_id,"failed",void 0,e.errorCode,e.errorMsg):(e.result?.outcome==="model_set"&&s&&o.onModelSet?.(s),r(t.action_id,"ok",e.result));return}if(!s||!o.isAcpAlive){r(t.action_id,"failed",void 0,"set_model_invalid","model_id is required and ACP session must be active");return}o.setModel(s).then(e=>{e&&o.onModelSet?.(s),r(t.action_id,e?"ok":"failed",e?{outcome:"model_set",modelId:s}:void 0,e?void 0:"set_model_failed",e?void 0:`failed to set model ${s}`)});break}case l.listOptions:{const s=o.getAcpSessionOptions(),i=s?{modes:s.modes.map(e=>({id:e.id,name:e.name})),currentModeId:s.currentModeId,models:s.models.map(e=>({modelId:e.modelId,name:e.name})),currentModelId:s.currentModelId,available_models:s.models.map(e=>({id:e.modelId,displayName:e.name})),available_modes:s.modes.map(e=>({id:e.id,displayName:e.name}))}:{modes:[],currentModeId:"",models:[],currentModelId:"",available_models:[],available_modes:[]};S.info("session-controller",`[toolbar] listOptions currentModelId=${i.currentModelId} models=${JSON.stringify(i.models?.map(e=>e.modelId))}`),r(t.action_id,"ok",i);break}case l.listSessions:{r(t.action_id,"ok",{outcome:"sessions_listed",sessions:[]});break}default:r(t.action_id,"failed",void 0,g.verbInvalid,`session control verb ${m} is not supported`)}}export{k as handleSessionControlCommand,A as handleSessionControlLocalAction,w as parseSessionControlCommand};