grix-connector 3.14.0 → 3.15.1
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/agy/agy-adapter.js +3 -3
- package/dist/adapter/claude/skill-scanner.js +2 -2
- package/dist/adapter/cursor/context-window.js +1 -0
- package/dist/adapter/cursor/cursor-adapter.js +17 -5
- package/dist/adapter/cursor/fetch-models.js +2 -0
- package/dist/adapter/cursor/fetch-usage.js +1 -0
- package/dist/adapter/cursor/index.js +1 -1
- package/dist/adapter/cursor/session-scanner.js +1 -1
- package/dist/bridge/bridge.js +9 -9
- package/dist/bridge/raw-event-delivery.js +2 -0
- package/dist/bridge/send-controller.js +3 -3
- package/dist/manager.js +1 -1
- package/dist/mcp/stream-http/security.js +1 -1
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import{spawn as $}from"node:child_process";import{randomUUID as k}from"node:crypto";import{EventEmitter as I}from"node:events";import{existsSync as E,readFileSync as _,readdirSync as b,statSync as w,mkdirSync as x,writeFileSync as C,realpathSync as L}from"node:fs";import{homedir as
|
|
2
|
-
`,"utf8"),o.info(a,`MCP config injected into ${e}`);const n=p(
|
|
1
|
+
import{spawn as $}from"node:child_process";import{randomUUID as k}from"node:crypto";import{EventEmitter as I}from"node:events";import{existsSync as E,readFileSync as _,readdirSync as b,statSync as w,mkdirSync as x,writeFileSync as C,realpathSync as L}from"node:fs";import{homedir as g}from"node:os";import{join as p,dirname as R,resolve as T}from"node:path";import{fileURLToPath as j}from"node:url";import{log as o}from"../../core/log/index.js";import{killProcessGroup as f}from"../../core/runtime/spawn.js";import{InternalApiServer as G}from"../../core/mcp/internal-api-server.js";import{syncDefaultSkillsToDir as M}from"../../default-skills/index.js";import{buildSimpleProbeReport as O}from"../shared/probe-util.js";const a="agy-adapter";function D(y){try{return L(y)}catch{return y}}const m=p(g(),".gemini","antigravity-cli","log"),A=p(g(),".gemini","antigravity-cli","cache","last_conversations.json"),N=["\u4F60\u63A5\u5165\u7684\u662F Grix \u804A\u5929\u3002\u7528\u6237\u6D88\u606F\u88AB\u5305\u5728 <channel ...> \u6807\u7B7E\u91CC\uFF0C\u6807\u7B7E\u5C5E\u6027\u643A\u5E26\u672C\u8F6E\u4E0A\u4E0B\u6587\uFF1A","chat_id\uFF08\u5F53\u524D\u4F1A\u8BDD\uFF09\u3001event_id\uFF08\u672C\u8F6E\u4E8B\u4EF6\uFF09\u3001message_id\uFF08\u7528\u6237\u8FD9\u6761\u6D88\u606F\u7684 ID\uFF09\u3001user_id\uFF08\u53D1\u9001\u8005\uFF09\u3002","\u91CD\u8981\uFF1A\u4F60\u7684\u56DE\u590D\u5FC5\u987B\u8C03\u7528 grix \u7684 `grix_message_send` \u5DE5\u5177\u53D1\u9001\uFF08sessionId \u53D6\u81EA channel \u7684 chat_id\uFF0Ccontent \u4E3A\u4F60\u7684\u56DE\u590D\u5185\u5BB9\uFF09\uFF0C","\u4E0D\u8981\u53EA\u628A\u7B54\u6848\u6253\u5370\u5230\u6807\u51C6\u8F93\u51FA\u2014\u2014\u53EA\u6709\u8C03\u7528\u5DE5\u5177\u624D\u4F1A\u628A\u6D88\u606F\u771F\u6B63\u53D1\u7ED9\u7528\u6237\u3002","grix_message_send \u4F1A\u8FD4\u56DE\u4F60\u521A\u53D1\u51FA\u6D88\u606F\u7684 msg_id\u3002\u9700\u8981\u65F6\u4F60\u53EF\u4EE5\u7528\u8FD9\u4E2A ID \u505A\u540E\u7EED\u64CD\u4F5C\uFF0C\u4F8B\u5982\u8C03\u7528 grix_message_unsend\uFF08\u4F20 msgId\uFF09\u64A4\u56DE\u67D0\u6761\u6D88\u606F\u3002"].join(""),B=new Set(["send_msg","grix_reply"]),Q=new Set(["send_msg","grix_reply","delete_msg","session_send","grix_complete","grix_composing","grix_event_ack"]);class tt extends I{type="agy";config;callbacks;runtimeResolver;alive=!1;stopped=!1;internalApi=null;activeEventId=null;activeEventSessionId=null;activeProcess=null;completedEventIds=new Set;activeEventSentViaTool=!1;sessionConversationMap=new Map;conversationOutputCache=new Map;eventQueue=[];constructor(t,e,s){super(),this.config=t,this.callbacks=e,this.runtimeResolver=s}async start(){this.alive||(this.alive=!0,this.stopped=!1,await this.startInternalApiAndInjectMcp(),o.info(a,"AgyAdapter started"))}async startInternalApiAndInjectMcp(){try{this.internalApi=new G,this.internalApi.setInvokeHandler(async(r,l,h,u)=>this.handleToolInvoke(r,l,u)),await this.internalApi.start(0),o.info(a,`Internal API started at ${this.internalApi.url}`);const t=this.getMcpConfig(),e=p(g(),".gemini","config","mcp_config.json");x(R(e),{recursive:!0});let s={};try{E(e)&&(s=JSON.parse(_(e,"utf8")))}catch{}const i=s.mcpServers&&typeof s.mcpServers=="object"?s.mcpServers:{};i[t.name]={command:t.command,args:t.args},s.mcpServers=i,C(e,`${JSON.stringify(s,null,2)}
|
|
2
|
+
`,"utf8"),o.info(a,`MCP config injected into ${e}`);const n=p(g(),".gemini","skills"),c=M(n);c.length>0&&o.info(a,`Synced connector skills to ${n}: [${c.join(", ")}]`)}catch(t){o.warn(a,`Failed to start MCP tools (non-fatal): ${t instanceof Error?t.message:String(t)}`)}}async handleToolInvoke(t,e,s){let i=e;Q.has(t)&&this.activeEventSessionId&&(i.session_id==null||i.session_id==="")&&(i={...i,session_id:this.activeEventSessionId});const n=await this.callbacks.agentInvoke(t,i,s);return B.has(t)&&this.activeEventId&&(this.activeEventSentViaTool=!0),n}async stop(){if(!this.stopped){if(this.stopped=!0,this.alive=!1,this.activeProcess){try{f(this.activeProcess,"SIGKILL")}catch{}this.activeProcess=null}if(this.activeEventId&&this.activeEventSessionId)try{this.callbacks.sendEventResult(this.activeEventId,"canceled","adapter stopped")}catch{}if(this.activeEventId=null,this.activeEventSessionId=null,this.internalApi){try{await this.internalApi.stop()}catch{}this.internalApi=null}this.emit("exit",0),o.info(a,"AgyAdapter stopped")}}isAlive(){return this.alive}async createSession(t){return t.cwd??k()}async resumeSession(t,e){}async destroySession(t){}sendPrompt(t){const e=new I;return e.adapterSessionId=t.adapterSessionId,e.cancel=async()=>{e.emit("done",{status:"canceled"})},e}async cancel(t){}cancelCurrentRun(){if(!this.activeEventId||!this.activeProcess)return;const t=this.activeEventId;o.info(a,`cancelCurrentRun: killing process group for event ${t}`);try{f(this.activeProcess,"SIGKILL")}catch{}this.activeProcess=null,this.callbacks.sendEventResult(t,"canceled","restarted by user"),this.clearActiveEvent(t),this.drainQueue()}deliverInboundEvent(t){if(!this.stopped){if(this.completedEventIds.has(t.event_id)){o.debug(a,`Skipping duplicate event: ${t.event_id}`);return}if(this.callbacks.sendEventAck(t.event_id,t.session_id),this.activeEventId){o.info(a,`Queuing event ${t.event_id} (active: ${this.activeEventId})`),this.eventQueue.push(t);return}this.processEvent(t)}}deliverStopEvent(t,e){if(this.activeEventId===t&&this.activeProcess){o.info(a,`Stopping event ${t}`);try{f(this.activeProcess,"SIGKILL")}catch{}this.activeProcess=null,this.callbacks.sendEventResult(t,"canceled","stopped by user"),this.clearActiveEvent(t),this.drainQueue()}}setPermissionHandler(){}async ping(t){return this.alive}getStatus(){return{alive:this.alive,busy:this.activeEventId!==null,sessions:this.sessionConversationMap.size}}async probe(t){const e=this.getStatus();return O(this.config.command||"agy",{alive:e.alive,busy:e.busy,started:e.alive},t)}getActiveEventIds(){return this.activeEventId?[this.activeEventId]:[]}clearActiveEventForShutdown(){if(this.activeProcess)try{f(this.activeProcess,"SIGKILL")}catch{}this.activeEventId=null,this.activeEventSessionId=null,this.activeProcess=null,this.activeEventSentViaTool=!1}getMcpConfig(){if(!this.internalApi)return null;const t=T(j(import.meta.url),"../../../mcp/acp-mcp-server.js");return{name:"grix-connector-tools",command:process.execPath,args:[t,"--api-url",this.internalApi.url]}}processEvent(t){const{event_id:e,session_id:s}=t,i=this.runtimeResolver(s);if(!i.cwd){o.warn(a,`No working directory for session ${s}, event should have been intercepted by bridge`),this.callbacks.forceCompleteInternalEvent(e,s);return}this.activeEventId=e,this.activeEventSessionId=s,this.activeEventSentViaTool=!1,this.emit("eventStarted",e,s),o.info(a,`Processing event ${e} for session ${s}`);const n=this.buildAgyPrompt(t);this.spawnAgyPrint(e,s,n,i)}buildAgyPrompt(t){const s=[["chat_id",t.session_id],["event_id",t.event_id],["message_id",t.msg_id],["user_id",t.sender_id],["quoted_message_id",t.quoted_message_id]].filter(([,l])=>l!=null&&l!=="").map(([l,h])=>`${l}="${h}"`).join(" "),i=this.renderContextBlock(t),n=i?`${i}
|
|
3
3
|
|
|
4
4
|
${t.content}`:t.content,c=`<channel source="grix-agy" ${s}>
|
|
5
5
|
${n}
|
|
@@ -10,4 +10,4 @@ ${c}`}buildIdentityBlock(){const t=this.callbacks.getAgentProfile?.(),e=t?.agent
|
|
|
10
10
|
|
|
11
11
|
`}renderContextBlock(t){const e=t.context_messages_json;if(!e)return"";let s;try{const r=JSON.parse(e);if(!Array.isArray(r))return"";s=r}catch{return""}const i=String(t.session_type??"")==="2",n=String(t.msg_id??""),c=[];for(const r of s){const l=String(r?.msg_id??"");if(l&&l===n)continue;const h=String(r?.content??"").trim();if(!h)continue;const u=String(r?.sender_id??"");if(h.startsWith("[\u5F15\u7528\u6D88\u606F]")){const d=h.slice(6).replace(/^\s*\n?/,"").trim();c.push(i&&u?`[\u5F15\u7528\u6D88\u606F] (\u6765\u81EA ${u})\uFF1A${d}`:`[\u5F15\u7528\u6D88\u606F]\uFF1A${d}`)}else c.push(i&&u?`[${u}]\uFF1A${h}`:h)}return c.join(`
|
|
12
12
|
`)}spawnAgyPrint(t,e,s,i){const n=this.buildPrintArgs(s,i),c={...process.env,...this.config.env??{}};o.info(a,`Spawning: agy ${n.map(d=>d.includes(" ")?`"${d}"`:d).join(" ")}`);const r=this.getLatestLogFilePath(),l=$(this.config.command,n,{cwd:i.cwd,env:c,stdio:["pipe","pipe","pipe"],detached:!0});this.activeProcess=l;const h=[],u=[];l.stdout?.on("data",d=>{h.push(d)}),l.stderr?.on("data",d=>{u.push(d)}),l.on("error",d=>{o.error(a,`Process spawn error for event ${t}: ${d.message}`),this.handleProcessResult(t,e,"",`spawn error: ${d.message}`,i)}),l.on("close",d=>{if(o.info(a,`Process exited for event ${t} with code ${d}`),this.activeEventId!==t)return;const v=Buffer.concat(h).toString("utf-8"),P=Buffer.concat(u).toString("utf-8");if(d!==0){const S=P.trim()||`process exited with code ${d}`;this.handleProcessResult(t,e,v,S,i)}else v.trim()?this.handleProcessResult(t,e,v,"",i):this.activeEventSentViaTool?this.handleProcessResult(t,e,v,"",i):setTimeout(()=>{const S=this.extractLogError(r);this.handleProcessResult(t,e,v,S,i)},300)}),l.stdin?.end()}buildPrintArgs(t,e){const s=[];return e.modelId&&s.push("--model",e.modelId),e.cwd&&s.push("--add-dir",e.cwd),s.push("--dangerously-skip-permissions"),e.conversationId&&s.push("--conversation",e.conversationId),s.push("-p",t),s}readAgyConversationId(t){try{if(E(A)){const e=JSON.parse(_(A,"utf-8"));return e[D(t)]||e[t]||void 0}}catch(e){o.debug(a,`readAgyConversationId: ${e}`)}}getLatestLogFilePath(){try{if(!E(m))return null;const t=b(m).filter(e=>e.startsWith("cli-")&&e.endsWith(".log")).map(e=>({name:e,path:p(m,e),mtime:w(p(m,e)).mtimeMs})).sort((e,s)=>s.mtime-e.mtime);return t.length>0?t[0].path:null}catch{return null}}extractLogError(t){try{const e=this.getLatestLogFilePath();if(!e)return o.info(a,"extractLogError: no log files found"),"agy completed without output";const s=_(e,"utf-8");if(!s)return o.info(a,`extractLogError: log file is empty: ${e}`),"agy completed without output";const i=s.split(`
|
|
13
|
-
`).filter(n=>n.startsWith("E")).map(n=>{const c=n.indexOf("] ");return c>=0?n.slice(c+2):n}).filter(n=>n.length>0);if(i.length>0){const n=i[i.length-1];return o.info(a,`Extracted error from agy log: ${n.slice(0,120)}`),n.length>500?n.slice(0,500)+"...":n}return"agy completed without output (possible auth or quota issue)"}catch(e){return o.info(a,`extractLogError: ${e}`),"agy completed without output"}}handleProcessResult(t,e,s,i,n){if(this.activeEventId===t){if(i&&!s.trim()){o.error(a,`Event ${t} failed: ${i}`);const r=i.includes("RESOURCE_EXHAUSTED")||i.includes("quota")?"The agy API quota has been exhausted. Please try again later.":`agy execution failed: ${i}`;this.callbacks.sendStreamChunk(t,e,r,1,!0),this.callbacks.sendEventResult(t,"failed",i)}else{const c=s.trim();if(c){const r=n.cwd,l=r?this.extractDelta(c,r):c;r&&this.conversationOutputCache.set(r,c),l&&!this.activeEventSentViaTool&&this.callbacks.sendStreamChunk(t,e,l,1,!0)}if(n.cwd){const r=this.readAgyConversationId(n.cwd);r&&r!==n.conversationId&&(this.sessionConversationMap.set(e,r),this.callbacks.persistConversationId(e,r))}this.callbacks.sendEventResult(t,"responded")}this.clearActiveEvent(t),this.drainQueue()}}extractDelta(t,e){const s=this.conversationOutputCache.get(e);return s&&t.startsWith(s)?t.slice(s.length).trim():t}clearActiveEvent(t){const e=t??this.activeEventId;if(e&&(this.completedEventIds.add(e),this.completedEventIds.size>1e3)){const s=Array.from(this.completedEventIds);this.completedEventIds.clear();for(let i=500;i<s.length;i++)this.completedEventIds.add(s[i])}this.activeEventId=null,this.activeEventSessionId=null,this.activeProcess=null,this.activeEventSentViaTool=!1,e&&this.emit("eventDone",e)}drainQueue(){if(this.stopped||this.activeEventId)return;const t=this.eventQueue.shift();t&&(o.info(a,`Draining queued event ${t.event_id}`),this.processEvent(t))}}export{tt as AgyAdapter};
|
|
13
|
+
`).filter(n=>n.startsWith("E")).map(n=>{const c=n.indexOf("] ");return c>=0?n.slice(c+2):n}).filter(n=>n.length>0);if(i.length>0){const n=i[i.length-1];return o.info(a,`Extracted error from agy log: ${n.slice(0,120)}`),n.length>500?n.slice(0,500)+"...":n}return"agy completed without output (possible auth or quota issue)"}catch(e){return o.info(a,`extractLogError: ${e}`),"agy completed without output"}}handleProcessResult(t,e,s,i,n){if(this.activeEventId===t){if(i&&!s.trim()){o.error(a,`Event ${t} failed: ${i}`);const r=i.includes("RESOURCE_EXHAUSTED")||i.includes("quota")?"The agy API quota has been exhausted. Please try again later.":`agy execution failed: ${i}`;this.callbacks.sendStreamChunk(t,e,r,1,!0,`agy-${t}`),this.callbacks.sendEventResult(t,"failed",i)}else{const c=s.trim();if(c){const r=n.cwd,l=r?this.extractDelta(c,r):c;r&&this.conversationOutputCache.set(r,c),l&&!this.activeEventSentViaTool&&this.callbacks.sendStreamChunk(t,e,l,1,!0,`agy-${t}`)}if(n.cwd){const r=this.readAgyConversationId(n.cwd);r&&r!==n.conversationId&&(this.sessionConversationMap.set(e,r),this.callbacks.persistConversationId(e,r))}this.callbacks.sendEventResult(t,"responded")}this.clearActiveEvent(t),this.drainQueue()}}extractDelta(t,e){const s=this.conversationOutputCache.get(e);return s&&t.startsWith(s)?t.slice(s.length).trim():t}clearActiveEvent(t){const e=t??this.activeEventId;if(e&&(this.completedEventIds.add(e),this.completedEventIds.size>1e3)){const s=Array.from(this.completedEventIds);this.completedEventIds.clear();for(let i=500;i<s.length;i++)this.completedEventIds.add(s[i])}this.activeEventId=null,this.activeEventSessionId=null,this.activeProcess=null,this.activeEventSentViaTool=!1,e&&this.emit("eventDone",e)}drainQueue(){if(this.stopped||this.activeEventId)return;const t=this.eventQueue.shift();t&&(o.info(a,`Draining queued event ${t.event_id}`),this.processEvent(t))}}export{tt as AgyAdapter};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{readdirSync as
|
|
2
|
-
`)){const a=
|
|
1
|
+
import{readdirSync as D,readFileSync as g,existsSync as d,statSync as S}from"node:fs";import{dirname as O,join as t,resolve as F}from"node:path";import{homedir as L}from"node:os";import{log as M}from"../../core/log/index.js";const j=".grix-managed";function x(r){const e=r.trim();if(!e.startsWith("---"))return{name:"",description:""};const i=e.indexOf("---",3);if(i===-1)return{name:"",description:""};const n=e.slice(3,i).trim();let s="",o="",u;for(const p of n.split(`
|
|
2
|
+
`)){const a=p.indexOf(":");if(a===-1)continue;const f=p.slice(0,a).trim();let l=p.slice(a+1).trim();(l.startsWith('"')&&l.endsWith('"')||l.startsWith("'")&&l.endsWith("'"))&&(l=l.slice(1,-1)),f==="name"?s=l:f==="description"?o=l:f==="trigger"&&(u=l)}return{name:s,description:o,trigger:u}}function C(r,e,i){if(!d(r))return[];const n=[];try{for(const s of D(r,{withFileTypes:!0})){const o=t(r,s.name);if(!b(s,o)||s.name.startsWith("."))continue;const u=t(o,"SKILL.md");if(d(u))try{const p=g(u,"utf-8"),a=x(p);a.name&&n.push({name:a.name,description:a.description,trigger:a.trigger,source:e,pluginName:i,filePath:u,managed:d(t(o,j))})}catch{}}}catch{}return n}function c(r,e,i){if(!d(r))return[];const n=i?.maxDepth??6,s=i?.includeHiddenDirs??!1,o=[],u=(p,a)=>{if(a>n)return;let f;try{f=D(p,{withFileTypes:!0,encoding:"utf8"})}catch{return}for(const l of f){const m=t(p,l.name);if(!b(l,m)||!s&&l.name.startsWith("."))continue;const h=t(m,"SKILL.md");if(d(h))try{const w=g(h,"utf-8"),k=x(w);k.name&&o.push({name:k.name,description:k.description,trigger:k.trigger,source:e,pluginName:i?.pluginName,filePath:h,managed:d(t(m,j))})}catch{}u(m,a+1)}};return u(r,0),o}function y(r){return r.map(e=>({...e,managed:!0}))}function b(r,e){if(r.isDirectory())return!0;if(!r.isSymbolicLink())return!1;try{return S(e).isDirectory()}catch{return!1}}function E(r){const e=[],i=new Set;for(const n of r){const s=n.name.trim().toLowerCase();i.has(s)||(i.add(s),e.push(n))}return e}function H(r){const e=[],i=new Set;let n=F(r);for(;;){i.has(n)||(e.push(n),i.add(n));const s=O(n);if(s===n)break;n=s}return e}function I(r){const e=t(r,".claude","plugins","installed_plugins.json");if(!d(e))return[];const i=[];try{const n=g(e,"utf-8"),o=JSON.parse(n)?.plugins;if(!o||typeof o!="object")return i;for(const[u,p]of Object.entries(o))if(Array.isArray(p))for(const a of p){const f=a?.installPath;if(!f||!d(f))continue;const l=t(f,"skills"),m=u.split("@")[0],h=y(C(l,"plugin",m));i.push(...h)}}catch{}return i}function N(r,e){const i=(r??"").trim().toLowerCase(),n=(e??"").trim().toLowerCase();return i==="claude"?"claude":i==="codex"?"codex":i==="pi"?"pi":i==="cursor"?"cursor":i==="codewhale"?"codewhale":i==="opencode"?"opencode":i==="agy"?"gemini":i==="openhuman"?"none":n==="kiro"?"kiro":n==="qwen"?"qwen":n==="reasonix"?"reasonix":n==="kimi"?"kimi":n==="hermes"||n==="copilot"?"none":"gemini"}function q(r){const e=[],i=r.homeDir??L();switch(r.mode){case"claude":{const s=t(i,".claude","skills");e.push(...c(s,"global")),r.projectDir&&e.push(...c(t(r.projectDir,".claude","skills"),"project")),e.push(...I(i));break}case"codex":{const s=process.env.CODEX_HOME?.trim()||t(i,".codex");if(r.projectDir)for(const o of H(r.projectDir))e.push(...c(t(o,".agents","skills"),"project")),e.push(...c(t(o,".codex","skills"),"project"));e.push(...c(t(i,".agents","skills"),"codex")),e.push(...c(t(s,"skills"),"codex")),e.push(...y(c(t(s,"skills",".system"),"codex")));break}case"gemini":{e.push(...c(t(i,".gemini","skills"),"gemini")),e.push(...c(t(i,".agents","skills"),"gemini")),r.projectDir&&(e.push(...c(t(r.projectDir,".gemini","skills"),"project")),e.push(...c(t(r.projectDir,".agents","skills"),"project")));break}case"pi":{e.push(...c(t(i,".pi","agent","skills"),"pi")),r.projectDir&&e.push(...c(t(r.projectDir,".pi","skills"),"project"));break}case"kiro":{e.push(...c(t(i,".kiro","skills"),"kiro")),r.projectDir&&e.push(...c(t(r.projectDir,".kiro","skills"),"project"));break}case"qwen":{e.push(...c(t(i,".qwen","skills"),"global")),r.projectDir&&e.push(...c(t(r.projectDir,".qwen","skills"),"project"));break}case"cursor":{e.push(...c(t(i,".cursor","skills"),"global")),r.projectDir&&e.push(...c(t(r.projectDir,".cursor","skills"),"project"));break}case"codewhale":{e.push(...c(t(i,".codewhale","skills"),"global")),r.projectDir&&e.push(...c(t(r.projectDir,".codewhale","skills"),"project"));break}case"opencode":{const s=process.env.XDG_CONFIG_HOME?.trim()||t(i,".config");e.push(...c(t(s,"opencode","skills"),"global"));break}case"reasonix":{e.push(...c(t(i,".reasonix","skills"),"global"));break}case"kimi":{const s=process.env.KIMI_CODE_HOME?.trim()||t(i,".kimi-code");e.push(...c(t(s,"skills"),"global"));break}case"none":break}const n=E(e);return M.info("skill-scanner",`Scanned skills: mode=${r.mode} count=${n.length}`),n}export{j as MANAGED_MARKER,E as dedupeSkills,y as markManaged,x as parseSkillFrontmatter,N as resolveSkillScanMode,c as scanSkillTree,q as scanSkills};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const f=2e5,_=1e6;function r(e){const s=Number(e??0);return Number.isFinite(s)&&s>0?s:0}function d(e,s={}){const n=r(e.inputTokens),t=r(e.cacheReadTokens),c=r(e.cacheWriteTokens),i=n+t+c;if(i<=0)return 0;const o=Math.max(n,t)+c;let u=i;n>1e4&&t>1e4&&i>o*1.5&&(u=o);const a=r(s.windowSize)||2e5,T=Math.max(1,Math.floor(r(s.apiRounds)||1));return T>1&&u>a*1.2&&(u=Math.ceil(u/T)),u>a&&(u=a),u}function m(e,s=0){const n=String(e??"").trim();let t=2e5;if(!n||/^auto$/i.test(n))t=1e6;else if(/context\s*=\s*1m\b/i.test(n))t=1e6;else if(/context\s*=\s*(\d+)k\b/i.test(n)){const i=n.match(/context\s*=\s*(\d+)k\b/i),o=Number(i?.[1]??0);Number.isFinite(o)&&o>0&&(t=o*1e3)}else if(/context\s*=\s*(\d+)\b/i.test(n)){const i=n.match(/context\s*=\s*(\d+)\b/i),o=Number(i?.[1]??0);Number.isFinite(o)&&o>=1e4&&(t=o)}return r(s)>t&&t<1e6&&(t=1e6),t}function N(e,s){const n=Math.max(0,Number.isFinite(e)?e:0),t=Math.max(1,Number.isFinite(s)&&s>0?s:2e5),c=Math.min(n,t),i=Math.min(100,c/t*100);return{usedPercentage:i,remainingPercentage:Math.max(0,100-i),usedTokens:c,sizeTokens:t}}function x(e,s,n={}){const t=r(e.inputTokens)+r(e.cacheReadTokens)+r(e.cacheWriteTokens);if(t<=0)return null;const c=m(s,t),i=d(e,{apiRounds:n.apiRounds,windowSize:c});return i<=0?null:N(i,c)}export{N as buildCursorContextWindow,x as buildCursorContextWindowFromUsage,d as estimateCursorContextTokens,m as resolveCursorContextWindowSize};
|
|
@@ -1,8 +1,20 @@
|
|
|
1
|
-
import{createInterface as
|
|
2
|
-
`)){const r=o.trim();if(!r||r.toLowerCase().includes("available model"))continue;const a=r.match(/^(\S+)\s+-\s+(.+)$/);a&&n.push({id:a[1],displayName:a[2].trim()})}n.length>0&&(this._availableModels=n,h.info("cursor-adapter",`Loaded ${n.length} available models`))}catch(e){h.warn("cursor-adapter",`Failed to refresh models: ${e instanceof Error?e.message:String(e)}`)}}async stop(){this.stopped=!0,this.alive=!1;for(const e of this.activeBySession.values())S(e.child,"SIGTERM");this.activeBySession.clear(),this.internalApi&&(await this.internalApi.stop(),this.internalApi=null)}isAlive(){return this.alive}async createSession(e){const t=`cursor-${Date.now()}-${++this.sessionSeq}`;return this.sessions.add(t),this.sessionRuntime.set(t,{cwd:typeof e.cwd=="string"?e.cwd:void 0,modelId:typeof e.modelId=="string"?e.modelId:void 0,modeId:typeof e.modeId=="string"?e.modeId:void 0}),t}async resumeSession(e,t){this.sessions.add(e);const i=this.sessionRuntime.get(e)??{};this.sessionRuntime.set(e,{...i,cwd:typeof t?.cwd=="string"?t.cwd:i.cwd,modelId:typeof t?.modelId=="string"?t.modelId:i.modelId,modeId:typeof t?.modeId=="string"?t.modeId:i.modeId})}onAgentProfileChanged(){this.identity.onProfileChanged()}async destroySession(e){this.sessions.delete(e),this.sessionRuntime.delete(e),this.identity.forgetSession(e)}sendPrompt(e){const t=new D(e.adapterSessionId);if(!this.alive||this.stopped)return queueMicrotask(()=>t.emitError(new Error("adapter not running"))),t;const i=this.inboundQueue.shift()??{event_id:`cursor-evt-${Date.now()}`,session_id:e.adapterSessionId,content:e.text},s=e.adapterSessionId,n=this.pendingBySession.get(s)??[];return n.push({event:i,request:e,handle:t}),this.pendingBySession.set(s,n),this.tryStartNext(s),t}tryStartNext(e){if(this.activeBySession.has(e)||!this.alive||this.stopped)return;const t=this.pendingBySession.get(e);if(!t||t.length===0)return;const i=t.shift();t.length===0?this.pendingBySession.delete(e):this.pendingBySession.set(e,t),i&&this.startJob(i,!1,0)}startJob(e,t,i){const{event:s,request:n,handle:o}=e,r=n.adapterSessionId,a=this.sessionRuntime.get(n.adapterSessionId)??{},d=this.config.options??{},l=[...this.config.args??[]];l.push("-p","--output-format","stream-json","--stream-partial-output"),d.trust!==!1&&l.push("--trust");const g=a.cwd||d.workspace;this.internalApi&&g&&(this.ensureWorkspaceMcpAndSkills(g),l.push("--approve-mcps")),g&&l.push("--workspace",g);const I=a.modelId||d.model;I&&l.push("--model",I);const R=b(a.modeId||d.mode)??"approval";R==="full_auto"?l.push("--yolo"):R==="plan"&&l.push("--plan");const w=a.cursorSessionId??this.bindingStore?.getAcpSessionId(n.adapterSessionId),A=!!(w&&d.use_continue!==!1&&!t);A&&l.push("--resume",w),l.push(this.buildPromptText(n));const _={...process.env,...this.config.env??{}},F=E(this.config.command,typeof _.PATH=="string"?_.PATH:void 0);h.info("cursor-adapter",`job start: event=${s.event_id} session=${s.session_id} retry=${i}`);let p;try{p=$(F,l,{cwd:g||process.cwd(),env:_,stdio:["ignore","pipe","pipe"]}).process}catch(u){this.finishActive(r,"failed",`spawn failed: ${u instanceof Error?u.message:String(u)}`);return}this.callbacks.sendEventAck(s.event_id,s.session_id);const c={event:s,request:n,handle:o,child:p,seq:0,done:!1,stderr:"",timer:null,idleTimer:null,toolCallsInFlight:new Set,retryCount:i,usedContinue:A,workspace:g||process.cwd(),args:l};this.activeBySession.set(r,c),this.notifyBindingReady(r),this.armActiveIdleTimer(r);const y=n.timeoutMs&&n.timeoutMs>0?n.timeoutMs:0;y>0&&(c.timer=setTimeout(()=>{c.done||(S(p,"SIGTERM"),this.finishActive(r,"failed",`cursor agent timeout after ${y}ms`))},y)),O({input:p.stdout}).on("line",u=>this.handleStdoutLineForActive(c,u)),p.stderr?.on("data",u=>{const v=c.stderr+String(u??"");c.stderr=v.length>k.STDERR_MAX_CHARS?v.slice(-k.STDERR_MAX_CHARS):v}),p.once("error",u=>{this.finishActive(r,"failed",`spawn failed: ${String(u?.message??u)}`)}),p.once("close",u=>{if(!c.done)if((u??0)===0)this.finishActive(r,"responded");else{if(this.shouldRetryWithoutContinue(c)){c.timer&&clearTimeout(c.timer),c.idleTimer&&clearTimeout(c.idleTimer),this.activeBySession.delete(r),this.startJob(e,!0,c.retryCount+1);return}const v=c.stderr.trim()||`cursor agent exited with code ${u??-1}`;this.finishActive(r,"failed",v)}})}async cancel(e){const t=this.activeBySession.get(e);t&&(S(t.child,"SIGTERM"),this.finishActive(e,"canceled","canceled"))}deliverInboundEvent(e){const t=String(e.session_id??"").trim();if(!t){h.warn("cursor-adapter",`Dropping event ${e.event_id}: missing session_id`),this.callbacks.sendEventResult(e.event_id,"failed","missing session_id");return}if(!this.alive||this.stopped){h.warn("cursor-adapter",`Dropping event ${e.event_id}: adapter not running`),this.callbacks.sendEventAck(e.event_id,t),this.callbacks.sendEventResult(e.event_id,"failed","adapter not running");return}const i={adapterSessionId:t,text:String(e.content??"")},s=new D(t),n=this.pendingBySession.get(t)??[];n.push({event:e,request:i,handle:s}),this.pendingBySession.set(t,n),h.info("cursor-adapter",`inbound queued: event=${e.event_id} session=${t} depth=${n.length}`),this.tryStartNext(t)}killChildWithFallback(e){S(e,"SIGTERM"),setTimeout(()=>{if(e.exitCode===null&&e.signalCode===null)try{S(e,"SIGKILL")}catch{}},5e3).unref()}deliverStopEvent(e,t){if(t){const i=this.activeBySession.get(t);if(i?.event.event_id===e){this.killChildWithFallback(i.child),this.finishActive(t,"canceled","stopped");return}const s=this.pendingBySession.get(t)??[],n=s.findIndex(o=>o.event.event_id===e);if(n>=0){const[o]=s.splice(n,1);s.length===0?this.pendingBySession.delete(t):this.pendingBySession.set(t,s),this.callbacks.sendEventAck(o.event.event_id,o.event.session_id),this.callbacks.sendEventResult(o.event.event_id,"canceled","stopped"),o.handle.emitDone({status:"canceled",error:"stopped"})}return}for(const[i,s]of this.activeBySession.entries())if(s.event.event_id===e){this.killChildWithFallback(s.child),this.finishActive(i,"canceled","stopped");return}for(const[i,s]of this.pendingBySession.entries()){const n=s.findIndex(r=>r.event.event_id===e);if(n<0)continue;const[o]=s.splice(n,1);s.length===0?this.pendingBySession.delete(i):this.pendingBySession.set(i,s),this.callbacks.sendEventAck(o.event.event_id,o.event.session_id),this.callbacks.sendEventResult(o.event.event_id,"canceled","stopped"),o.handle.emitDone({status:"canceled",error:"stopped"});return}}async handleLocalAction(e){const t=String(e.action_type??"").trim().toLowerCase(),i=e.params??{},s=String(i.session_id??"").trim(),n=e.action_id;switch(t){case"set_model":{const o=String(i.model_id??"").trim();if(!o||!s)return this.callbacks.sendLocalActionResult(n,"failed",void 0,"invalid_params","model_id and session_id are required"),{handled:!0,kind:"set_model"};const r=this.sessionRuntime.get(s)??{};this.sessionRuntime.set(s,{...r,modelId:o}),this.notifyBindingReady(s);const a=this.getToolbarMeta(s);return this.callbacks.sendLocalActionResult(n,"ok",{outcome:"model_set",session_context:{model_id:o,mode_id:a.mode_id,modelId:o,modeId:a.mode_id},model_id:o,mode_id:a.mode_id,available_models:this.availableModels,available_modes:this.availableModes}),{handled:!0,kind:"set_model"}}case"set_mode":{const o=b(i.mode_id);if(!o||!s)return this.callbacks.sendLocalActionResult(n,"failed",void 0,"invalid_params","mode_id must be approval, full_auto, or plan and session_id is required"),{handled:!0,kind:"set_mode"};const r=this.sessionRuntime.get(s)??{};this.sessionRuntime.set(s,{...r,modeId:o}),this.notifyBindingReady(s);const a=this.getToolbarMeta(s);return this.callbacks.sendLocalActionResult(n,"ok",{outcome:"mode_set",session_context:{model_id:a.model_id,mode_id:o,modelId:a.model_id,modeId:o},model_id:a.model_id,mode_id:o,available_models:this.availableModels,available_modes:this.availableModes}),{handled:!0,kind:"set_mode"}}case"get_context":{const o=s?this.sessionRuntime.get(s):void 0,r=this.config.options??{},a=this.getToolbarMeta(s);return this.callbacks.sendLocalActionResult(n,"ok",{session_context:{model_id:a.model_id,mode_id:a.mode_id,cwd:o?.cwd??r.workspace??null},model_id:a.model_id,mode_id:a.mode_id,modelId:a.model_id,modeId:a.mode_id,cwd:o?.cwd??r.workspace??null,available_models:this.availableModels,available_modes:this.availableModes}),{handled:!0,kind:"get_context"}}case"get_rate_limits":{const o=this.getRateLimitsSnapshot();return this.callbacks.sendLocalActionResult(n,"ok",o),{handled:!0,kind:"get_rate_limits"}}case"get_session_usage":{const o=s?this.getUsageSnapshot(s):null;return o?this.callbacks.sendLocalActionResult(n,"ok",{adapterType:"cursor",available:!0,sampledAt:o.sampledAt,turns:o.turns,tokenUsage:o.total}):this.callbacks.sendLocalActionResult(n,"ok",{adapterType:"cursor",available:!1,sampledAt:null,turns:0,tokenUsage:null}),{handled:!0,kind:"get_session_usage"}}case"session_control":{const o=String(i.verb??"").trim().toLowerCase();if(o==="restart"&&s)this.deliverStopEvent("__all__",s),this.callbacks.sendLocalActionResult(n,"ok",{outcome:"restarted"});else if(o==="status"){const r=this.sessionRuntime.get(s),a=this.config.options??{},d=this.getToolbarMeta(s),l=this.activeBySession.has(s);this.callbacks.sendLocalActionResult(n,"ok",{verb:"status",status:l?"running":"idle",session_context:{model_id:d.model_id,mode_id:d.mode_id,cwd:r?.cwd??a.workspace??null},model_id:d.model_id,mode_id:d.mode_id,modelId:d.model_id,modeId:d.mode_id,cwd:r?.cwd??a.workspace??null,available_models:this.availableModels,available_modes:this.availableModes})}else this.callbacks.sendLocalActionResult(n,"failed",void 0,"invalid_verb",`Unsupported verb: ${o}`);return{handled:!0,kind:"session_control"}}default:return{handled:!1,kind:"unsupported"}}}setPermissionHandler(e){this.permissionHandler=e}async ping(e){if(!this.alive||this.stopped||(this.config.options??{}).mcp_tools!==!1&&this.internalApi===null)return!1;for(const i of this.activeBySession.values()){const s=i.child?.pid;if(s&&!V(s))return!1}return!0}getStatus(){let e=0;for(const t of this.pendingBySession.values())e+=t.length;return{alive:this.alive,busy:this.activeBySession.size>0,sessions:this.sessions.size,details:{queueDepth:e+this.inboundQueue.length,activeSessions:this.activeBySession.size}}}getActiveEventIds(){const e=[];for(const t of this.activeBySession.values())t.event.event_id&&e.push(t.event.event_id);return e}clearActiveEventForShutdown(){this.activeBySession.clear()}getMcpConfig(){return null}async probe(e){const t=this.getStatus();return{...await J(this.config.command||"cursor-agent",{alive:t.alive,busy:t.busy,started:this.alive},e),session:this.probeSessionRecord()}}probeSessionRecord(){const e=this.firstKnownCursorSessionId(),t=e?Z(e):null;if(!t)return{recordPath:null,lastActivityMs:null,freshMs:null};try{const i=U(t);return{recordPath:t,lastActivityMs:i.mtimeMs,freshMs:Date.now()-i.mtimeMs}}catch{return{recordPath:t,lastActivityMs:null,freshMs:null}}}firstKnownCursorSessionId(){for(const e of this.activeBySession.keys()){const t=this.sessionRuntime.get(e);if(t?.cursorSessionId)return t.cursorSessionId}for(const e of this.sessionRuntime.values())if(e.cursorSessionId)return e.cursorSessionId;return null}getUsageSnapshot(e){return this.lastUsageBySession.get(e)??null}getRateLimitsSnapshot(){return{adapterType:"cursor",available:!1,cached:!1,sampledAt:null,rateLimits:null,contextWindow:null,tokenUsage:null}}getToolbarMeta(e){const t=this.config.options??{},i=e||String(t.aibotSessionId??""),s=i?this.sessionRuntime.get(i):void 0,n=String(s?.modelId||t.model||"auto"),o=b(s?.modeId||t.mode)??"approval";return{model_id:n,mode_id:o,currentModelId:n,currentModeId:o,available_models:this.availableModels,available_modes:this.availableModes}}notifyBindingReady(e){if(!this.callbacks.sendUpdateBindingCard)return;const t=this.config.options??{},i=e||String(t.aibotSessionId??"");if(!i)return;const s=this.sessionRuntime.get(i),n=String(s?.cwd||t.workspace||"");n&&this.callbacks.sendUpdateBindingCard(i,this.activeBySession.has(i)?"busy":"ready",n,this.getToolbarMeta(i))}buildPromptText(e){let t;return!e.contextMessages||e.contextMessages.length===0?t=e.text:t=`${e.contextMessages.map(s=>`[${s.senderId}] ${s.content}`).join(`
|
|
1
|
+
import{createInterface as x}from"node:readline";import{EventEmitter as B}from"node:events";import{randomUUID as z}from"node:crypto";import{mkdirSync as $,readFileSync as q,writeFileSync as L,readdirSync as J,statSync as G}from"node:fs";import{join as b,resolve as X}from"node:path";import{fileURLToPath as K}from"node:url";import{homedir as E}from"node:os";import{GRIX_PATHS as Y,log as u}from"../../core/log/index.js";import{IdentityInjector as V}from"../shared/identity-injector.js";import{buildSimpleProbeReport as Z}from"../shared/probe-util.js";import{InternalApiServer as ee}from"../../core/mcp/internal-api-server.js";import{syncDefaultSkillsToDir as te}from"../../default-skills/index.js";import{resolveCommandPath as R,spawnCommand as T,killProcessGroup as w}from"../../core/runtime/spawn.js";import{SessionBindingStore as se}from"../../core/persistence/session-binding-store.js";import{fetchCursorParameterizedModels as ne}from"./fetch-models.js";import{fetchCursorAccountUsage as ie}from"./fetch-usage.js";import{buildCursorContextWindow as re,buildCursorContextWindowFromUsage as oe,resolveCursorContextWindowSize as ae}from"./context-window.js";const le=12e4,P=300*1e3,de=6e5,ce=8192,ue="Summarize this conversation for handoff to a fresh chat. Include: goals, key decisions, important file paths, current status, and next steps. Be concise but complete. Output ONLY the summary text.",D=12e4,N=new Set(["completed","failed","error","errored","canceled","cancelled","rejected"]);function me(l){const e=l.replace(/ToolCall$/i,"").trim();return e?e.charAt(0).toUpperCase()+e.slice(1):l||"tool"}function O(l){const e=String(l.call_id??l.callId??"").trim(),t=l.tool_call??l.toolCall;if(t&&typeof t=="object"&&!Array.isArray(t)){const n=Object.keys(t);if(n.length>0){const s=n[0],i=t[s],r=i&&typeof i=="object"&&!Array.isArray(i)?i:null;return{toolName:me(s),toolInput:r?.args??r?.arguments??{},callId:e}}}return{toolName:String(l.name??l.tool_name??l.toolName??"tool").trim()||"tool",toolInput:l.args??l.tool_input??l.toolInput??{},callId:e}}function j(l){return String(l??"").trim().toLowerCase().replace(/[^a-z0-9]/g,"")}function U(l){const e=j(l);return e==="askquestion"||e==="askuserquestion"}function Q(l){const e=j(l);return e==="createplan"||e==="exitplanmode"}function he(l){if(!Array.isArray(l))return;const e=l.map(t=>{if(typeof t=="string")return t.trim();if(!t||typeof t!="object")return"";const n=t,s=String(n.label??"").trim();if(s)return s;const i=String(n.value??"").trim();if(i)return i;const r=String(n.text??"").trim();return r||""}).filter(t=>t.length>0);return e.length>0?e:void 0}function fe(l){if(!l||typeof l!="object"||Array.isArray(l))return null;const e=l,t=String(e.title??"").trim(),n=e.questions;if(!Array.isArray(n)||n.length===0)return null;const s=[];for(let i=0;i<n.length;i++){const r=n[i];if(!r||typeof r!="object")continue;const a=r,o=t||`Question ${i+1}`,d=String(a.header??a.id??a.question??o).trim()||o,c=String(a.prompt??a.question??"").trim();if(!c)continue;const h=he(a.options),S=a.multiSelect===!0||a.multi_select===!0;s.push({header:d,prompt:c,...h?{options:h}:{},...S?{multi_select:!0}:{}})}return s.length>0?s:null}function pe(l){if(!l||typeof l!="object"||Array.isArray(l))return null;const e=l,t=String(e.name??e.title??"").trim(),n=String(e.overview??"").trim(),s=String(e.plan??"").trim();if(!s&&!n&&!t)return null;const i=[];return t&&i.push(`# ${t}`),n&&i.push(n),s&&i.push(s),i.join(`
|
|
2
|
+
|
|
3
|
+
`)}function ge(l){if(!l||typeof l!="object"||Array.isArray(l))return null;const e=l,t=String(e.type??"").trim().toLowerCase();if(!t)return null;switch(t){case"tool_call":{const n=String(e.subtype??"").trim().toLowerCase(),s=O(e);return U(s.toolName)||Q(s.toolName)?null:n==="started"||n==="start"?{type:"tool_call",payload:{tool_name:s.toolName,...s.callId?{tool_call_id:s.callId}:{}}}:(N.has(n),null)}case"error":return{type:"error",payload:{message:String(e.message??e.error??e.result??"cursor error").trim()||"cursor error"}};case"result":{const n=String(e.subtype??"").trim().toLowerCase();return e.is_error===!0||n==="error"?{type:"error",payload:{message:String(e.result??e.message??"cursor run failed").trim()||"cursor run failed"}}:null}default:return null}}function ye(l,e){if(!l||typeof l!="object"||Array.isArray(l))return"";const t=l;if(String(t.type??"")!=="assistant")return"";const n=t.model_call_id??t.modelCallId;if(n!=null&&String(n).trim()!=="")return"";const s=t.message,i=s&&typeof s=="object"&&!Array.isArray(s)?s.content:void 0;if(!Array.isArray(i))return"";const r=i.map(a=>{if(!a||typeof a!="object"||Array.isArray(a))return"";const o=a;return o.type==="text"?String(o.text??""):""}).filter(Boolean).join("");if(!r)return"";if(e){if(r===e||e.startsWith(r)&&r.length<e.length)return"";if(r.startsWith(e))return r.slice(e.length)}return r}const F=[{id:"approval",displayName:"\u4EBA\u5DE5\u786E\u8BA4"},{id:"full_auto",displayName:"\u81EA\u7531\u6A21\u5F0F"},{id:"plan",displayName:"\u8BA1\u5212\u6A21\u5F0F"}];function C(l){const e=String(l??"").trim().toLowerCase();return F.some(t=>t.id===e)?e:null}function Se(l){try{return process.kill(l,0),!0}catch(e){return e.code==="EPERM"}}function W(l,e,t){if(t<0)return null;let n;try{n=J(l,{withFileTypes:!0})}catch{return null}for(const s of n){const i=b(l,s.name);if(s.isDirectory()){const r=W(i,e,t-1);if(r)return r}else if(s.isFile()&&s.name.endsWith(e))return i}return null}function _e(l,e){if(!l)return null;const t=e||b(E(),".cursor","projects");return W(t,`${l}.jsonl`,6)}function ve(l){const e=l.context_messages_json;if(e)try{const t=JSON.parse(e);return!Array.isArray(t)||t.length===0?void 0:t.map(n=>({senderId:String(n.sender_id??"unknown"),content:String(n.content??"")}))}catch{return}}function be(l,e){const t=[String(l??"")],n=String(e.quoted_message_id??"").trim();n&&t.push(`[quoted_message_id] ${n}`);const s=String(e.attachments_json??"").trim();return s&&t.push(`[attachments_json]
|
|
4
|
+
${s}`),t.filter(i=>i.length>0).join(`
|
|
5
|
+
|
|
6
|
+
`)}class H extends B{adapterSessionId;constructor(e){super(),this.adapterSessionId=e}emitDone(e){this.emit("done",e)}emitError(e){if(this.listenerCount("error")===0){u.warn("cursor-adapter",`Prompt handle error (no listeners): ${e.message}`);return}this.emit("error",e)}async cancel(){}}class M extends B{type="cursor";static STDERR_MAX_CHARS=8192;config;callbacks;alive=!1;stopped=!1;permissionHandler=null;internalApi=null;bindingStore=null;identity;inboundQueue=[];pendingBySession=new Map;sessions=new Set;sessionRuntime=new Map;sessionSeq=0;lastUsageBySession=new Map;lastContextBySession=new Map;pendingHandoffBySession=new Map;pendingQuestionByRequestId=new Map;pendingQuestionAnswerBySession=new Map;cachedAccountRateLimits=null;rateLimitsInflight=null;_availableModels=[];activeBySession=new Map;constructor(e,t){super(),this.config=e,this.callbacks=t;const n=e.options??{};this.bindingStore=n.bindingStore instanceof se?n.bindingStore:null,this.identity=new V("cursor-adapter",t.getAgentProfile)}async start(){this.alive=!0,this.stopped=!1,(this.config.options??{}).mcp_tools!==!1&&(this.internalApi=new ee,this.internalApi.setInvokeHandler(async(t,n,s,i)=>this.callbacks.agentInvoke(t,n,i)),await this.internalApi.start()),this.notifyBindingReady(),this.refreshModels().then(()=>this.notifyBindingReady()).catch(()=>{}),this.refreshAccountRateLimits().then(()=>this.notifyBindingReady()).catch(()=>{})}get availableModels(){return this._availableModels}get availableModes(){return F.map(e=>({...e}))}async refreshModels(){try{const e={...process.env,...this.config.env??{}},t=await ne({command:this.config.command,env:e,cwd:process.cwd()});t.length>0&&(this._availableModels=t,u.info("cursor-adapter",`Loaded ${t.length} available models`))}catch(e){u.warn("cursor-adapter",`Failed to refresh models: ${e instanceof Error?e.message:String(e)}`)}}async stop(){this.stopped=!0,this.alive=!1;for(const e of this.activeBySession.values())w(e.child,"SIGTERM");this.activeBySession.clear(),this.internalApi&&(await this.internalApi.stop(),this.internalApi=null)}isAlive(){return this.alive}async createSession(e){const t=`cursor-${Date.now()}-${++this.sessionSeq}`;return this.sessions.add(t),this.sessionRuntime.set(t,{cwd:typeof e.cwd=="string"?e.cwd:void 0,modelId:typeof e.modelId=="string"?e.modelId:void 0,modeId:typeof e.modeId=="string"?e.modeId:void 0}),t}async resumeSession(e,t){this.sessions.add(e);const n=this.sessionRuntime.get(e)??{};this.sessionRuntime.set(e,{...n,cwd:typeof t?.cwd=="string"?t.cwd:n.cwd,modelId:typeof t?.modelId=="string"?t.modelId:n.modelId,modeId:typeof t?.modeId=="string"?t.modeId:n.modeId})}onAgentProfileChanged(){this.identity.onProfileChanged()}async destroySession(e){this.sessions.delete(e),this.sessionRuntime.delete(e),this.identity.forgetSession(e),this.pendingQuestionAnswerBySession.delete(e),this.pendingHandoffBySession.delete(e);for(const[t,n]of this.pendingQuestionByRequestId)n.sessionId===e&&this.pendingQuestionByRequestId.delete(t)}sendPrompt(e){const t=new H(e.adapterSessionId);if(!this.alive||this.stopped)return queueMicrotask(()=>t.emitError(new Error("adapter not running"))),t;const n=this.inboundQueue.shift()??{event_id:`cursor-evt-${Date.now()}`,session_id:e.adapterSessionId,content:e.text},s=e.adapterSessionId,i=this.pendingBySession.get(s)??[];return i.push({event:n,request:e,handle:t}),this.pendingBySession.set(s,i),this.tryStartNext(s),t}tryStartNext(e){if(this.activeBySession.has(e)||!this.alive||this.stopped)return;const t=this.pendingBySession.get(e);if(!t||t.length===0)return;const n=t.shift();t.length===0?this.pendingBySession.delete(e):this.pendingBySession.set(e,t),n&&this.startJob(n,!1,0)}startJob(e,t,n){const{event:s,request:i,handle:r}=e,a=i.adapterSessionId,o=this.sessionRuntime.get(i.adapterSessionId)??{},d=this.config.options??{},c=[...this.config.args??[]];c.push("-p","--output-format","stream-json"),d.trust!==!1&&c.push("--trust");const h=o.cwd||d.workspace;this.internalApi&&h&&(this.ensureWorkspaceMcpAndSkills(h),c.push("--approve-mcps")),h&&c.push("--workspace",h);const S=o.modelId||d.model;S&&c.push("--model",S);const v=C(o.modeId||d.mode)??"full_auto";v==="full_auto"?c.push("--yolo"):v==="plan"&&c.push("--plan");const _=o.cursorSessionId??this.bindingStore?.getAcpSessionId(i.adapterSessionId),A=!!(_&&d.use_continue!==!1&&!t);A&&c.push("--resume",_),c.push(this.buildPromptText(i));const m={...process.env,...this.config.env??{}},y=R(this.config.command,typeof m.PATH=="string"?m.PATH:void 0);u.info("cursor-adapter",`job start: event=${s.event_id} session=${s.session_id} retry=${n}`);let p;try{p=T(y,c,{cwd:h||process.cwd(),env:m,stdio:["ignore","pipe","pipe"]}).process}catch(g){this.finishActive(a,"failed",`spawn failed: ${g instanceof Error?g.message:String(g)}`);return}this.callbacks.sendEventAck(s.event_id,s.session_id),this.emit("eventStarted",s.event_id,s.session_id);const f={event:s,request:i,handle:r,child:p,seq:0,done:!1,stderr:"",timer:null,idleTimer:null,toolCallsInFlight:new Set,toolCallsStarted:0,assistantTextEmitted:"",streamBuffer:"",retryCount:n,usedContinue:A,workspace:h||process.cwd(),args:c};this.activeBySession.set(a,f),this.notifyBindingReady(a),this.armActiveIdleTimer(a);const k=i.timeoutMs&&i.timeoutMs>0?i.timeoutMs:0;k>0&&(f.timer=setTimeout(()=>{f.done||(w(p,"SIGTERM"),this.finishActive(a,"failed",`cursor agent timeout after ${k}ms`))},k)),x({input:p.stdout}).on("line",g=>this.handleStdoutLineForActive(f,g)),p.stderr?.on("data",g=>{const I=f.stderr+String(g??"");f.stderr=I.length>M.STDERR_MAX_CHARS?I.slice(-M.STDERR_MAX_CHARS):I}),p.once("error",g=>{this.finishActive(a,"failed",`spawn failed: ${String(g?.message??g)}`)}),p.once("close",g=>{if(!f.done)if((g??0)===0)this.finishActive(a,"responded");else{if(this.shouldRetryWithoutContinue(f)){f.timer&&clearTimeout(f.timer),f.idleTimer&&clearTimeout(f.idleTimer),this.activeBySession.delete(a),this.startJob(e,!0,f.retryCount+1);return}const I=f.stderr.trim()||`cursor agent exited with code ${g??-1}`;this.finishActive(a,"failed",I)}})}async cancel(e){const t=this.activeBySession.get(e);t&&(w(t.child,"SIGTERM"),this.finishActive(e,"canceled","canceled"))}deliverInboundEvent(e){const t=String(e.session_id??"").trim();if(!t){u.warn("cursor-adapter",`Dropping event ${e.event_id}: missing session_id`),this.callbacks.sendEventResult(e.event_id,"failed","missing session_id");return}if(!this.alive||this.stopped){u.warn("cursor-adapter",`Dropping event ${e.event_id}: adapter not running`),this.callbacks.sendEventAck(e.event_id,t),this.callbacks.sendEventResult(e.event_id,"failed","adapter not running");return}const n={adapterSessionId:t,text:be(String(e.content??""),e),contextMessages:ve(e)},s=new H(t),i=this.pendingBySession.get(t)??[];i.push({event:e,request:n,handle:s}),this.pendingBySession.set(t,i),u.info("cursor-adapter",`inbound queued: event=${e.event_id} session=${t} depth=${i.length}`),this.tryStartNext(t)}cancelSessionWork(e,t){const n=this.pendingBySession.get(e)??[];this.pendingBySession.delete(e);const s=this.activeBySession.get(e);s&&(this.killChildWithFallback(s.child),this.finishActive(e,"canceled",t));for(const i of n)this.callbacks.sendEventAck(i.event.event_id,i.event.session_id),this.callbacks.sendEventResult(i.event.event_id,"canceled",t),i.handle.emitDone({status:"canceled",error:t})}killChildWithFallback(e){w(e,"SIGTERM"),setTimeout(()=>{if(e.exitCode===null&&e.signalCode===null)try{w(e,"SIGKILL")}catch{}},5e3).unref()}deliverStopEvent(e,t){if(t){const n=this.activeBySession.get(t);if(n?.event.event_id===e){this.killChildWithFallback(n.child),this.finishActive(t,"canceled","stopped");return}const s=this.pendingBySession.get(t)??[],i=s.findIndex(r=>r.event.event_id===e);if(i>=0){const[r]=s.splice(i,1);s.length===0?this.pendingBySession.delete(t):this.pendingBySession.set(t,s),this.callbacks.sendEventAck(r.event.event_id,r.event.session_id),this.callbacks.sendEventResult(r.event.event_id,"canceled","stopped"),r.handle.emitDone({status:"canceled",error:"stopped"})}return}for(const[n,s]of this.activeBySession.entries())if(s.event.event_id===e){this.killChildWithFallback(s.child),this.finishActive(n,"canceled","stopped");return}for(const[n,s]of this.pendingBySession.entries()){const i=s.findIndex(a=>a.event.event_id===e);if(i<0)continue;const[r]=s.splice(i,1);s.length===0?this.pendingBySession.delete(n):this.pendingBySession.set(n,s),this.callbacks.sendEventAck(r.event.event_id,r.event.session_id),this.callbacks.sendEventResult(r.event.event_id,"canceled","stopped"),r.handle.emitDone({status:"canceled",error:"stopped"});return}}async handleLocalAction(e){const t=String(e.action_type??"").trim().toLowerCase(),n=e.params??{},s=String(n.session_id??"").trim(),i=e.action_id;if(t==="claude_interaction_reply")return String(n.kind??"").trim()==="permission"?(this.callbacks.sendLocalActionResult(i,"failed",void 0,"unsupported","Cursor adapter does not support interactive permission approvals"),{handled:!0,kind:"permission_unsupported"}):this.handleQuestionReply(e);switch(t){case"set_model":{const r=String(n.model_id??"").trim();if(!r||!s)return this.callbacks.sendLocalActionResult(i,"failed",void 0,"invalid_params","model_id and session_id are required"),{handled:!0,kind:"set_model"};const a=this.sessionRuntime.get(s)??{};this.sessionRuntime.set(s,{...a,modelId:r}),this.notifyBindingReady(s);const o=this.getToolbarMeta(s);return this.callbacks.sendLocalActionResult(i,"ok",{outcome:"model_set",session_context:{model_id:r,mode_id:o.mode_id,modelId:r,modeId:o.mode_id},model_id:r,mode_id:o.mode_id,available_models:this.availableModels,available_modes:this.availableModes}),{handled:!0,kind:"set_model"}}case"set_mode":{const r=C(n.mode_id);if(!r||!s)return this.callbacks.sendLocalActionResult(i,"failed",void 0,"invalid_params","mode_id must be approval, full_auto, or plan and session_id is required"),{handled:!0,kind:"set_mode"};const a=this.sessionRuntime.get(s)??{};this.sessionRuntime.set(s,{...a,modeId:r}),this.notifyBindingReady(s);const o=this.getToolbarMeta(s);return this.callbacks.sendLocalActionResult(i,"ok",{outcome:"mode_set",session_context:{model_id:o.model_id,mode_id:r,modelId:o.model_id,modeId:r},model_id:o.model_id,mode_id:r,available_models:this.availableModels,available_modes:this.availableModes}),{handled:!0,kind:"set_mode"}}case"get_context":{const r=s?this.sessionRuntime.get(s):void 0,a=this.config.options??{},o=this.getToolbarMeta(s);return this.callbacks.sendLocalActionResult(i,"ok",{session_context:{model_id:o.model_id,mode_id:o.mode_id,cwd:r?.cwd??a.workspace??null},model_id:o.model_id,mode_id:o.mode_id,modelId:o.model_id,modeId:o.mode_id,cwd:r?.cwd??a.workspace??null,available_models:this.availableModels,available_modes:this.availableModes}),{handled:!0,kind:"get_context"}}case"get_rate_limits":{await this.refreshAccountRateLimits({force:!0});const r=this.getRateLimitsSnapshot(s||void 0);return this.callbacks.sendLocalActionResult(i,"ok",r),s&&this.notifyBindingReady(s),{handled:!0,kind:"get_rate_limits"}}case"get_session_usage":{const r=s?this.getUsageSnapshot(s):null;return r?this.callbacks.sendLocalActionResult(i,"ok",{adapterType:"cursor",available:!0,sampledAt:r.sampledAt,turns:r.turns,tokenUsage:r.total}):this.callbacks.sendLocalActionResult(i,"ok",{adapterType:"cursor",available:!1,sampledAt:null,turns:0,tokenUsage:null}),{handled:!0,kind:"get_session_usage"}}case"session_control":{const r=String(n.verb??"").trim().toLowerCase();if(r==="restart"&&s)this.cancelSessionWork(s,"restarted"),this.callbacks.sendLocalActionResult(i,"ok",{outcome:"restarted"});else if(r==="status"){const a=this.sessionRuntime.get(s),o=this.config.options??{},d=this.getToolbarMeta(s),c=this.activeBySession.has(s);this.callbacks.sendLocalActionResult(i,"ok",{verb:"status",status:c?"running":"idle",session_context:{model_id:d.model_id,mode_id:d.mode_id,cwd:a?.cwd??o.workspace??null},model_id:d.model_id,mode_id:d.mode_id,modelId:d.model_id,modeId:d.mode_id,cwd:a?.cwd??o.workspace??null,available_models:this.availableModels,available_modes:this.availableModes})}else this.callbacks.sendLocalActionResult(i,"failed",void 0,"invalid_verb",`Unsupported verb: ${r}`);return{handled:!0,kind:"session_control"}}default:return{handled:!1,kind:"unsupported"}}}async execCommand(e,t,n){return e!=="compact"?{status:"unsupported",message:`Unsupported command: ${e}`}:this.runCompactHandoff(n)}async runCompactHandoff(e){if(!e)return{status:"failed",message:"session_id is required"};if(this.activeBySession.has(e))return{status:"failed",message:"Cannot compact while a Cursor turn is active"};const t=this.sessionRuntime.get(e)??{},n=this.config.options??{},s=t.cursorSessionId??this.bindingStore?.getAcpSessionId(e)??null;if(!s)return{status:"failed",message:"No Cursor chat to compact (send a message first)"};const i=String(t.cwd||n.workspace||process.cwd()),r=String(t.modelId||n.model||""),a=C(t.modeId||n.mode)??"full_auto";u.info("cursor-adapter",`[compact] summarize resume=${s} session=${e}`);const o=await this.runPrintOnce({prompt:ue,resumeChatId:s,cwd:i,modelId:r||void 0,modeId:a,timeoutMs:D});if(!o.ok||!o.text.trim())return{status:"failed",message:o.error??"Summarize failed (empty result)"};const d=await this.createEmptyChat();return d?(this.sessionRuntime.set(e,{...t,cwd:i,modelId:r||t.modelId,modeId:a,cursorSessionId:d}),this.bindingStore?.setAcpSessionId(e,d),this.pendingHandoffBySession.set(e,o.text.trim()),this.lastContextBySession.set(e,re(0,ae(r))),this.notifyBindingReady(e),u.info("cursor-adapter",`[compact] handoff ready: old=${s} new=${d} summaryChars=${o.text.trim().length}`),{status:"ok",message:"Context compacted via summarize + new chat",data:{previousChatId:s,chatId:d}}):{status:"failed",message:"agent create-chat failed"}}runPrintOnce(e){const t={...process.env,...this.config.env??{}},n=R(this.config.command,typeof t.PATH=="string"?t.PATH:void 0),s=[...this.config.args??[]];s.push("-p","--output-format","stream-json"),this.config.options?.trust!==!1&&s.push("--trust"),e.cwd&&s.push("--workspace",e.cwd),e.modelId&&s.push("--model",e.modelId);const i=C(e.modeId)??"full_auto";return i==="full_auto"?s.push("--yolo"):i==="plan"&&s.push("--plan"),e.resumeChatId&&s.push("--resume",e.resumeChatId),s.push(e.prompt),new Promise(r=>{let a;try{a=T(n,s,{cwd:e.cwd||process.cwd(),env:t,stdio:["ignore","pipe","pipe"]}).process}catch(m){r({ok:!1,text:"",error:`spawn failed: ${m instanceof Error?m.message:String(m)}`});return}let o="",d="",c="",h=!1;const S=e.timeoutMs&&e.timeoutMs>0?e.timeoutMs:D,v=setTimeout(()=>{h||(h=!0,this.killChildWithFallback(a),r({ok:!1,text:o,sessionId:d||void 0,error:`compact timeout after ${S}ms`}))},S);v.unref?.();const _=(m,y)=>{h||(h=!0,clearTimeout(v),r({ok:m,text:o,sessionId:d||void 0,error:y}))};x({input:a.stdout}).on("line",m=>{const y=m.trim();if(!y)return;let p;try{p=JSON.parse(y)}catch{return}if(p?.type!=="result")return;const f=typeof p?.result=="string"?p.result:"";f&&(o=f);const k=String(p?.session_id??"").trim();k&&(d=k)}),a.stderr?.on("data",m=>{c+=typeof m=="string"?m:m.toString("utf8")}),a.on("error",m=>_(!1,m.message)),a.on("close",m=>{if(!h){if(o.trim()){_(!0);return}_(!1,c.trim()||`cursor agent exited with code ${m??"unknown"}`)}})})}createEmptyChat(){const e={...process.env,...this.config.env??{}},t=R(this.config.command,typeof e.PATH=="string"?e.PATH:void 0);return new Promise(n=>{let s;try{s=T(t,["create-chat"],{cwd:process.cwd(),env:e,stdio:["ignore","pipe","pipe"]}).process}catch{n(null);return}let i="",r=!1;const a=setTimeout(()=>{r||(r=!0,this.killChildWithFallback(s),n(null))},3e4);a.unref?.(),s.stdout?.on("data",o=>{i+=typeof o=="string"?o:o.toString("utf8")}),s.on("error",()=>{r||(r=!0,clearTimeout(a),n(null))}),s.on("close",o=>{if(r)return;r=!0,clearTimeout(a);const d=i.trim().split(/\s+/)[0]??"";n(o===0&&/^[0-9a-f-]{16,}$/i.test(d)?d:null)})})}setPermissionHandler(e){this.permissionHandler=e}async ping(e){if(!this.alive||this.stopped||(this.config.options??{}).mcp_tools!==!1&&this.internalApi===null)return!1;for(const n of this.activeBySession.values()){const s=n.child?.pid;if(s&&!Se(s))return!1}return!0}getStatus(){let e=0;for(const t of this.pendingBySession.values())e+=t.length;return{alive:this.alive,busy:this.activeBySession.size>0,sessions:this.sessions.size,details:{queueDepth:e+this.inboundQueue.length,activeSessions:this.activeBySession.size}}}getActiveEventIds(){const e=[];for(const t of this.activeBySession.values())t.event.event_id&&e.push(t.event.event_id);return e}clearActiveEventForShutdown(){this.activeBySession.clear()}getMcpConfig(){return null}async probe(e){const t=this.getStatus();return{...await Z(this.config.command||"cursor-agent",{alive:t.alive,busy:t.busy,started:this.alive},e),session:this.probeSessionRecord()}}probeSessionRecord(){const e=this.firstKnownCursorSessionId(),t=e?_e(e):null;if(!t)return{recordPath:null,lastActivityMs:null,freshMs:null};try{const n=G(t);return{recordPath:t,lastActivityMs:n.mtimeMs,freshMs:Date.now()-n.mtimeMs}}catch{return{recordPath:t,lastActivityMs:null,freshMs:null}}}firstKnownCursorSessionId(){for(const e of this.activeBySession.keys()){const t=this.sessionRuntime.get(e);if(t?.cursorSessionId)return t.cursorSessionId}for(const e of this.sessionRuntime.values())if(e.cursorSessionId)return e.cursorSessionId;return null}getUsageSnapshot(e){return this.lastUsageBySession.get(e)??null}getRateLimitsSnapshot(e){const t=this.cachedAccountRateLimits,n=e?this.lastContextBySession.get(e)??null:null;if(!t)return{adapterType:"cursor",available:!1,cached:!1,sampledAt:null,rateLimits:null,contextWindow:n,tokenUsage:null};const s=Date.parse(t.sampledAt);return{adapterType:"cursor",available:t.available&&!!t.rateLimits,cached:!0,sampledAt:Number.isFinite(s)?s:Date.now(),rateLimits:t.rateLimits,contextWindow:n,tokenUsage:null,...t.displayMessage?{displayMessage:t.displayMessage}:{},...t.error?{error:t.error}:{}}}async refreshAccountRateLimits(e){const t=e?.force===!0,n=this.cachedAccountRateLimits,s=n?Date.now()-Date.parse(n.sampledAt):Number.POSITIVE_INFINITY;if(!t&&n&&Number.isFinite(s)&&s<6e4||this.rateLimitsInflight&&(await this.rateLimitsInflight,!t))return;const i=(async()=>{const r=await ie();this.cachedAccountRateLimits={available:r.available,sampledAt:r.sampledAt,rateLimits:r.rateLimits,displayMessage:r.displayMessage,error:r.error},r.available&&r.rateLimits?u.info("cursor-adapter",`[rate-limits] monthly=${r.rateLimits.fiveHour.usedPercentage.toFixed(1)}% api=${r.rateLimits.sevenDay.usedPercentage.toFixed(1)}% resetsAt=${r.rateLimits.fiveHour.resetsAt}`+(r.displayMessage?` msg=${r.displayMessage}`:"")):u.warn("cursor-adapter",`[rate-limits] unavailable: ${r.error??"unknown"}`)})();this.rateLimitsInflight=i.finally(()=>{this.rateLimitsInflight=null}),await this.rateLimitsInflight}getToolbarMeta(e){const t=this.config.options??{},n=e||String(t.aibotSessionId??""),s=n?this.sessionRuntime.get(n):void 0,i=String(s?.modelId||t.model||"auto"),r=C(s?.modeId||t.mode)??"full_auto",a={model_id:i,mode_id:r,currentModelId:i,currentModeId:r,available_models:this.availableModels,available_modes:this.availableModes},o=this.cachedAccountRateLimits?.rateLimits;if(o?.fiveHour||o?.sevenDay){const c=Date.parse(this.cachedAccountRateLimits?.sampledAt??"")||Date.now();a.rate_limits={...o.fiveHour?{fiveHour:o.fiveHour}:{},...o.sevenDay?{sevenDay:o.sevenDay}:{},sampledAt:c}}const d=n?this.lastContextBySession.get(n):void 0;return d&&(a.context_window={usedPercentage:d.usedPercentage,remainingPercentage:d.remainingPercentage,usedTokens:d.usedTokens,sizeTokens:d.sizeTokens}),a}notifyBindingReady(e){if(!this.callbacks.sendUpdateBindingCard)return;const t=this.config.options??{},n=e||String(t.aibotSessionId??"");if(!n)return;const s=this.sessionRuntime.get(n),i=String(s?.cwd||t.workspace||"");i&&this.callbacks.sendUpdateBindingCard(n,this.activeBySession.has(n)?"busy":"ready",i,this.getToolbarMeta(n))}buildPromptText(e){let t;!e.contextMessages||e.contextMessages.length===0?t=e.text:t=`${e.contextMessages.map(r=>`[${r.senderId}] ${r.content}`).join(`
|
|
3
7
|
`)}
|
|
4
8
|
|
|
5
9
|
[Current user message]
|
|
6
|
-
${e.text}
|
|
7
|
-
|
|
8
|
-
|
|
10
|
+
${e.text}`;const n=this.pendingQuestionAnswerBySession.get(e.adapterSessionId);n&&(this.pendingQuestionAnswerBySession.delete(e.adapterSessionId),t=`[User answered a previous agent question]
|
|
11
|
+
${n}
|
|
12
|
+
|
|
13
|
+
[Current user message]
|
|
14
|
+
${t}`);const s=this.pendingHandoffBySession.get(e.adapterSessionId);return s&&(this.pendingHandoffBySession.delete(e.adapterSessionId),t=`[Previous conversation summary \u2014 continue from this handoff]
|
|
15
|
+
${s}
|
|
16
|
+
|
|
17
|
+
[Current user message]
|
|
18
|
+
${t}`),this.identity.injectOnce(e.adapterSessionId,t)}handleStdoutLineForActive(e,t){if(e.done)return;const n=t.trim();if(!n)return;const{event:s}=e;let i;try{i=JSON.parse(n)}catch{this.armActiveIdleTimer(e.request.adapterSessionId);return}if(i?.type==="tool_call"){const o=String(i?.call_id??""),d=String(i?.subtype??"");o&&(d==="started"||d==="start"?(this.flushAssistantStreamBuffer(e),e.toolCallsInFlight.add(o),e.toolCallsStarted+=1,this.handleSpecialToolCallStarted(e,i)):N.has(d)&&e.toolCallsInFlight.delete(o))}this.armActiveIdleTimer(e.request.adapterSessionId);const r=ge(i);r&&this.callbacks.sendRawEventEnvelope?.(s.event_id,s.session_id,r);const a=ye(i,e.assistantTextEmitted);if(a&&this.enqueueAssistantStreamDelta(e,a),i?.type==="result"){const o=typeof i?.result=="string"?i.result.trim():"";o&&(e.assistantTextEmitted?o.startsWith(e.assistantTextEmitted)&&o.length>e.assistantTextEmitted.length&&this.enqueueAssistantStreamDelta(e,o.slice(e.assistantTextEmitted.length)):this.enqueueAssistantStreamDelta(e,o)),this.flushAssistantStreamBuffer(e);const d=String(i?.session_id??"").trim();if(d){const p=this.sessionRuntime.get(e.request.adapterSessionId)??{};this.sessionRuntime.set(e.request.adapterSessionId,{...p,cursorSessionId:d}),this.bindingStore?.setAcpSessionId(e.request.adapterSessionId,d)}const c=i?.usage??{},h=this.lastUsageBySession.get(e.request.adapterSessionId);this.lastUsageBySession.set(e.request.adapterSessionId,{sampledAt:new Date().toISOString(),turns:(h?.turns??0)+1,total:{input:(h?.total.input??0)+Number(c.inputTokens??0),output:(h?.total.output??0)+Number(c.outputTokens??0),cacheRead:(h?.total.cacheRead??0)+Number(c.cacheReadTokens??0),cacheWrite:(h?.total.cacheWrite??0)+Number(c.cacheWriteTokens??0)}});const S=String((this.sessionRuntime.get(e.request.adapterSessionId)?.modelId??this.config.options?.model??"")||""),v=Math.max(1,(e.toolCallsStarted??0)+1),_=Number(c.inputTokens??0),A=Number(c.cacheReadTokens??0),m=Number(c.cacheWriteTokens??0),y=oe({inputTokens:_,cacheReadTokens:A,cacheWriteTokens:m},S,{apiRounds:v});y&&(this.lastContextBySession.set(e.request.adapterSessionId,y),u.info("cursor-adapter",`[context_window] used=${y.usedTokens} size=${y.sizeTokens} pct=${y.usedPercentage.toFixed(1)}% model=${S||"auto"} rawIn=${_} rawCache=${A} rawWrite=${m} apiRounds=${v}`))}}handleSpecialToolCallStarted(e,t){const n=O(t);if(Q(n.toolName)){const s=pe(n.toolInput);s?(e.seq+=1,this.callbacks.sendStreamChunk(e.event.event_id,e.event.session_id,s,e.seq,!1),u.info("cursor-adapter",`CreatePlan forwarded as stream text: event=${e.event.event_id} chars=${s.length}`)):(e.seq+=1,this.callbacks.sendStreamChunk(e.event.event_id,e.event.session_id,"[CreatePlan] \u65B9\u6848\u5185\u5BB9\u89E3\u6790\u5931\u8D25\uFF0C\u672A\u4E0B\u53D1\u6B63\u6587\u3002",e.seq,!1),u.warn("cursor-adapter",`CreatePlan missing plan body: event=${e.event.event_id}`));return}U(n.toolName)&&this.emitAskQuestionCard(e,n.toolInput)}emitAskQuestionCard(e,t){const n=e.event,s=fe(t);if(!s){e.seq+=1,this.callbacks.sendStreamChunk(n.event_id,n.session_id,"[AskQuestion] \u95EE\u9898\u5185\u5BB9\u89E3\u6790\u5931\u8D25\uFF0C\u672A\u4E0B\u53D1\u95EE\u9898\u5361\u3002",e.seq,!1),u.warn("cursor-adapter",`AskQuestion parse failed: event=${n.event_id}`);return}if(!this.callbacks.sendAgentQuestionCard){u.warn("cursor-adapter","AskQuestion skipped: sendAgentQuestionCard callback not wired");return}const i=z(),r=Date.now();this.pendingQuestionByRequestId.set(i,{eventId:n.event_id,sessionId:n.session_id,createdAt:r}),this.callbacks.sendAgentQuestionCard(n.event_id,n.session_id,{request_id:i,mode:"form",questions:s,expires_at:r+P}),u.info("cursor-adapter",`Sent agent_question card: request_id=${i} questions=${s.length} event=${n.event_id}`)}async handleQuestionReply(e){const t=e.params??{},n=String(t.request_id??"").trim();if(!n)return this.callbacks.sendLocalActionResult(e.action_id,"failed",void 0,"request_id_required","request_id is required"),{handled:!0,kind:"question_reply_no_id"};const s=this.pendingQuestionByRequestId.get(n),i=s?Date.now()-s.createdAt>P+3e4:!0;if(!s||i)return this.pendingQuestionByRequestId.delete(n),this.callbacks.sendLocalActionResult(e.action_id,"failed",void 0,"interaction_request_not_pending","The question is no longer pending; the reply was not delivered."),{handled:!0,kind:"question_reply_not_pending"};const r=t.resolution??{},a=String(r.type??"");if(a==="action"&&String(r.value??"")==="cancel")return this.pendingQuestionByRequestId.delete(n),this.callbacks.sendLocalActionResult(e.action_id,"ok",{request_id:n,resolution:"cancel"}),{handled:!0,kind:"question_reply_cancel"};let o="";return a==="text"?o=String(r.value??""):a==="map"&&(o=(Array.isArray(r.entries)?r.entries:[]).map(c=>c.value).join(", ")),o?(this.pendingQuestionByRequestId.delete(n),this.pendingQuestionAnswerBySession.set(s.sessionId,o),this.callbacks.sendLocalActionResult(e.action_id,"ok",{request_id:n,resolution:"answer"}),u.info("cursor-adapter",`Question reply cached for next prompt: request_id=${n} session=${s.sessionId}`),{handled:!0,kind:"question_reply"}):(this.pendingQuestionByRequestId.delete(n),this.callbacks.sendLocalActionResult(e.action_id,"ok",{request_id:n,resolution:"cancel"}),{handled:!0,kind:"question_reply_empty"})}enqueueAssistantStreamDelta(e,t){t&&(e.assistantTextEmitted+=t,e.streamBuffer+=t,e.streamBuffer.length>=ce&&this.flushAssistantStreamBuffer(e))}flushAssistantStreamBuffer(e){if(!e.streamBuffer)return;const t=e.streamBuffer;e.streamBuffer="",e.seq+=1,this.callbacks.sendStreamChunk(e.event.event_id,e.event.session_id,t,e.seq,!1,void 0,void 0)}armActiveIdleTimer(e){const t=this.activeBySession.get(e);if(!t||t.done)return;t.idleTimer&&clearTimeout(t.idleTimer);const n=t.toolCallsInFlight.size>0?de:le;t.idleTimer=setTimeout(()=>{t.done||(u.error("cursor-adapter",`Idle timeout (${n/1e3}s, toolsInFlight=${t.toolCallsInFlight.size}) \u2014 killing cursor child: event=${t.event.event_id} session=${e}`),this.killChildWithFallback(t.child),this.finishActive(e,"failed",`idle timeout after ${n/1e3}s`))},n),t.idleTimer.unref?.()}finishActive(e,t,n){const s=this.activeBySession.get(e);if(!s)return;s.done=!0,s.timer&&clearTimeout(s.timer),s.idleTimer&&clearTimeout(s.idleTimer),this.flushAssistantStreamBuffer(s),s.seq+=1;const i=String(s.event.quoted_message_id??"").trim()||void 0;this.callbacks.sendStreamChunk(s.event.event_id,s.event.session_id,"",s.seq,!0,void 0,i),u.info("cursor-adapter",`job finish: event=${s.event.event_id} session=${s.event.session_id} status=${t}${n?` msg=${n}`:""}`),this.emit("eventDone",s.event.event_id),this.callbacks.sendEventResult(s.event.event_id,t,n),t==="responded"?s.handle.emitDone({status:"completed"}):t==="canceled"?s.handle.emitDone({status:"canceled",error:n}):s.handle.emitDone({status:"failed",error:n}),this.activeBySession.delete(e),this.notifyBindingReady(e),this.tryStartNext(e)}shouldRetryWithoutContinue(e){if(!e.usedContinue||e.retryCount>0)return!1;const t=e.stderr.toLowerCase(),n=t.includes("resume")||t.includes("continue")||t.includes("chat")||t.includes("session"),s=t.includes("not found")||t.includes("no previous session")||t.includes("does not exist")||t.includes("invalid");return n&&s}ensureWorkspaceMcpAndSkills(e){if(!this.internalApi)return;this.ensureCursorMcpConfig(e,this.internalApi.url);const t=b(E(),".cursor","skills"),n=te(t);n.length>0&&u.info("cursor-adapter",`Synced connector skills to ${t}: [${n.join(", ")}]`)}ensureCursorMcpConfig(e,t){try{const n=b(e,".cursor"),s=b(n,"mcp.json");$(n,{recursive:!0});let i={};try{i=JSON.parse(q(s,"utf8"))}catch{i={}}const r={command:process.execPath,args:[X(K(import.meta.url),"../../../mcp/acp-mcp-server.js"),"--api-url",t]};(!i.mcpServers||typeof i.mcpServers!="object")&&(i.mcpServers={}),i.mcpServers.grix=r,L(s,`${JSON.stringify(i,null,2)}
|
|
19
|
+
`,"utf8"),this.recordCursorMcpRegistry(e,s,r),u.info("cursor-adapter",`MCP config synced: workspace=${e} file=${s}`)}catch(n){u.warn("cursor-adapter",`Failed to ensure .cursor/mcp.json: ${String(n)}`)}}recordCursorMcpRegistry(e,t,n){try{const s=b(Y.data,"cursor"),i=b(s,"mcp-registry.json");$(s,{recursive:!0});let r={};try{r=JSON.parse(q(i,"utf8"))}catch{r={}}r[e]={mcp_path:t,server:n,updated_at:new Date().toISOString()},L(i,`${JSON.stringify(r,null,2)}
|
|
20
|
+
`,"utf8")}catch(s){u.warn("cursor-adapter",`Failed to record MCP registry: ${String(s)}`)}}}export{F as CURSOR_MODE_OPTIONS,M as CursorAdapter,ye as extractCursorAssistantStreamDelta,_e as findCursorTranscript,pe as formatCursorCreatePlanText,U as isCursorAskQuestionTool,Q as isCursorCreatePlanTool,ge as mapCursorStreamJsonToRawEnvelope,fe as parseCursorAskQuestionPayload};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{JsonRpcTransport as y}from"../../core/transport/json-rpc.js";import{log as c}from"../../core/log/index.js";import{killProcessGroup as g,resolveCommandPath as w,spawnCommand as h}from"../../core/runtime/spawn.js";const E=3e4,u="none|low|medium|high|xhigh|extra-high|max";function M(o,e){const t=o.trim(),r=e.trim();return t==="default"||t==="default[]"||/^default\[.*\]$/.test(t)?{id:"auto",displayName:!r||r==="default"||r==="Auto"?"Auto (default)":r}:{id:t,displayName:r||t}}function v(o){const e=o.trim();if(!e||e==="auto"||e==="default"||e==="default[]")return"auto";let t=e;return t=t.replace(new RegExp(`-thinking-(?:${u})(?:-fast)?$`),""),t=t.replace(new RegExp(`-(?:${u})-thinking$`),""),t=t.replace(/-thinking(?:-fast)?$/,""),t=t.replace(new RegExp(`-(?:${u})(?:-fast)?$`),""),t=t.replace(/-fast$/,""),t||e}function T(o){let e=o.trim();e=e.replace(/\s*\(default\)\s*$/i,""),e=e.replace(/\s*\(NO ZDR\)\s*$/i,"");const t=/(?:\s+(?:None|Low|Medium|High|Extra High|Max|Fast|Thinking|1M))$/i;for(let r=0;r<8&&t.test(e);r++)e=e.replace(t,"").trim();return e||o.trim()}function $(o){const e=[];for(const t of o.split(`
|
|
2
|
+
`)){const r=t.trim();if(!r||r.toLowerCase().includes("available model")||r.toLowerCase().startsWith("tip:"))continue;const n=r.match(/^(\S+)\s+-\s+(.+)$/);n&&e.push({id:n[1],displayName:n[2].trim()})}return e}function A(o){const e=new Set,t=[];for(const r of o){const n=v(r.id);if(e.has(n))continue;e.add(n);const s=n==="auto"?"Auto (default)":T(r.displayName);t.push({id:n,displayName:s})}return t}async function I(o){return new Promise(e=>{let t="";o.stdout?.on("data",r=>{t+=String(r)}),o.once("close",()=>e(t)),o.once("error",()=>e(t))})}async function C(o){const e={...process.env,...o.env??{}},t=w(o.command,typeof e.PATH=="string"?e.PATH:void 0),{process:r}=h(t,["models"],{cwd:o.cwd??process.cwd(),env:e,stdio:["ignore","pipe","pipe"]}),n=await I(r);return $(n)}async function k(o){const e=await C(o);return A(e)}async function P(o){const e=o.timeoutMs??E,t=o.cwd??process.cwd(),r={...process.env,...o.env??{}},n=w(o.command,typeof r.PATH=="string"?r.PATH:void 0),{process:s}=h(n,["acp"],{cwd:t,env:r,stdio:["pipe","pipe","pipe"]});if(!s.stdout||!s.stdin)throw g(s,"SIGTERM"),new Error("cursor acp spawn missing stdio pipes");const a=new y(s.stdout,s.stdin);a.setHandlers(void 0,(d,m)=>{String(d).startsWith("cursor/")?a.respondSuccess(m,{}):a.respondError(m,-32601,"method not implemented")});const l=new AbortController,x=setTimeout(()=>l.abort(),e),p=d=>{l.abort(d)};s.once("error",p);try{await a.call("initialize",{protocolVersion:1,clientCapabilities:{fs:{readTextFile:!1,writeTextFile:!1}},clientInfo:{name:"grix-connector",version:"0"}},l.signal);const f=((await a.call("session/new",{cwd:t,mcpServers:[]},l.signal)).models?.availableModels??[]).filter(i=>typeof i.modelId=="string"&&i.modelId.length>0).map(i=>M(i.modelId,i.name??i.modelId));if(f.length===0)throw new Error("cursor acp session/new returned no models");return f}finally{clearTimeout(x),s.off("error",p);try{a.close()}catch{}g(s,"SIGTERM")}}async function L(o){try{const e=await C(o);if(e.length>0)return c.info("cursor-adapter",`Loaded ${e.length} models via CLI agent models`),e;c.warn("cursor-adapter","CLI agent models returned empty list, falling back to ACP")}catch(e){const t=e instanceof Error?e.message:String(e);c.warn("cursor-adapter",`CLI agent models failed, falling back to ACP: ${t}`)}try{const e=await P(o);return c.info("cursor-adapter",`Loaded ${e.length} parameterized models via acp fallback`),e}catch(e){const t=e instanceof Error?e.message:String(e);return c.warn("cursor-adapter",`ACP model fallback failed: ${t}`),[]}}export{T as collapseCursorDisplayName,v as collapseCursorModelId,A as collapseExplodedCursorModels,k as fetchCollapsedModelsFromCli,L as fetchCursorParameterizedModels,P as fetchCursorParameterizedModelsViaAcp,C as fetchExplodedModelsFromCli,M as normalizeCursorAvailableModel,$ as parseExplodedCursorModels};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{execFile as f}from"node:child_process";import{homedir as p,platform as d}from"node:os";import{join as y}from"node:path";import{promisify as b}from"node:util";const u=b(f),g="https://api2.cursor.sh/aiserver.v1.DashboardService/GetCurrentPeriodUsage";function m(e){return Number.isFinite(e)?Math.max(0,Math.min(100,e)):0}function h(e){if(e==null||e==="")return 0;const t=typeof e=="number"?e:Number(String(e).trim());return!Number.isFinite(t)||t<=0?0:t>1e12?Math.floor(t/1e3):Math.floor(t)}function k(e){const t=e.planUsage;if(!t||typeof t!="object")return null;const r=h(e.billingCycleEnd);if(r<=0)return null;const i=m(Number(t.totalPercentUsed??0)),a=t.apiPercentUsed,s=t.autoPercentUsed,c=m(Number(a!=null&&Number.isFinite(Number(a))?a:s??0));return{fiveHour:{usedPercentage:i,resetsAt:r},sevenDay:{usedPercentage:c,resetsAt:r}}}async function S(){if(d()!=="darwin")return null;try{const{stdout:e}=await u("security",["find-generic-password","-s","cursor-access-token","-a","cursor-user","-w"],{timeout:5e3,encoding:"utf8"});return String(e??"").trim()||null}catch{return null}}async function M(){const e=y(p(),"Library","Application Support","Cursor","User","globalStorage","state.vscdb");try{const{stdout:t}=await u("sqlite3",[e,"SELECT value FROM ItemTable WHERE key = 'cursorAuth/accessToken' LIMIT 1;"],{timeout:5e3,encoding:"utf8"});return String(t??"").trim()||null}catch{return null}}async function v(){const e=await S();return e||M()}async function w(e){const t=new Date().toISOString(),r=e&&Object.prototype.hasOwnProperty.call(e,"token")?e.token??null:await v();if(!r)return{available:!1,rateLimits:null,sampledAt:t,error:"no_cursor_access_token"};const i=e?.fetchImpl??fetch,a=e?.timeoutMs??12e3,s=new AbortController,c=setTimeout(()=>s.abort(),a);try{const n=await i(g,{method:"POST",headers:{Authorization:`Bearer ${r}`,"Content-Type":"application/json","Connect-Protocol-Version":"1","x-cursor-client-type":"cli"},body:"{}",signal:s.signal});if(!n.ok)return{available:!1,rateLimits:null,sampledAt:t,error:`http_${n.status}`};const o=await n.json(),l=k(o);return l?{available:!0,rateLimits:l,sampledAt:t,displayMessage:o.displayMessage}:{available:!1,rateLimits:null,sampledAt:t,displayMessage:o.displayMessage,error:"plan_usage_unavailable"}}catch(n){const o=n instanceof Error?n.message:String(n);return{available:!1,rateLimits:null,sampledAt:t,error:o.includes("abort")?"timeout":o}}finally{clearTimeout(c)}}export{w as fetchCursorAccountUsage,k as mapCursorPeriodUsageToRateLimits,v as readCursorAccessToken};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{CursorAdapter as
|
|
1
|
+
import{CursorAdapter as e,CURSOR_MODE_OPTIONS as s,extractCursorAssistantStreamDelta as t,mapCursorStreamJsonToRawEnvelope as a,isCursorAskQuestionTool as l,isCursorCreatePlanTool as C,parseCursorAskQuestionPayload as u,formatCursorCreatePlanText as d}from"./cursor-adapter.js";import{collapseCursorDisplayName as n,collapseCursorModelId as p,collapseExplodedCursorModels as m,fetchCursorParameterizedModels as x,fetchExplodedModelsFromCli as c,normalizeCursorAvailableModel as f,parseExplodedCursorModels as T}from"./fetch-models.js";import{fetchCursorAccountUsage as M,mapCursorPeriodUsageToRateLimits as P,readCursorAccessToken as E}from"./fetch-usage.js";import{buildCursorContextWindow as k,buildCursorContextWindowFromUsage as w,estimateCursorContextTokens as O,resolveCursorContextWindowSize as R}from"./context-window.js";export{s as CURSOR_MODE_OPTIONS,e as CursorAdapter,k as buildCursorContextWindow,w as buildCursorContextWindowFromUsage,n as collapseCursorDisplayName,p as collapseCursorModelId,m as collapseExplodedCursorModels,O as estimateCursorContextTokens,t as extractCursorAssistantStreamDelta,M as fetchCursorAccountUsage,x as fetchCursorParameterizedModels,c as fetchExplodedModelsFromCli,d as formatCursorCreatePlanText,l as isCursorAskQuestionTool,C as isCursorCreatePlanTool,P as mapCursorPeriodUsageToRateLimits,a as mapCursorStreamJsonToRawEnvelope,f as normalizeCursorAvailableModel,u as parseCursorAskQuestionPayload,T as parseExplodedCursorModels,E as readCursorAccessToken,R as resolveCursorContextWindowSize};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{readFileSync as
|
|
1
|
+
import{readFileSync as S,readdirSync as m}from"node:fs";import f from"node:path";import{homedir as p}from"node:os";import{log as I}from"../../core/log/index.js";import{makeTitle as h,stripInjectedPreamble as y}from"../../core/util/session-title.js";import{openReadOnlyDatabase as g}from"../../core/util/sqlite-reader.js";function T(e=p()){return f.join(e,".cursor","chats")}function F(e){try{return JSON.parse(S(e,"utf8"))}catch{return null}}function w(e){const r=[];for(let t=0;t+34<=e.length;t+=1)e[t]===10&&e[t+1]===32&&r.push(e.subarray(t+2,t+34).toString("hex"));return r}function A(e){let r;try{r=JSON.parse(e.toString("utf8"))}catch{return}if(r.role!=="user")return;const t=r.content;if(typeof t=="string")return t;if(Array.isArray(t)){for(const n of t)if(n&&typeof n=="object"&&typeof n.text=="string")return n.text}}function x(e){const r=/<user_query>([\s\S]*?)<\/user_query>/.exec(e);if(r)return h(y(r[1]));const t=/\[system-identity\][\s\S]*?\[\/system-identity\]/.exec(e),n=/\[\[message_id:[^\]]+\]\]/.exec(e);if(!t&&!n)return;const s=Math.min(t?.index??Number.POSITIVE_INFINITY,n?.index??Number.POSITIVE_INFINITY),o=y(e.slice(s)),i=o.search(/[\x00-\x08\x0b\x0c\x0e-\x1f]/),c=(i>=0?o.slice(0,i):o).replace(/^\uFFFD+/,"").trim();return h(c)}function b(e){const r=A(e);if(r){const n=x(r);if(n)return n}const t=e.toString("utf8");if(!t.trimStart().startsWith("{"))return x(t)}function D(e){const r=g(e);if(r)try{const t=r.all("SELECT value FROM meta LIMIT 1");if(t.length===0)return;const n=JSON.parse(Buffer.from(t[0].value,"hex").toString("utf8")),s=new Map;for(const o of r.all("SELECT id, data FROM blobs"))s.set(o.id,Buffer.from(o.data));if(n.latestRootBlobId){const o=s.get(n.latestRootBlobId);if(o)for(const i of w(o)){const c=s.get(i);if(!c)continue;const a=b(c);if(a)return a}}for(const o of s.values()){const i=b(o);if(i)return i}return}catch{return}finally{r.close()}}function R(e=p()){const r=T(e),t=[];let n;try{n=m(r,{withFileTypes:!0})}catch{return t}for(const s of n){if(!s.isDirectory())continue;const o=f.join(r,s.name);let i;try{i=m(o,{withFileTypes:!0})}catch{continue}for(const c of i){if(!c.isDirectory())continue;const a=f.join(o,c.name),d=f.join(a,"meta.json"),u=F(d);if(!u?.updatedAtMs)continue;const l=D(f.join(a,"store.db"));!u.hasConversation&&!l||t.push({sessionId:c.name,cwd:u.cwd??"",title:l,createdAt:u.createdAtMs,updatedAt:u.updatedAtMs,filePath:d})}}return t.sort((s,o)=>o.updatedAt-s.updatedAt),I.info("cursor-session-scanner",`Scanned ${t.length} Cursor sessions from ${r}`),t}export{T as resolveCursorChatsDir,R as scanCursorSessions};
|