grix-connector 3.7.0 → 3.8.0
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/acp/acp-adapter.js +10 -10
- package/dist/adapter/codewhale/codewhale-adapter.js +1 -1
- package/dist/core/admin/admin-server.js +1 -1
- package/dist/core/installer/manual-guide.js +1 -1
- package/dist/core/installer/registry.js +1 -1
- package/dist/core/proxy/hermes-profile-relay.js +1 -0
- package/dist/core/proxy/hermes-profile-relay.py +492 -0
- package/dist/core/proxy/relay-credential-router.js +1 -0
- package/dist/grix.js +6 -6
- package/dist/mcp/stream-http/security.js +1 -1
- package/dist/protocol/acp-client.js +1 -1
- package/openclaw-plugin/index.js +217 -74
- package/package.json +2 -2
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
import{fileURLToPath as w}from"node:url";import f from"node:path";import{homedir as I}from"node:os";import{promises as v}from"node:fs";import{EventEmitter as b}from"node:events";import{spawn as y}from"node:child_process";import{AgentProcess as k}from"../../agent/process.js";import{hasChildProcesses as _}from"../../core/runtime/spawn.js";import{applyNativeProviderConfig as $}from"../shared/native-provider-config.js";import{syncDefaultSkillsToDir as P}from"../../default-skills/index.js";import{AcpClient as E,AcpAuthRequiredError as M,isAuthRequiredError as x}from"../../protocol/acp-client.js";import{AgentEventType as m}from"../../types/events.js";import{InternalApiServer as q}from"../../core/mcp/internal-api-server.js";import{EventResultsStore as N}from"../../core/persistence/event-results-store.js";import{QuotedMessageStream as D}from"../../core/util/quoted-message-stream.js";import{SafeMarkdownStreamSegmenter as j}from"../../core/text-segmentation/index.js";import{extractAcpTurnInput as U}from"../../core/protocol/payload-parser.js";import{injectMessageMetadata as B}from"../../core/protocol/message-metadata.js";import{log as r}from"../../core/log/index.js";import{scanSkills as A}from"../claude/skill-scanner.js";import{checkKiroSessionActivity as L}from"./session-activity.js";import{resolveCliPath as O,getCliVersion as F}from"../../core/util/cli-probe.js";const S=f.dirname(w(import.meta.url)),K=[{modelId:"deepseek-v4-pro",name:"DeepSeek V4 Pro"},{modelId:"deepseek-v4-flash",name:"DeepSeek V4 Flash"}],H=200,W=60*1e3,z=600*1e3,R=80,T=1200*1e3,V=["[grix protocol] Markers like [[message_id:<id>]] and [[quoted_message_id:<id>]] are metadata, not user prose \u2014 never repeat them verbatim except as instructed here.","The message you are replying to carries its id in a [[message_id:<id>]] marker.","If (and only if) you want your reply to quote/thread onto that specific message, include [[quoted_message_id:<id>]] anywhere in your reply using that id; otherwise omit it."].join(" ");function G(p){return!(!(p instanceof Error)||p.code!==-32603||x(p))}function g(p){if(!(p instanceof Error))return String(p);let e=p.message;const i=p.data;return i&&(e+=` data=${JSON.stringify(i)}`),e}function Q(p){return p.replace(/\u001b\[[0-9;?]*[ -/]*[@-~]/g,"")}class Ie extends b{type="acp";config;callbacks;agentProcess=null;acpClient=null;internalApi=null;activeRun=null;compacting=!1;compactingTimer=null;compactionDoneResolver=null;pendingAutoCompact=!1;eventResults=null;pendingApprovals=new Map;clientMsgSeq=0;stopped=!1;acpAuthMethod;acpInitialMode;acpInitialModel;acpMcpTools;rawTransport;approvalMode;autoInjectArgs;nativeProviderScope;bindingStore;sessionBindings=new Map;deferredEvents=new Map;currentAibotSessionId;bridgeLog;sessionConnected=!1;rawEventSeq=0;recoveryContextBySessionId=new Map;quoteProtocolTaughtSessions=new Set;identityInjectedSessions=new Set;constructor(e,i,t){if(super(),this.config=e,this.callbacks=i,this.bridgeLog=t?.bridgeLog??null,this.acpAuthMethod=t?.acpAuthMethod,this.acpInitialMode=t?.acpInitialMode,this.acpInitialModel=t?.acpInitialModel,this.acpMcpTools=t?.acpMcpTools??!0,this.rawTransport=t?.rawTransport??!1,this.approvalMode=t?.approvalMode??"default",this.autoInjectArgs=t?.autoInjectArgs,this.nativeProviderScope=t?.nativeProviderScope,this.bindingStore=t?.bindingStore??null,this.currentAibotSessionId=t?.aibotSessionId?String(t.aibotSessionId).trim():void 0,t?.eventResultsPath&&(this.eventResults=new N(t.eventResultsPath)),this.bindingStore&&this.currentAibotSessionId){const s=this.bindingStore.get(this.currentAibotSessionId);s?.cwd&&this.sessionBindings.set(this.currentAibotSessionId,s.cwd)}}async start(){if(await this.spawnProcess(),!this.bindingStore){await this.connectSession(this.resolveCwd());return}const e=this.currentAibotSessionId?this.sessionBindings.get(this.currentAibotSessionId):void 0;e&&await this.connectSession(e)}async stop(){this.stopped=!0,this.rejectDeferredEvents("adapter stopped"),this.cancelAllDeferredTimers(),this.pendingAutoCompact=!1,this.compacting=!1,this.compactingTimer&&(clearTimeout(this.compactingTimer),this.compactingTimer=null),this.compactionDoneResolver&&(this.compactionDoneResolver(),this.compactionDoneResolver=null),this.activeRun&&(this.activeRun.flushTimer&&(clearTimeout(this.activeRun.flushTimer),this.activeRun.flushTimer=null),this.activeRun.idleTimer&&(clearTimeout(this.activeRun.idleTimer),this.activeRun.idleTimer=null),this.activeRun=null),this.acpClient&&(this.acpClient.clearSettleTimer(),this.acpClient.removeAllListeners(),this.acpClient=null),this.agentProcess&&(await this.agentProcess.close(),this.agentProcess=null),this.internalApi&&(await this.internalApi.stop(),this.internalApi=null)}isAlive(){return this.agentProcess?.alive??!1}async createSession(e){return this.acpClient?.sessionId??""}async resumeSession(e,i){}async destroySession(e){this.quoteProtocolTaughtSessions.delete(e),this.identityInjectedSessions.delete(e)}onAgentProfileChanged(){this.identityInjectedSessions.size>0&&(r.info("acp-adapter",`agent profile changed; clearing ${this.identityInjectedSessions.size} identity-injected session(s)`),this.identityInjectedSessions.clear())}sendPrompt(e){const i=new X(e.adapterSessionId);return this.acpClient?.isAlive&&this.acpClient.send(e.text).catch(t=>{i.emitError(t instanceof Error?t:new Error(String(t)))}),i}async cancel(e){this.activeRun&&this.acpClient&&(await this.acpClient.cancel(),this.flushStream(),this.finishRun("canceled","stopped by user"))}setPermissionHandler(e){}async ping(e){return this.acpClient?.isAlive?this.acpClient.ping(e):this.agentProcess?.alive??!1}getStatus(){return{alive:this.agentProcess?.alive??!1,busy:this.activeRun!==null||this.compacting,sessions:this.sessionConnected?1:0}}getActiveEventIds(){return this.activeRun?[this.activeRun.eventId]:[]}clearActiveEventForShutdown(){this.activeRun&&(this.activeRun.flushTimer&&(clearTimeout(this.activeRun.flushTimer),this.activeRun.flushTimer=null),this.activeRun.idleTimer&&(clearTimeout(this.activeRun.idleTimer),this.activeRun.idleTimer=null),this.activeRun=null)}getMcpConfig(){if(!this.internalApi)return null;this.internalApi.setMcpBridgeUpHandler(i=>this.onMcpBridgeUp(i));const e=f.resolve(S,"../../mcp/mcp-bridge-server.js");return{name:"grix-app-bridge",command:process.execPath,args:[e,"--ws-url",this.internalApi.mcpBridgeWsUrl]}}async hasBackgroundWork(){const e=this.agentProcess?.pid;return e?_(e,[e]):!1}async probe(e){const i=this.config.command||"gemini",t=await O(i),s=t!==null;let n=null,a;if(s){const d=await F(i);n=d.version,d.error&&(a=d.error)}else a={code:"cli_not_found",message:`command not found: ${i}`};const o=this.acpClient?.model??null,c=this.getStatus();let l;return e?.conversation?this.acpClient?.isAlive?l={attempted:!0,ok:!0,latency_ms:null}:l={attempted:!0,ok:!1,latency_ms:null,error:{code:"unsupported",message:"ACP session not connected, cannot probe conversation"}}:l={attempted:!1,ok:!1,latency_ms:null},{cli:{command:i,installed:s,path:t,version:n,...a?{error:a}:{}},conversation:l,config:{model:o,base_url:null,source:{model:o?"runtime":"unknown",base_url:"unknown"}},process:{started:!!this.agentProcess,alive:c.alive,busy:c.busy}}}getSupportedCommands(){return this.acpClient?.availableCommands?.length?this.acpClient.availableCommands.map(e=>({name:e.name,description:e.description??"",args:e.args})):[{name:"model",description:"List or set model",args:"[model_id]"},{name:"mode",description:"List or set collaboration mode",args:"[mode_id]"},{name:"interrupt",description:"Interrupt current run"},{name:"compact",description:"\u538B\u7F29\u4E0A\u4E0B\u6587"},{name:"status",description:"Show session status"},{name:"skills",description:"List available skills"}]}async execCommand(e,i,t){return this.acpClient?.supportsCommandsExecute?this.execCommandViaAcp(e,i):this.execCommandFallback(e,i)}async execCommandViaAcp(e,i){if(!this.acpClient)return{status:"failed",message:"No active ACP session"};if(e==="interrupt")return this.activeRun?(await this.cancel(this.activeRun.sessionId),{status:"ok",message:"Run interrupted"}):{status:"failed",message:"No active run to interrupt"};if(e==="compact"){if(this.compacting)return{status:"failed",message:"Compaction already in progress"};if(this.activeRun)return{status:"failed",message:"agent busy"};this.compacting=!0;try{const t=await this.acpClient.executeCommand(e,i||void 0);return this.finishCompaction("acp-commands-execute"),{status:t.status==="failed"?"failed":"ok",message:t.message??"Compacted",data:t.data}}catch(t){return this.finishCompaction("error"),t?.code===-32601?this.execCommandFallback(e,i):{status:"failed",message:`compact failed: ${t instanceof Error?t.message:t}`}}}try{const t=await this.acpClient.executeCommand(e,i||void 0);return{status:t.status??"ok",message:t.message,data:t.options?{command:e,options:t.options}:t.data}}catch(t){return t?.code===-32601?this.execCommandFallback(e,i):{status:"failed",message:`Command failed: ${t instanceof Error?t.message:t}`}}}async execCommandFallback(e,i){try{switch(e){case"model":{const t=i.trim();if(t)return await this.setModel(t)?{status:"ok",message:`Model set to ${t}`}:{status:"failed",message:`Failed to set model: ${t}`};const s=this.buildToolbarContext("model_list");return{status:"ok",message:`Current: ${s.currentModelId||"unknown"}`,data:s}}case"mode":{const t=i.trim();if(t)return await this.setMode(t)?{status:"ok",message:`Mode set to ${t}`}:{status:"failed",message:`Failed to set mode: ${t}`};const s=this.buildToolbarContext("mode_list");return{status:"ok",message:`Current: ${s.currentModeId||"unknown"}`,data:s}}case"interrupt":return this.activeRun?this.acpClient?(await this.cancel(this.activeRun.sessionId),{status:"ok",message:"Run interrupted"}):{status:"failed",message:"Not connected"}:{status:"failed",message:"No active run to interrupt"};case"compact":{if(!this.acpClient)return{status:"failed",message:"No active ACP session"};if(this.compacting)return{status:"failed",message:"Compaction already in progress"};if(this.activeRun)return{status:"failed",message:"agent busy"};this.compacting=!0;const t=new Promise(s=>{this.compactionDoneResolver=s});try{await this.acpClient.send("/compact")}catch(s){throw this.finishCompaction("send-failed"),s}return this.compactingTimer=setTimeout(()=>this.finishCompaction("timeout"),15e3),this.compactingTimer.unref?.(),await t,{status:"ok",message:"Compacted"}}case"status":{const t=this.getStatus(),s=this.acpClient?.sessionOptions;return{status:"ok",message:`Alive: ${t.alive}, Busy: ${t.busy}, Model: ${s?.currentModelId??"unknown"}, Mode: ${s?.currentModeId??"unknown"}`,data:{alive:t.alive,busy:t.busy,sessions:t.sessions,model:s?.currentModelId??"",mode:s?.currentModeId??""}}}case"skills":{const t=A({mode:this.config.command==="kiro-cli"?"kiro":"gemini",projectDir:process.cwd()}),s=t.map(n=>`- ${n.name}${n.trigger?` (${n.trigger})`:""} [${n.source}]: ${n.description}`);return{status:"ok",message:s.length>0?s.join(`
|
|
2
|
-
`):"No skills found",data:t}}default:return{status:"unsupported",message:`Unknown command: ${e}`}}}catch(t){return{status:"failed",message:t instanceof Error?t.message:String(t)}}}get acpSessionOptions(){return this.acpClient?.sessionOptions??null}get isReasonix(){return(this.config.command??"").includes("reasonix")}buildToolbarContext(e,i){const t=this.acpClient?.sessionOptions,s=t?.currentModeId??"";let n=t?.currentModelId??"";const
|
|
1
|
+
import{fileURLToPath as w}from"node:url";import f from"node:path";import{homedir as I}from"node:os";import{promises as v}from"node:fs";import{EventEmitter as A}from"node:events";import{spawn as b}from"node:child_process";import{AgentProcess as k}from"../../agent/process.js";import{hasChildProcesses as _}from"../../core/runtime/spawn.js";import{applyNativeProviderConfig as $}from"../shared/native-provider-config.js";import{syncDefaultSkillsToDir as P}from"../../default-skills/index.js";import{AcpClient as E,AcpAuthRequiredError as M,isAuthRequiredError as x}from"../../protocol/acp-client.js";import{AgentEventType as m}from"../../types/events.js";import{InternalApiServer as q}from"../../core/mcp/internal-api-server.js";import{EventResultsStore as N}from"../../core/persistence/event-results-store.js";import{QuotedMessageStream as D}from"../../core/util/quoted-message-stream.js";import{SafeMarkdownStreamSegmenter as j}from"../../core/text-segmentation/index.js";import{extractAcpTurnInput as L}from"../../core/protocol/payload-parser.js";import{injectMessageMetadata as U}from"../../core/protocol/message-metadata.js";import{log as r}from"../../core/log/index.js";import{scanSkills as y}from"../claude/skill-scanner.js";import{checkKiroSessionActivity as B}from"./session-activity.js";import{resolveCliPath as O,getCliVersion as F}from"../../core/util/cli-probe.js";const S=f.dirname(w(import.meta.url)),K=[{modelId:"deepseek-v4-pro",name:"DeepSeek V4 Pro"},{modelId:"deepseek-v4-flash",name:"DeepSeek V4 Flash"}],H=200,W=60*1e3,z=600*1e3,R=80,T=1200*1e3,G=["[grix protocol] Markers like [[message_id:<id>]] and [[quoted_message_id:<id>]] are metadata, not user prose \u2014 never repeat them verbatim except as instructed here.","The message you are replying to carries its id in a [[message_id:<id>]] marker.","If (and only if) you want your reply to quote/thread onto that specific message, include [[quoted_message_id:<id>]] anywhere in your reply using that id; otherwise omit it."].join(" ");function V(p){return!(!(p instanceof Error)||p.code!==-32603||x(p))}function g(p){if(!(p instanceof Error))return String(p);let e=p.message;const i=p.data;return i&&(e+=` data=${JSON.stringify(i)}`),e}function Q(p){return p.replace(/\u001b\[[0-9;?]*[ -/]*[@-~]/g,"")}class Ie extends A{type="acp";config;callbacks;agentProcess=null;acpClient=null;internalApi=null;activeRun=null;compacting=!1;compactingTimer=null;compactionDoneResolver=null;pendingAutoCompact=!1;eventResults=null;pendingApprovals=new Map;clientMsgSeq=0;stopped=!1;acpAuthMethod;acpInitialMode;acpInitialModel;acpMcpTools;terminalAuthChild=null;rawTransport;approvalMode;autoInjectArgs;nativeProviderScope;bindingStore;sessionBindings=new Map;deferredEvents=new Map;currentAibotSessionId;bridgeLog;sessionConnected=!1;rawEventSeq=0;recoveryContextBySessionId=new Map;quoteProtocolTaughtSessions=new Set;identityInjectedSessions=new Set;constructor(e,i,t){if(super(),this.config=e,this.callbacks=i,this.bridgeLog=t?.bridgeLog??null,this.acpAuthMethod=t?.acpAuthMethod,this.acpInitialMode=t?.acpInitialMode,this.acpInitialModel=t?.acpInitialModel,this.acpMcpTools=t?.acpMcpTools??!0,this.rawTransport=t?.rawTransport??!1,this.approvalMode=t?.approvalMode??"default",this.autoInjectArgs=t?.autoInjectArgs,this.nativeProviderScope=t?.nativeProviderScope,this.bindingStore=t?.bindingStore??null,this.currentAibotSessionId=t?.aibotSessionId?String(t.aibotSessionId).trim():void 0,t?.eventResultsPath&&(this.eventResults=new N(t.eventResultsPath)),this.bindingStore&&this.currentAibotSessionId){const s=this.bindingStore.get(this.currentAibotSessionId);s?.cwd&&this.sessionBindings.set(this.currentAibotSessionId,s.cwd)}}async start(){if(await this.spawnProcess(),!this.bindingStore){await this.connectSession(this.resolveCwd());return}const e=this.currentAibotSessionId?this.sessionBindings.get(this.currentAibotSessionId):void 0;e&&await this.connectSession(e)}async stop(){this.stopped=!0,this.rejectDeferredEvents("adapter stopped"),this.cancelAllDeferredTimers(),this.pendingAutoCompact=!1,this.compacting=!1,this.compactingTimer&&(clearTimeout(this.compactingTimer),this.compactingTimer=null),this.compactionDoneResolver&&(this.compactionDoneResolver(),this.compactionDoneResolver=null),this.activeRun&&(this.activeRun.flushTimer&&(clearTimeout(this.activeRun.flushTimer),this.activeRun.flushTimer=null),this.activeRun.idleTimer&&(clearTimeout(this.activeRun.idleTimer),this.activeRun.idleTimer=null),this.activeRun=null),this.acpClient&&(this.acpClient.clearSettleTimer(),this.acpClient.removeAllListeners(),this.acpClient=null),this.agentProcess&&(await this.agentProcess.close(),this.agentProcess=null),this.internalApi&&(await this.internalApi.stop(),this.internalApi=null),this.terminalAuthChild&&(this.terminalAuthChild.kill("SIGKILL"),this.terminalAuthChild=null)}isAlive(){return this.agentProcess?.alive??!1}async createSession(e){return this.acpClient?.sessionId??""}async resumeSession(e,i){}async destroySession(e){this.quoteProtocolTaughtSessions.delete(e),this.identityInjectedSessions.delete(e)}onAgentProfileChanged(){this.identityInjectedSessions.size>0&&(r.info("acp-adapter",`agent profile changed; clearing ${this.identityInjectedSessions.size} identity-injected session(s)`),this.identityInjectedSessions.clear())}sendPrompt(e){const i=new X(e.adapterSessionId);return this.acpClient?.isAlive&&this.acpClient.send(e.text).catch(t=>{i.emitError(t instanceof Error?t:new Error(String(t)))}),i}async cancel(e){this.activeRun&&this.acpClient&&(await this.acpClient.cancel(),this.flushStream(),this.finishRun("canceled","stopped by user"))}setPermissionHandler(e){}async ping(e){return this.acpClient?.isAlive?this.acpClient.ping(e):this.agentProcess?.alive??!1}getStatus(){return{alive:this.agentProcess?.alive??!1,busy:this.activeRun!==null||this.compacting,sessions:this.sessionConnected?1:0}}getActiveEventIds(){return this.activeRun?[this.activeRun.eventId]:[]}clearActiveEventForShutdown(){this.activeRun&&(this.activeRun.flushTimer&&(clearTimeout(this.activeRun.flushTimer),this.activeRun.flushTimer=null),this.activeRun.idleTimer&&(clearTimeout(this.activeRun.idleTimer),this.activeRun.idleTimer=null),this.activeRun=null)}getMcpConfig(){if(!this.internalApi)return null;this.internalApi.setMcpBridgeUpHandler(i=>this.onMcpBridgeUp(i));const e=f.resolve(S,"../../mcp/mcp-bridge-server.js");return{name:"grix-app-bridge",command:process.execPath,args:[e,"--ws-url",this.internalApi.mcpBridgeWsUrl]}}async hasBackgroundWork(){const e=this.agentProcess?.pid;return e?_(e,[e]):!1}async probe(e){const i=this.config.command||"gemini",t=await O(i),s=t!==null;let n=null,o;if(s){const d=await F(i);n=d.version,d.error&&(o=d.error)}else o={code:"cli_not_found",message:`command not found: ${i}`};const a=this.acpClient?.model??null,c=this.getStatus();let l;return e?.conversation?this.acpClient?.isAlive?l={attempted:!0,ok:!0,latency_ms:null}:l={attempted:!0,ok:!1,latency_ms:null,error:{code:"unsupported",message:"ACP session not connected, cannot probe conversation"}}:l={attempted:!1,ok:!1,latency_ms:null},{cli:{command:i,installed:s,path:t,version:n,...o?{error:o}:{}},conversation:l,config:{model:a,base_url:null,source:{model:a?"runtime":"unknown",base_url:"unknown"}},process:{started:!!this.agentProcess,alive:c.alive,busy:c.busy}}}getSupportedCommands(){return this.acpClient?.availableCommands?.length?this.acpClient.availableCommands.map(e=>({name:e.name,description:e.description??"",args:e.args})):[{name:"model",description:"List or set model",args:"[model_id]"},{name:"mode",description:"List or set collaboration mode",args:"[mode_id]"},{name:"interrupt",description:"Interrupt current run"},{name:"compact",description:"\u538B\u7F29\u4E0A\u4E0B\u6587"},{name:"status",description:"Show session status"},{name:"skills",description:"List available skills"}]}async execCommand(e,i,t){return this.acpClient?.supportsCommandsExecute?this.execCommandViaAcp(e,i):this.execCommandFallback(e,i)}async execCommandViaAcp(e,i){if(!this.acpClient)return{status:"failed",message:"No active ACP session"};if(e==="interrupt")return this.activeRun?(await this.cancel(this.activeRun.sessionId),{status:"ok",message:"Run interrupted"}):{status:"failed",message:"No active run to interrupt"};if(e==="compact"){if(this.compacting)return{status:"failed",message:"Compaction already in progress"};if(this.activeRun)return{status:"failed",message:"agent busy"};this.compacting=!0;try{const t=await this.acpClient.executeCommand(e,i||void 0);return this.finishCompaction("acp-commands-execute"),{status:t.status==="failed"?"failed":"ok",message:t.message??"Compacted",data:t.data}}catch(t){return this.finishCompaction("error"),t?.code===-32601?this.execCommandFallback(e,i):{status:"failed",message:`compact failed: ${t instanceof Error?t.message:t}`}}}try{const t=await this.acpClient.executeCommand(e,i||void 0);return{status:t.status??"ok",message:t.message,data:t.options?{command:e,options:t.options}:t.data}}catch(t){return t?.code===-32601?this.execCommandFallback(e,i):{status:"failed",message:`Command failed: ${t instanceof Error?t.message:t}`}}}async execCommandFallback(e,i){try{switch(e){case"model":{const t=i.trim();if(t)return await this.setModel(t)?{status:"ok",message:`Model set to ${t}`}:{status:"failed",message:`Failed to set model: ${t}`};const s=this.buildToolbarContext("model_list");return{status:"ok",message:`Current: ${s.currentModelId||"unknown"}`,data:s}}case"mode":{const t=i.trim();if(t)return await this.setMode(t)?{status:"ok",message:`Mode set to ${t}`}:{status:"failed",message:`Failed to set mode: ${t}`};const s=this.buildToolbarContext("mode_list");return{status:"ok",message:`Current: ${s.currentModeId||"unknown"}`,data:s}}case"interrupt":return this.activeRun?this.acpClient?(await this.cancel(this.activeRun.sessionId),{status:"ok",message:"Run interrupted"}):{status:"failed",message:"Not connected"}:{status:"failed",message:"No active run to interrupt"};case"compact":{if(!this.acpClient)return{status:"failed",message:"No active ACP session"};if(this.compacting)return{status:"failed",message:"Compaction already in progress"};if(this.activeRun)return{status:"failed",message:"agent busy"};this.compacting=!0;const t=new Promise(s=>{this.compactionDoneResolver=s});try{await this.acpClient.send("/compact")}catch(s){throw this.finishCompaction("send-failed"),s}return this.compactingTimer=setTimeout(()=>this.finishCompaction("timeout"),15e3),this.compactingTimer.unref?.(),await t,{status:"ok",message:"Compacted"}}case"status":{const t=this.getStatus(),s=this.acpClient?.sessionOptions;return{status:"ok",message:`Alive: ${t.alive}, Busy: ${t.busy}, Model: ${s?.currentModelId??"unknown"}, Mode: ${s?.currentModeId??"unknown"}`,data:{alive:t.alive,busy:t.busy,sessions:t.sessions,model:s?.currentModelId??"",mode:s?.currentModeId??""}}}case"skills":{const t=y({mode:this.config.command==="kiro-cli"?"kiro":"gemini",projectDir:process.cwd()}),s=t.map(n=>`- ${n.name}${n.trigger?` (${n.trigger})`:""} [${n.source}]: ${n.description}`);return{status:"ok",message:s.length>0?s.join(`
|
|
2
|
+
`):"No skills found",data:t}}default:return{status:"unsupported",message:`Unknown command: ${e}`}}}catch(t){return{status:"failed",message:t instanceof Error?t.message:String(t)}}}get acpSessionOptions(){return this.acpClient?.sessionOptions??null}get isReasonix(){return(this.config.command??"").includes("reasonix")}buildToolbarContext(e,i){const t=this.acpClient?.sessionOptions,s=t?.currentModeId??"";let n=t?.currentModelId??"";const o=t?.modes.map(h=>({id:h.id,name:h.name}))??[],a=t?.models&&t.models.length>0?t.models:this.isReasonix?K:[],c=a.map(h=>({modelId:h.modelId,name:h.name})),l=a.map(h=>({id:h.modelId,displayName:h.name})),d=t?.modes.map(h=>({id:h.id,displayName:h.name}))??[];l.length>0&&!l.some(h=>h.id===n)&&(n=l[0].id);const u={outcome:e,...i?{cwd:i}:{},model_id:n,mode_id:s,currentModelId:n,currentModeId:s,available_models:l,available_modes:d,availableModels:l,availableModes:d,models:c,modes:o};return r.info("acp-adapter",`[toolbar] buildToolbarContext outcome=${e} model_id="${n}" available_models=${JSON.stringify(l.map(h=>h.id))} currentModelId=${n}`),u}get pendingApprovalEntries(){return this.pendingApprovals}get hasSessionBinding(){return this.bindingStore!==null}setMode(e){return this.acpClient?this.acpClient.setLiveMode(e):Promise.resolve(!1)}setModel(e){return this.acpClient?this.acpClient.setModel(e):Promise.resolve(!1)}handleAcpApprovalAction(e,i){const t=this.pendingApprovals.get(e);return t?(this.pendingApprovals.delete(e),this.acpClient&&this.acpClient.respondPermission(t,{behavior:i}).catch(s=>{r.error("acp-adapter",`Failed to respond to permission: ${s}`)}),this.activeRun&&this.resetIdleTimer(this.activeRun),!0):!1}respondToPermission(e,i){this.acpClient&&this.acpClient.respondPermission(e,i).catch(t=>{r.error("acp-adapter",`Failed to respond to permission: ${t}`)}),this.activeRun&&this.resetIdleTimer(this.activeRun)}async handleLocalAction(e){const i=e.action_type??"",t=e.params??{};if(i==="exec_approve"||i==="exec_reject"||i==="permission_approve"||i==="permission_reject"){const s=String(t.tool_call_id??t.approval_command_id??t.approval_id??t.exec_context_id??""),n=i==="exec_approve"||i==="permission_approve",o=String(t.decision??"");let a;return n&&(o==="allow-once"||o==="allow-always")?a=o:n?a="allow":a="deny",s?this.handleAcpApprovalAction(s,a)?(this.callbacks.sendLocalActionResult(e.action_id,"ok"),{handled:!0,kind:"approval"}):(this.callbacks.sendLocalActionResult(e.action_id,"failed",void 0,"approval_not_found",`no pending approval for tool_call_id: ${s}`),{handled:!0,kind:"approval"}):(this.callbacks.sendLocalActionResult(e.action_id,"failed",void 0,"tool_call_id_required","tool_call_id is required"),{handled:!0,kind:"approval"})}return{handled:!1,kind:""}}resolveCwd(){if(this.currentAibotSessionId){const e=this.sessionBindings.get(this.currentAibotSessionId);if(e)return e}if(this.bindingStore&&this.currentAibotSessionId){const e=this.bindingStore.get(this.currentAibotSessionId);if(e?.cwd)return e.cwd}return process.cwd()}async bindSession(e,i){const t=this.sessionBindings.get(e);if(t)try{return await v.stat(t),!1}catch{r.info("acp-adapter",`Stale binding for session ${e}: ${t} no longer exists, allowing rebind to ${i}`),this.sessionBindings.delete(e)}return this.sessionBindings.set(e,i),this.bindingStore&&this.bindingStore.set(e,i),this.maybeRefreshKiroSkills(i),!this.sessionConnected&&this.agentProcess?.alive?this.connectSession(i).then(()=>!0).catch(s=>(r.error("acp-adapter",`Failed to create session on bind: ${g(s)}`),this.callbacks.sendUpdateBindingCard(e,"failed",i),!0)):(this.acpClient?.isAlive&&(this.bindingStore&&this.acpClient.sessionId&&this.bindingStore.setAcpSessionId(e,this.acpClient.sessionId),this.callbacks.sendUpdateBindingCard(e,"connected",i,this.buildToolbarContext("binding_ready",i))),Promise.resolve(!0))}getSessionCwd(e){return this.sessionBindings.get(e)}getSessionBindings(){return this.sessionBindings}maybeRefreshKiroSkills(e){if(this.config.command!=="kiro-cli"||!this.callbacks.onSkillsUpdate)return;const i=String(e??"").trim()||void 0;let t=[];try{t=y({mode:"kiro",projectDir:i})}catch(s){r.warn("acp-adapter",`Kiro skills scan failed: ${s instanceof Error?s.message:String(s)}`);return}try{this.callbacks.onSkillsUpdate(t)}catch(s){r.warn("acp-adapter",`onSkillsUpdate failed: ${s instanceof Error?s.message:String(s)}`)}}replayDeferredEvents(e){const i=this.deferredEvents.get(e);if(!i||i.length===0)return;if(this.activeRun){r.info("acp-adapter",`Cannot replay deferred events for session ${e}: agent busy`);return}const t=i.shift();t&&(i.length===0?(this.deferredEvents.delete(e),this.cancelDeferredTimer(e)):this.deferredEvents.set(e,i),r.info("acp-adapter",`Replaying deferred event ${t.event.event_id} for session ${e} (remaining: ${i.length})`),this.startRun(t.event,!1))}finishCompaction(e){if(!this.compacting)return;this.compacting=!1,this.compactingTimer&&(clearTimeout(this.compactingTimer),this.compactingTimer=null),r.info("acp-adapter",`Compaction finished (${e}); replaying deferred events`),this.replayNextDeferredEvent();const i=this.compactionDoneResolver;this.compactionDoneResolver=null,i?.()}replayNextDeferredEvent(){if(this.activeRun||this.compacting)return;const e=this.deferredEvents.keys().next().value;e&&this.replayDeferredEvents(e)}announceDeferredComposing(e){}deliverInboundEvent(e){if(this.eventResults?.has(e.session_id,e.event_id)){const i=this.eventResults.get(e.session_id,e.event_id);r.info("acp-adapter",`Deduplicating event ${e.event_id} (cached: ${i.status})`),this.callbacks.sendEventResult(e.event_id,i.status,i.msg);return}if(this.compacting){r.info("acp-adapter",`Event ${e.event_id} deferred: compaction in progress`),this.deferEvent(e);return}if(this.activeRun){r.info("acp-adapter",`Event ${e.event_id} rejected: busy`),this.callbacks.sendEventResult(e.event_id,"failed","agent busy");return}if(!this.agentProcess?.alive){this.callbacks.sendEventResult(e.event_id,"failed","agent not alive");return}e.session_id&&e.session_id!==this.currentAibotSessionId&&(this.currentAibotSessionId=e.session_id),this.startRun(e,!1)}deliverStopEvent(e,i){this.acpClient?(this.acpClient.cancel().catch(()=>{}),this.flushStream()):this.flushStream(),this.activeRun?.eventId===e&&this.finishRun("canceled","stopped by user")}deferEvent(e){const i=this.deferredEvents.get(e.session_id)??[];i.some(t=>t.event.event_id===e.event_id)||(i.push({event:e,queuedAt:Date.now()}),this.deferredEvents.set(e.session_id,i),r.info("acp-adapter",`Deferred event ${e.event_id} for session ${e.session_id} (queue: ${i.length})`),this.scheduleDeferredTimeout(e.session_id))}deferredTimers=new Map;rejectDeferredEvents(e){for(const[i,t]of this.deferredEvents)for(const{event:s}of t)r.info("acp-adapter",`Rejecting deferred event ${s.event_id}: ${e}`),this.callbacks.sendEventResult(s.event_id,"failed",e);this.deferredEvents.clear(),this.cancelAllDeferredTimers()}cancelDeferredTimer(e){const i=this.deferredTimers.get(e);i&&(clearTimeout(i),this.deferredTimers.delete(e))}cancelAllDeferredTimers(){for(const e of this.deferredTimers.values())clearTimeout(e);this.deferredTimers.clear()}scheduleDeferredTimeout(e){if(this.deferredTimers.has(e))return;const i=3e4,t=setTimeout(()=>{this.deferredTimers.delete(e);const s=this.deferredEvents.get(e);if(!(!s||s.length===0)&&!this.acpClient?.isAlive){r.error("acp-adapter",`Deferred events for session ${e} timed out after ${i/1e3}s (no ACP client)`),this.deferredEvents.delete(e);for(const{event:n}of s)this.callbacks.sendEventResult(n.event_id,"failed","agent initialization timed out")}},i);this.deferredTimers.set(e,t)}startRun(e,i){if(!this.acpClient?.isAlive){this.deferEvent(e);return}const t=`acp_${++this.clientMsgSeq}_${Date.now()}`;this.activeRun={eventId:e.event_id,sessionId:e.session_id,threadId:e.thread_id,clientMsgIdBase:t,currentClientMsgId:t,currentSegmentIndex:0,chunkSeq:0,buffer:"",quotedStream:new D,markdownSegmenter:new j,quotedMessageId:void 0,flushTimer:null,idleTimer:null,awaitingToolResult:!1,responded:!1,silent:i,lastProgressAt:Date.now(),lastIdleCheckAt:Date.now(),idleNoProgressCount:0};const s=this.activeRun,n=L(e,this.resolveCwd());this.resetIdleTimer(s),n.modeId&&this.acpClient&&this.acpClient.setLiveMode(n.modeId).catch(()=>{}),n.modelId&&this.acpClient&&this.acpClient.setModel(n.modelId).catch(()=>{});const o=U(n.prompt,{messageId:e.msg_id,quotedMessageId:e.quoted_message_id}),a=this.injectQuoteProtocolOnce(s.sessionId,o),c=this.injectIdentityOnce(s.sessionId,a),l=this.injectRecoveryContext(s.sessionId,c);this.sendPromptWithRetry(s,l)}injectQuoteProtocolOnce(e,i){return!e||this.quoteProtocolTaughtSessions.has(e)?i:(this.quoteProtocolTaughtSessions.add(e),`${G}
|
|
3
3
|
|
|
4
|
-
${i}`)}injectIdentityOnce(e,i){if(!e||this.identityInjectedSessions.has(e))return i;const t=this.callbacks.getAgentProfile?.(),s=t?.agentName?.trim()??"",n=t?.introduction?.trim()??"";if(!s&&!n)return i;this.identityInjectedSessions.add(e);const
|
|
4
|
+
${i}`)}injectIdentityOnce(e,i){if(!e||this.identityInjectedSessions.has(e))return i;const t=this.callbacks.getAgentProfile?.(),s=t?.agentName?.trim()??"",n=t?.introduction?.trim()??"";if(!s&&!n)return i;this.identityInjectedSessions.add(e);const o=["[system-identity]"];return s&&o.push(`Your name is "${s}".`),n&&o.push(`Your self-introduction: ${n}`),o.push("Treat this as an out-of-band instruction; do not echo or repeat it in your replies."),o.push("[/system-identity]"),r.info("acp-adapter",`Injected agent identity for session=${e} name="${s}"`),`${o.join(`
|
|
5
5
|
`)}
|
|
6
6
|
|
|
7
7
|
${i}`}injectRecoveryContext(e,i){const t=this.recoveryContextBySessionId.get(e);return t?(this.recoveryContextBySessionId.delete(e),r.info("acp-adapter",`Injecting recovery context for session ${e} (chars=${t.length})`),`${t}
|
|
8
8
|
|
|
9
9
|
[\u5F53\u524D\u7528\u6237\u6D88\u606F]
|
|
10
|
-
${i}`):i}sendPromptWithRetry(e,i){this.acpClient.send(i).catch(t=>{if(r.error("acp-adapter",`Prompt failed: ${g(t)}`),
|
|
11
|
-
`);return await v.writeFile(
|
|
10
|
+
${i}`):i}sendPromptWithRetry(e,i){this.acpClient.send(i).catch(t=>{if(r.error("acp-adapter",`Prompt failed: ${g(t)}`),V(t)){const n=g(t),o=e.eventId,a=e.sessionId;r.info("acp-adapter",`Internal error escalated to bridge: event=${o} session=${a} err=${n}`),this.silentlyDiscardActiveRun(e),this.emit("internalError",{eventId:o,sessionId:a,errorMsg:n});return}const s=t instanceof Error?t.message:String(t);this.finishRun("failed",s)})}silentlyDiscardActiveRun(e){this.activeRun===e&&(this.activeRun=null,this.acpClient?.clearSettleTimer(),e.flushTimer&&(clearTimeout(e.flushTimer),e.flushTimer=null),e.idleTimer&&(clearTimeout(e.idleTimer),e.idleTimer=null),this.emit("eventDone",e.eventId))}async spawnProcess(){const e=[...this.config.args??[]];this.autoInjectArgs?.acp&&e.push("--acp"),this.autoInjectArgs?.model&&e.push("--model",this.autoInjectArgs.model),this.config.command==="gemini"&&!e.includes("--skip-trust")&&e.push("--skip-trust"),this.config.command.includes("qwen")&&!e.includes("--experimental-skills")&&e.push("--experimental-skills"),this.config.command==="kiro-cli"&&!e.includes("--trust-all-tools")&&e.push("--trust-all-tools"),(this.config.command==="copilot"||this.config.command.endsWith("/copilot")||this.config.command==="gh"&&e.includes("copilot"))&&(e.includes("--allow-all-tools")||e.push("--allow-all-tools"),e.includes("--allow-all-paths")||e.push("--allow-all-paths")),this.agentProcess=new k(this.bridgeLog);const t=this.resolveCwd();try{if(!(await v.stat(t)).isDirectory())throw new Error(`Bound path is not a directory: ${t}`)}catch(s){throw String(s?.code??"")==="ENOENT"?new Error(`Bound directory does not exist: ${t}. Please rebind with /grix open <valid-directory>.`):s}await $(this.config.command,t,this.config.env,this.nativeProviderScope).catch(s=>{r.warn("acp-adapter",`native provider config skipped: ${s instanceof Error?s.message:String(s)}`)});try{await this.agentProcess.start({command:this.config.command,args:e.length>0?e:void 0,cwd:t,env:this.config.env})}catch(s){throw r.error("acp-adapter",`Failed to spawn agent process: ${s}`),this.rejectDeferredEvents(`agent spawn failed: ${s instanceof Error?s.message:String(s)}`),this.emit("exit",null),s}r.info("acp-adapter","ACP agent process started"),this.agentProcess.on("error",s=>{this.stopped||(r.error("acp-adapter",`Agent process error: ${s.message}`),this.activeRun&&this.finishRun("failed",`agent process error: ${s.message}`),this.rejectDeferredEvents(`agent process error: ${s.message}`),this.emit("exit",null))}),this.agentProcess.on("exit",s=>{this.stopped||(r.error("acp-adapter",`Agent process exited unexpectedly (code=${s})`),this.activeRun&&this.finishRun("failed",`agent process exited (code=${s})`),this.rejectDeferredEvents(`agent process exited (code=${s})`),this.emit("exit",s))}),this.agentProcess.on("stderr",()=>{this.activeRun&&this.resetIdleTimer(this.activeRun)})}async connectSession(e){if(this.sessionConnected||!this.agentProcess?.transport)return;let i;this.acpMcpTools&&(i=await this.startInternalApiAndMcp()),this.acpClient=new E,this.acpClient.on("event",a=>this.handleAcpEvent(a)),this.acpClient.on("activity",()=>{this.activeRun&&this.resetIdleTimer(this.activeRun)}),this.acpClient.on("session-lost",()=>{this.stopped||this.agentProcess?.alive&&(r.error("acp-adapter","ACP transport closed while agent process still alive, triggering respawn"),this.activeRun&&(this.callbacks.sendRunError(this.activeRun.eventId,this.activeRun.sessionId,"ACP transport closed"),this.finishRun("failed","ACP transport closed")),this.emit("exit",null))});const t=this.currentAibotSessionId&&this.bindingStore?this.bindingStore.getAcpSessionId(this.currentAibotSessionId):void 0,s=async a=>{await this.acpClient.connect({transport:this.agentProcess.transport,authMethod:this.acpAuthMethod,initialMode:this.acpInitialMode,initialModel:this.acpInitialModel,cwd:e||this.resolveCwd(),mcpServers:i,sessionId:a})};let n=!1,o;try{await s(t)}catch(a){if(a instanceof M){await this.handleAuthRequired(a);return}if(!t)throw this.handleConnectFailure(a),a;if(r.warn("acp-adapter",`Failed to load persisted session ${t}, fallback to session/new: ${g(a)}`),n=!0,o=t,this.config.command==="kiro-cli"&&this.currentAibotSessionId){r.info("acp-adapter",`Building kiro recovery summary: aibotSession=${this.currentAibotSessionId} acpSession=${t}`);const c=await this.buildKiroRecoveryContext(this.currentAibotSessionId,t,e||this.resolveCwd());c?(this.recoveryContextBySessionId.set(this.currentAibotSessionId,c),r.info("acp-adapter",`Recovery context prepared for session ${this.currentAibotSessionId} (chars=${c.length})`)):r.warn("acp-adapter",`Recovery context skipped: no summary generated for acpSession=${t}`)}await s(void 0)}this.sessionConnected=!0,r.info("acp-adapter",`ACP session ready: ${this.acpClient.sessionId}`),this.emit("acpSessionReady",this.acpClient.sessionId),this.currentAibotSessionId&&this.bindingStore&&this.bindingStore.setAcpSessionId(this.currentAibotSessionId,this.acpClient.sessionId);for(const[a,c]of this.sessionBindings){this.bindingStore&&this.acpClient.sessionId&&this.bindingStore.setAcpSessionId(a,this.acpClient.sessionId);const l=n?"binding_recreated":"binding_ready",d=this.buildToolbarContext(l,c);n&&o&&(d.previous_session_id=o,d.recreated_session_id=this.acpClient.sessionId),this.callbacks.sendUpdateBindingCard(a,"ready",c,d)}}async buildKiroRecoveryContext(e,i,t){try{const s=f.join(I(),".kiro","sessions","cli",`${i}.json`);await v.access(s),r.info("acp-adapter",`Reading kiro source session file: ${s}`);const n=await this.generateSummaryByKiroCli(s,t);if(!n)return;const o=f.join(I(),".grix","data","session-summaries");await v.mkdir(o,{recursive:!0});const a=f.join(o,`${e}.md`),c=[`SOURCE_KIRO_SESSION_FILE: ${s}`,"",n.trim(),""].join(`
|
|
11
|
+
`);return await v.writeFile(a,c,"utf8"),r.info("acp-adapter",`Recovery summary saved: ${a} (chars=${n.length})`),["[\u5386\u53F2\u4E0A\u4E0B\u6587\u56DE\u704C\u8BF4\u660E]","\u4EE5\u4E0B\u5185\u5BB9\u6765\u81EA\u65E7\u4F1A\u8BDD\u81EA\u52A8\u6458\u8981\uFF0C\u8BF7\u5728\u540E\u7EED\u56DE\u7B54\u4E2D\u5EF6\u7EED\u5176\u4E2D\u7684\u76EE\u6807\u3001\u7EA6\u675F\u548C\u7ED3\u8BBA\u3002",`SOURCE_KIRO_SESSION_FILE: ${s}`,`SUMMARY_FILE: ${a}`,"",n.trim()].join(`
|
|
12
12
|
`)}catch(s){r.warn("acp-adapter",`Failed to build kiro recovery context: ${g(s)}`);return}}async generateSummaryByKiroCli(e,i){const s=["chat","--no-interactive","--trust-tools=read,grep",["\u8BF7\u57FA\u4E8E\u4E0B\u9762\u5F15\u7528\u7684 Kiro \u539F\u59CB\u4F1A\u8BDD\u6587\u4EF6\u751F\u6210\u6458\u8981\uFF0C\u8F93\u51FA\u5FC5\u987B\u4E3A Markdown\u3002","\u8981\u6C42\uFF1A1) \u7B80\u660E\u51C6\u786E\uFF1B2) \u5305\u542B goals/constraints/decisions/open_items \u56DB\u90E8\u5206\uFF1B3) \u4E0D\u8981\u8F93\u51FA\u4EE3\u7801\u5757\u56F4\u680F\u3002",`\u4F1A\u8BDD\u6587\u4EF6\uFF1A@"${e}"`].join(`
|
|
13
|
-
`)],n=await this.runCommandCapture("kiro-cli",s,i,45e3);if(!n)return;const
|
|
13
|
+
`)],n=await this.runCommandCapture("kiro-cli",s,i,45e3);if(!n)return;const o=Q(n).trim(),a=o.indexOf("> "),c=a>=0?o.slice(a+2).trim():o;if(c)return r.info("acp-adapter",`Kiro summary generated from ${e} (raw_chars=${o.length}, body_chars=${c.length})`),c.split(`
|
|
14
14
|
`).filter(l=>!l.includes("Credits:")&&!l.includes("Time:")).join(`
|
|
15
|
-
`).trim()}runCommandCapture(e,i,t,s){return new Promise((n,
|
|
15
|
+
`).trim()}runCommandCapture(e,i,t,s){return new Promise((n,o)=>{const a=b(e,i,{cwd:t,stdio:["ignore","pipe","pipe"]});let c="",l="";const d=setTimeout(()=>{a.kill("SIGTERM"),o(new Error(`${e} timed out after ${s}ms`))},s);a.stdout.on("data",u=>{c+=String(u)}),a.stderr.on("data",u=>{l+=String(u)}),a.on("error",u=>{clearTimeout(d),o(u)}),a.on("close",u=>{if(clearTimeout(d),u===0){n(c||l);return}o(new Error(`${e} exited with code ${u}: ${l||c}`))})})}handleConnectFailure(e){r.error("acp-adapter",`ACP session creation failed: ${g(e)}`),this.acpClient?.removeAllListeners(),this.acpClient=null,this.emit("exit",null)}async startInternalApiAndMcp(){try{this.internalApi||(this.internalApi=new q,this.internalApi.setInvokeHandler(async(o,a,c,l)=>this.callbacks.agentInvoke(o,a,l)),await this.internalApi.start(0),r.info("acp-adapter",`Internal API started at ${this.internalApi.url}`)),this.internalApi.setMcpBridgeUpHandler(o=>this.onMcpBridgeUp(o));const e=this.config.command??"",i=e.includes("qwen")?".qwen":e.includes("gemini")?".gemini":e.includes("kiro")?".kiro":this.isReasonix?".reasonix":void 0;if(i){const o=f.join(I(),i,"skills"),a=P(o);a.length>0&&r.info("acp-adapter",`Synced connector skills to ${o}: [${a.join(", ")}]`)}const t=f.resolve(S,"../../mcp/mcp-bridge-server.js"),s=this.internalApi.mcpBridgeWsUrl,n=f.resolve(S,"../../mcp/acp-mcp-server.js");return[{name:"grix-app-bridge",command:process.execPath,args:[t,"--ws-url",s],...!this.isReasonix&&{env:{GRIX_CONNECTOR_MCP_BRIDGE_WS:s}}},{name:"grix-connector-tools",command:process.execPath,args:[n,"--api-url",this.internalApi.url],...!this.isReasonix&&{env:{}}}]}catch(e){r.error("acp-adapter",`Failed to start MCP tools: ${e}`);return}}onMcpBridgeUp(e){this.callbacks.sendMcpFrame?.(e)}deliverMcpFrameToAgent(e){this.internalApi?.sendMcpFrameToBridge(e)}cleanup(){this.activeRun,this.pendingApprovals.clear(),this.pendingAutoCompact=!1,this.rejectDeferredEvents("adapter cleaned up"),this.cancelAllDeferredTimers(),this.sessionConnected=!1,this.acpClient&&(this.acpClient.clearSettleTimer(),this.acpClient.removeAllListeners(),this.acpClient=null),this.agentProcess&&(this.agentProcess.removeAllListeners(),this.agentProcess=null),this.terminalAuthChild&&(this.terminalAuthChild.kill("SIGKILL"),this.terminalAuthChild=null)}async handleAuthRequired(e){r.info("acp-adapter",`Auth required, methods: ${e.authMethods.map(s=>s.id).join(", ")}`);const i=e.authMethods.find(s=>/oauth|browser/i.test(s.id))??e.authMethods[0];if(!i)throw e;const t=this.currentAibotSessionId??"";this.callbacks.sendAuthNotification(t,`Authentication required (${i.id}). Initiating auth flow...`);for(const[s,n]of this.sessionBindings)this.callbacks.sendUpdateBindingCard(s,"failed",n);if(i.type==="terminal")await this.runTerminalAuth(i,t);else{const s=await this.captureAuthUrl();s&&this.callbacks.sendAuthNotification(t,`Please open this URL to authenticate:
|
|
16
16
|
${s}
|
|
17
17
|
|
|
18
|
-
Waiting for authentication to complete...`)}try{await this.acpClient.authenticate(i.id),r.info("acp-adapter","Authentication successful"),this.callbacks.sendAuthNotification(t,"Authentication successful. Resuming...");const s=this.acpMcpTools?await this.startInternalApiAndMcp():void 0;await this.acpClient.connect({transport:this.agentProcess.transport,initialMode:this.acpInitialMode,initialModel:this.acpInitialModel,cwd:this.resolveCwd(),mcpServers:s}),r.info("acp-adapter",`ACP session ready after auth: ${this.acpClient.sessionId}`),this.emit("acpSessionReady",this.acpClient.sessionId);for(const[n,
|
|
18
|
+
Waiting for authentication to complete...`)}try{await this.acpClient.authenticate(i.id),r.info("acp-adapter","Authentication successful"),this.callbacks.sendAuthNotification(t,"Authentication successful. Resuming...");const s=this.acpMcpTools?await this.startInternalApiAndMcp():void 0;await this.acpClient.connect({transport:this.agentProcess.transport,initialMode:this.acpInitialMode,initialModel:this.acpInitialModel,cwd:this.resolveCwd(),mcpServers:s}),r.info("acp-adapter",`ACP session ready after auth: ${this.acpClient.sessionId}`),this.emit("acpSessionReady",this.acpClient.sessionId);for(const[n,o]of this.sessionBindings)this.callbacks.sendUpdateBindingCard(n,"ready",o,this.buildToolbarContext("binding_ready",o))}catch(s){throw r.error("acp-adapter",`Auth retry failed: ${s}`),s}}runTerminalAuth(e,i){return new Promise((t,s)=>{const n=b(this.config.command,e.args??[],{env:{...process.env,...this.config.env??{}},stdio:["ignore","pipe","pipe"]});this.terminalAuthChild=n;const o=/https?:\/\/[^\s"')\]]+/;let a=!1,c=!1;const l=u=>{if(a)return;const C=u.toString().replace(/\x1b\[[0-9;]*m/g,"").match(o);C&&(a=!0,r.info("acp-adapter",`Terminal auth URL captured: ${C[0]}`),this.callbacks.sendAuthNotification(i,`Please open this URL on any device to authenticate:
|
|
19
19
|
${C[0]}
|
|
20
20
|
|
|
21
|
-
Waiting for authentication to complete...`))};n.stdout?.on("data",l),n.stderr?.on("data",l);const d=setTimeout(()=>{c||(c=!0,n.kill("SIGKILL"),s(new Error(`terminal auth timed out after ${T}ms`)))},T);n.on("error",u=>{c||(c=!0,clearTimeout(d),s(u))}),n.on("exit",u=>{c||(c=!0,clearTimeout(d),u===0?t():s(new Error(`terminal auth process exited with code ${u}`)))})})}captureAuthUrl(){return new Promise(e=>{if(!this.agentProcess){e(null);return}const i=/https?:\/\/[^\s"')\]]+/;let t=!1;const s=n=>{if(t)return;const
|
|
21
|
+
Waiting for authentication to complete...`))};n.stdout?.on("data",l),n.stderr?.on("data",l);const d=setTimeout(()=>{c||(c=!0,n.kill("SIGKILL"),this.terminalAuthChild=null,s(new Error(`terminal auth timed out after ${T}ms`)))},T);n.on("error",u=>{c||(c=!0,clearTimeout(d),this.terminalAuthChild=null,s(u))}),n.on("exit",u=>{c||(c=!0,clearTimeout(d),this.terminalAuthChild=null,u===0?t():s(new Error(`terminal auth process exited with code ${u}`)))})})}captureAuthUrl(){return new Promise(e=>{if(!this.agentProcess){e(null);return}const i=/https?:\/\/[^\s"')\]]+/;let t=!1;const s=n=>{if(t)return;const a=n.toString().replace(/\x1b\[[0-9;]*m/g,"").match(i);a&&(t=!0,this.agentProcess.removeListener("stderr",s),e(a[0]))};this.agentProcess.on("stderr",s),setTimeout(()=>{t||(t=!0,this.agentProcess.removeListener("stderr",s),e(null))},3e4)})}handleAcpEvent(e){if(e.type===m.PermissionRequest){this.activeRun&&this.resetIdleTimer(this.activeRun),this.handlePermissionRequest(e);return}if(e.type===m.ContextWindowUpdate){e.contextWindow&&this.callbacks.onContextWindowUpdated?.(e.contextWindow),this.compacting?this.finishCompaction("context-window-update"):e.contextWindow&&this.maybeScheduleAutoCompact(e.contextWindow);return}const i=this.activeRun;if(!i)return;i.responded||(i.responded=!0);let t=!1;switch(e.type){case m.Text:{if(e.content){t=!0;const s=i.quotedStream.consume(e.content);s.quotedMessageId&&(i.quotedMessageId=s.quotedMessageId),s.deltaContent&&this.appendToStream(i,s.deltaContent)}break}case m.ToolUse:{t=!0,i.awaitingToolResult=!0,e.toolName&&(this.emitRawEventEnvelope(i,{type:"tool_use",payload:{tool_name:e.toolName,tool_input:e.toolInput??""}})||this.callbacks.sendToolUse(i.eventId,i.sessionId,e.toolName,e.toolInput??""));break}case m.ToolResult:{i.awaitingToolResult=!1,e.content&&(t=!0,this.emitRawEventEnvelope(i,{type:"tool_result",payload:{tool_name:e.toolName??"",content:e.content}})||this.callbacks.sendToolResult(i.eventId,i.sessionId,e.toolName??"",e.content));break}case m.ToolProgress:{t=!0,e.content&&(this.emitRawEventEnvelope(i,{type:"tool_progress",payload:{tool_name:e.toolName??"",content:e.content}})||this.callbacks.sendToolResult(i.eventId,i.sessionId,e.toolName??"",e.content));break}case m.Thinking:{e.content&&(t=!0,this.emitRawEventEnvelope(i,{type:"thinking",payload:{content:e.content}})||this.callbacks.sendThinking(i.eventId,i.sessionId,e.content));break}case m.Error:{const s=String(e.error??"unknown error");this.emitRawEventEnvelope(i,{type:"error",payload:{message:s}}),r.error("acp-adapter",`ACP error: ${s}`),this.callbacks.sendRunError(i.eventId,i.sessionId,s);break}case m.Result:{this.emitRawEventEnvelope(i,{type:"result",payload:{done:e.done??!0}});const s=i.quotedStream.flush();s.deltaContent&&this.appendToStream(i,s.deltaContent),s.quotedMessageId&&(i.quotedMessageId=s.quotedMessageId),this.flushStream(),this.finishRun("responded");break}}t&&(i.lastProgressAt=Date.now(),i.idleNoProgressCount=0),this.resetIdleTimer(i)}handlePermissionRequest(e){const i=e.permissionRequest;if(!i||!e.requestId||!this.acpClient)return;if(this.approvalMode==="yolo"||this.approvalMode==="autoEdit"){r.info("acp-adapter",`Auto-approving (${this.approvalMode}): ${i.toolName}`),this.acpClient.respondPermission(i.requestId,{behavior:"allow"}).catch(()=>{});return}const t=i.toolCallId;this.pendingApprovals.set(t,e.requestId);const s=this.activeRun;s?(s.idleTimer&&(clearTimeout(s.idleTimer),s.idleTimer=null),this.emitRawEventEnvelope(s,{type:"permission_request",payload:{request_id:i.requestId,tool_call_id:t,tool_name:i.toolName,tool_title:i.toolTitle,options:i.options}})||this.callbacks.sendPermissionCard({eventId:s.eventId,sessionId:s.sessionId,toolCallId:t,toolName:i.toolName,toolTitle:i.toolTitle,options:i.options})):(r.info("acp-adapter",`Permission request without active run, auto-approving: ${i.toolName}`),this.acpClient.respondPermission(e.requestId,{behavior:"allow"}),this.pendingApprovals.delete(t))}emitRawEventEnvelope(e,i){return!e||!this.rawTransport||!this.callbacks.sendRawEventEnvelope?!1:(this.callbacks.sendRawEventEnvelope(e.eventId,e.sessionId,{type:i.type,payload:i.payload,seq:++this.rawEventSeq,at:new Date().toISOString()}),!0)}resetIdleTimer(e){e.idleTimer&&clearTimeout(e.idleTimer);const i=e.awaitingToolResult?z:W;e.idleTimer=setTimeout(()=>{if(this.activeRun?.eventId!==e.eventId)return;const t=this.acpClient;t?.isAlive?t.ping(5e3).then(async s=>{if(this.activeRun?.eventId!==e.eventId)return;if(!s){r.error("acp-adapter",`Ping failed, declaring stuck: ${e.eventId}`),this.finishRun("failed","agent unreachable"),this.declareStuck();return}if(e.awaitingToolResult){r.info("acp-adapter",`Idle timer: tool running, ping ok, continuing: ${e.eventId}`),e.idleNoProgressCount=0,this.resetIdleTimer(e);return}const n=e.lastIdleCheckAt,o=e.lastProgressAt>n,a=o?!1:await B(t.sessionId,n),c=o||a;e.lastIdleCheckAt=Date.now(),c?(a&&r.info("acp-adapter",`Idle timer: no stream progress but kiro session active, continuing: ${e.eventId}`),e.idleNoProgressCount=0,this.resetIdleTimer(e)):(e.idleNoProgressCount++,e.idleNoProgressCount>=2?(r.error("acp-adapter",`Agent alive but no progress for ${e.idleNoProgressCount} idle cycles, declaring stuck: ${e.eventId}`),this.finishRun("failed",`agent alive but no progress for ${e.idleNoProgressCount} idle cycles`),this.declareStuck()):(r.warn("acp-adapter",`Idle timer fired, ping ok, no progress (${e.idleNoProgressCount}/2): ${e.eventId}`),this.resetIdleTimer(e)))}).catch(()=>{this.activeRun?.eventId===e.eventId&&(r.error("acp-adapter",`Ping error, declaring stuck: ${e.eventId}`),this.finishRun("failed","agent unreachable"),this.declareStuck())}):(r.error("acp-adapter",`Agent idle (no client), declaring stuck: ${e.eventId}`),this.finishRun("failed","agent unreachable"),this.declareStuck())},i)}declareStuck(){this.acpClient&&(this.acpClient.clearSettleTimer(),this.acpClient.removeAllListeners(),this.acpClient=null),this.agentProcess&&(this.agentProcess.close().catch(()=>{}),this.agentProcess=null),this.emit("stuck")}appendToStream(e,i){e.buffer+=i,e.flushTimer||(e.flushTimer=setTimeout(()=>this.flushStream(),H))}emitSegmentedStream(e,i){if(!i)return;const t=e.markdownSegmenter.push(i);for(const s of t)s.text&&(this.callbacks.sendStreamChunk(e.eventId,e.sessionId,s.text,++e.chunkSeq,s.closeAfter===!0,e.currentClientMsgId,e.quotedMessageId),s.closeAfter&&(e.currentSegmentIndex+=1,e.currentClientMsgId=`${e.clientMsgIdBase}_seg_${e.currentSegmentIndex}`,e.chunkSeq=0,r.info("acp-adapter",`stream segment rollover event=${e.eventId} segment=${e.currentSegmentIndex} reason=${s.reason??"threshold"}`)))}flushStream(){const e=this.activeRun;if(!e||!e.buffer)return;e.flushTimer&&(clearTimeout(e.flushTimer),e.flushTimer=null);const i=e.buffer;e.buffer="",this.emitSegmentedStream(e,i)}finishRun(e,i){const t=this.activeRun;if(!t)return;this.activeRun=null,this.acpClient?.clearSettleTimer(),this.emit("eventDone",t.eventId),t.flushTimer&&(clearTimeout(t.flushTimer),t.flushTimer=null),t.idleTimer&&(clearTimeout(t.idleTimer),t.idleTimer=null);const s=t.quotedStream.flush();s.deltaContent&&(t.buffer+=s.deltaContent),s.quotedMessageId&&(t.quotedMessageId=s.quotedMessageId),i&&(e==="failed"?(r.error("acp-adapter",`finishRun failed: ${i} event=${t.eventId}`),t.buffer+=`
|
|
22
22
|
|
|
23
|
-
The task was interrupted. Please resend your instruction.`):r.info("acp-adapter",`finishRun ${e}: ${i} event=${t.eventId}`)),t.buffer&&(this.emitSegmentedStream(t,t.buffer),t.buffer="");const n=++t.chunkSeq;if(this.callbacks.sendFinalStreamChunkReliable){let
|
|
23
|
+
The task was interrupted. Please resend your instruction.`):r.info("acp-adapter",`finishRun ${e}: ${i} event=${t.eventId}`)),t.buffer&&(this.emitSegmentedStream(t,t.buffer),t.buffer="");const n=++t.chunkSeq;if(this.callbacks.sendFinalStreamChunkReliable){let a;const c=this.callbacks.sendFinalStreamChunkReliable(t.eventId,t.sessionId,n,t.currentClientMsgId);c.catch(()=>{});const l=new Promise((d,u)=>{a=setTimeout(()=>u(new Error("final chunk ACK timeout")),5e3),a.unref()});Promise.race([c,l]).then(()=>{clearTimeout(a),t.silent||this.callbacks.sendEventResult(t.eventId,e,i),this.persistEventResult(t,e,i),this.replayNextDeferredEvent(),this.tryRunPendingAutoCompact()}).catch(d=>{clearTimeout(a),r.warn("acp-adapter",`finalStreamChunk ACK timeout/failed event=${t.eventId}: ${d instanceof Error?d.message:d}`),this.callbacks.sendStreamChunk(t.eventId,t.sessionId,"",n,!0,t.currentClientMsgId),t.silent||this.callbacks.sendEventResult(t.eventId,e,i),this.persistEventResult(t,e,i),this.replayNextDeferredEvent(),this.tryRunPendingAutoCompact()})}else this.callbacks.sendStreamChunk(t.eventId,t.sessionId,"",n,!0,t.currentClientMsgId),t.silent||this.callbacks.sendEventResult(t.eventId,e,i),this.persistEventResult(t,e,i),this.replayNextDeferredEvent(),this.tryRunPendingAutoCompact()}getContextWindowUsedPercentage(e){return"usedPercentage"in e?Number.isFinite(e.usedPercentage)?e.usedPercentage:null:!Number.isFinite(e.used)||!Number.isFinite(e.size)||e.size<=0?null:Math.min(100,e.used/e.size*100)}maybeScheduleAutoCompact(e){const i=this.getContextWindowUsedPercentage(e);i===null||i<R||this.pendingAutoCompact||this.compacting||(this.pendingAutoCompact=!0,r.info("acp-adapter",`[auto-compact] context_window usedPercentage=${i.toFixed(1)}% >= ${R}%, scheduling compact`),this.tryRunPendingAutoCompact())}tryRunPendingAutoCompact(){this.pendingAutoCompact&&(this.stopped||!this.acpClient?.isAlive||this.activeRun||this.compacting||(this.pendingAutoCompact=!1,this.execCommand("compact","",this.currentAibotSessionId??"").then(e=>{r.info("acp-adapter",`[auto-compact] compact done status=${e.status} msg=${e.message??""}`)}).catch(e=>{r.warn("acp-adapter",`[auto-compact] compact error: ${e instanceof Error?e.message:String(e)}`)})))}persistEventResult(e,i,t){this.eventResults&&!e.silent&&this.eventResults.set({sessionId:e.sessionId,eventId:e.eventId,status:i,msg:t,updatedAt:Date.now()})}}class X extends A{adapterSessionId;constructor(e){super(),this.adapterSessionId=e}emitDone(e){this.emit("done",e)}emitError(e){if(this.listenerCount("error")===0){r.warn("acp-adapter",`Prompt handle error (no listeners): ${e.message}`);return}this.emit("error",e)}async cancel(){}}export{Ie as AcpAdapter};
|
|
@@ -3,4 +3,4 @@ ${e.contextMessages.map(l=>`[${l.senderId??"unknown"}]: ${l.content}`).join(`
|
|
|
3
3
|
`)}
|
|
4
4
|
|
|
5
5
|
Latest user message:
|
|
6
|
-
${s}`),s=this.identity.injectOnce(e.adapterSessionId,s);const a=this.config.command||"codewhale",c=this.buildExecArgs(s),r={...process.env,...this.config.env},u=I(a,typeof r.PATH=="string"?r.PATH:void 0);n.info("codewhale-adapter",`Spawning: ${u} ${c.slice(0,5).join(" ")}...`);try{if(!(await C(this.cwd)).isDirectory())throw new Error(`Bound path is not a directory: ${this.cwd}`)}catch(d){throw String(d?.code??"")==="ENOENT"?new Error(`Bound directory does not exist: ${this.cwd}. Please rebind with /grix open <valid-directory>.`):d}const h=_(u,c,{cwd:this.cwd,env:r}).process;return this.activeProcess=h,h.stderr?.on("data",d=>{const l=d.toString().trim();l&&n.info("codewhale-adapter",`[codewhale stderr] ${l}`)}),new Promise((d,l)=>{let m=!1,f="";const S=()=>{this.activeProcess=null};h.on("error",p=>{m||(m=!0,S(),l(p))}),h.on("exit",p=>{if(f.trim()&&this.handleOutputLine(f.trim(),i),f="",m){S();return}if(m=!0,S(),p!==0&&i&&this.activeEventId===i){l(new Error(`codewhale exec exited with code ${p}`));return}t.emitDone({status:"completed"}),d()}),M({input:h.stdout}).on("line",p=>{p.trim()&&this.handleOutputLine(p.trim(),i)}),h.stdin?.end()})}handleOutputLine(e,t){let i;try{i=JSON.parse(e)}catch{n.error("codewhale-adapter",`Invalid JSON: ${e.slice(0,200)}`);return}switch(i.type){case"content":{const s=i.content;s&&t&&this.activeEventId===t&&this.activeSessionId&&(this.chunkSeq++,this.callbacks.sendStreamChunk(t,this.activeSessionId,s,this.chunkSeq,!1,this.activeClientMsgId??void 0),this.startComposing(),this.resetIdleTimer(t));break}case"session_capture":{const s=i.content;s&&(this.deepSeekSessionId=s,this.persistSessionId(s),n.info("codewhale-adapter",`Session captured: ${s}`));break}case"metadata":{const s=i.meta;if(s){n.info("codewhale-adapter",`Metadata: model=${s.model}, tokens_in=${s.input_tokens}, tokens_out=${s.output_tokens}`);const a=Number(s.input_tokens??0),c=Number(s.output_tokens??0);if(a>0||c>0){const r=this.lastUsage;this.lastUsage={sampledAt:new Date().toISOString(),turns:(r?.turns??0)+1,total:{input:(r?.total.input??0)+a,output:(r?.total.output??0)+c}}}}break}case"tool_use":{const s=i.name,a=typeof i.input=="string"?i.input:JSON.stringify(i.input??{});s&&t&&this.activeEventId===t&&this.activeSessionId&&(n.info("codewhale-adapter",`Tool use: ${s}`),this.callbacks.sendToolUse(t,this.activeSessionId,s,a),this.resetIdleTimer(t));break}case"tool_result":{const s=i.name,a=i.output;t&&this.activeEventId===t&&this.activeSessionId&&(this.callbacks.sendToolResult(t,this.activeSessionId,s??"unknown",a??""),this.resetIdleTimer(t));break}case"done":{this.handleMessageCompleted(t);break}default:break}}handleMessageCompleted(e){if(this.stopComposing(),e&&this.activeEventId===e){const t=this.activeSessionId??"",i=this.activeClientMsgId??void 0;t&&(this.chunkSeq++,this.callbacks.sendStreamChunk(e,t,"",this.chunkSeq,!0,i)),this.callbacks.sendEventResult(e,"responded"),this.clearActive()}}killActiveProcess(){const e=this.activeProcess;this.activeProcess=null,e?.pid&&(k(e,"SIGTERM"),setTimeout(()=>{if(e.exitCode===null&&e.signalCode===null)try{k(e,"SIGKILL")}catch{}},5e3).unref())}notifyBindingReady(){if(!this.aibotSessionId||!this.cwd)return;const e=this.availableModelsMeta(),t=this.currentModel??e[0]?.id??"";this.callbacks.sendUpdateBindingCard(this.aibotSessionId,"ready",this.cwd,{model_id:t,available_models:e})}persistSessionId(e){!this.bindingStore||!this.aibotSessionId||this.bindingStore.setCodeWhaleThreadId(this.aibotSessionId,e)}startComposing(){}stopComposing(){}resetIdleTimer(e){this.clearIdleTimer(),this.idleTimer=setTimeout(()=>{this.activeEventId===e&&(n.error("codewhale-adapter",`Agent idle for ${v/1e3}s: ${e}`),this.killActiveProcess(),this.callbacks.sendEventResult(e,"failed",`agent idle for ${v/1e3}s`),this.clearActive(),this.emit("stuck"))},v)}clearIdleTimer(){this.idleTimer&&(clearTimeout(this.idleTimer),this.idleTimer=null)}clearActive(){const e=this.activeEventId;this.stopComposing(),this.activeEventId=null,this.activeSessionId=null,this.chunkSeq=0,this.activeClientMsgId=null,this.clearIdleTimer(),e&&this.emit("eventDone",e)}}class y extends b{adapterSessionId;constructor(e){super(),this.adapterSessionId=e}emitDone(e){this.emit("done",e)}emitError(e){if(this.listenerCount("error")===0){n.warn("codewhale-adapter",`Prompt handle error (no listeners): ${e.message}`);return}this.emit("error",e)}async cancel(){}}export{se as CodeWhaleAdapter};
|
|
6
|
+
${s}`),s=this.identity.injectOnce(o??e.adapterSessionId,s);const a=this.config.command||"codewhale",c=this.buildExecArgs(s),r={...process.env,...this.config.env},u=I(a,typeof r.PATH=="string"?r.PATH:void 0);n.info("codewhale-adapter",`Spawning: ${u} ${c.slice(0,5).join(" ")}...`);try{if(!(await C(this.cwd)).isDirectory())throw new Error(`Bound path is not a directory: ${this.cwd}`)}catch(d){throw String(d?.code??"")==="ENOENT"?new Error(`Bound directory does not exist: ${this.cwd}. Please rebind with /grix open <valid-directory>.`):d}const h=_(u,c,{cwd:this.cwd,env:r}).process;return this.activeProcess=h,h.stderr?.on("data",d=>{const l=d.toString().trim();l&&n.info("codewhale-adapter",`[codewhale stderr] ${l}`)}),new Promise((d,l)=>{let m=!1,f="";const S=()=>{this.activeProcess=null};h.on("error",p=>{m||(m=!0,S(),l(p))}),h.on("exit",p=>{if(f.trim()&&this.handleOutputLine(f.trim(),i),f="",m){S();return}if(m=!0,S(),p!==0&&i&&this.activeEventId===i){l(new Error(`codewhale exec exited with code ${p}`));return}t.emitDone({status:"completed"}),d()}),M({input:h.stdout}).on("line",p=>{p.trim()&&this.handleOutputLine(p.trim(),i)}),h.stdin?.end()})}handleOutputLine(e,t){let i;try{i=JSON.parse(e)}catch{n.error("codewhale-adapter",`Invalid JSON: ${e.slice(0,200)}`);return}switch(i.type){case"content":{const s=i.content;s&&t&&this.activeEventId===t&&this.activeSessionId&&(this.chunkSeq++,this.callbacks.sendStreamChunk(t,this.activeSessionId,s,this.chunkSeq,!1,this.activeClientMsgId??void 0),this.startComposing(),this.resetIdleTimer(t));break}case"session_capture":{const s=i.content;s&&(this.deepSeekSessionId=s,this.persistSessionId(s),n.info("codewhale-adapter",`Session captured: ${s}`));break}case"metadata":{const s=i.meta;if(s){n.info("codewhale-adapter",`Metadata: model=${s.model}, tokens_in=${s.input_tokens}, tokens_out=${s.output_tokens}`);const a=Number(s.input_tokens??0),c=Number(s.output_tokens??0);if(a>0||c>0){const r=this.lastUsage;this.lastUsage={sampledAt:new Date().toISOString(),turns:(r?.turns??0)+1,total:{input:(r?.total.input??0)+a,output:(r?.total.output??0)+c}}}}break}case"tool_use":{const s=i.name,a=typeof i.input=="string"?i.input:JSON.stringify(i.input??{});s&&t&&this.activeEventId===t&&this.activeSessionId&&(n.info("codewhale-adapter",`Tool use: ${s}`),this.callbacks.sendToolUse(t,this.activeSessionId,s,a),this.resetIdleTimer(t));break}case"tool_result":{const s=i.name,a=i.output;t&&this.activeEventId===t&&this.activeSessionId&&(this.callbacks.sendToolResult(t,this.activeSessionId,s??"unknown",a??""),this.resetIdleTimer(t));break}case"done":{this.handleMessageCompleted(t);break}default:break}}handleMessageCompleted(e){if(this.stopComposing(),e&&this.activeEventId===e){const t=this.activeSessionId??"",i=this.activeClientMsgId??void 0;t&&(this.chunkSeq++,this.callbacks.sendStreamChunk(e,t,"",this.chunkSeq,!0,i)),this.callbacks.sendEventResult(e,"responded"),this.clearActive()}}killActiveProcess(){const e=this.activeProcess;this.activeProcess=null,e?.pid&&(k(e,"SIGTERM"),setTimeout(()=>{if(e.exitCode===null&&e.signalCode===null)try{k(e,"SIGKILL")}catch{}},5e3).unref())}notifyBindingReady(){if(!this.aibotSessionId||!this.cwd)return;const e=this.availableModelsMeta(),t=this.currentModel??e[0]?.id??"";this.callbacks.sendUpdateBindingCard(this.aibotSessionId,"ready",this.cwd,{model_id:t,available_models:e})}persistSessionId(e){!this.bindingStore||!this.aibotSessionId||this.bindingStore.setCodeWhaleThreadId(this.aibotSessionId,e)}startComposing(){}stopComposing(){}resetIdleTimer(e){this.clearIdleTimer(),this.idleTimer=setTimeout(()=>{this.activeEventId===e&&(n.error("codewhale-adapter",`Agent idle for ${v/1e3}s: ${e}`),this.killActiveProcess(),this.callbacks.sendEventResult(e,"failed",`agent idle for ${v/1e3}s`),this.clearActive(),this.emit("stuck"))},v)}clearIdleTimer(){this.idleTimer&&(clearTimeout(this.idleTimer),this.idleTimer=null)}clearActive(){const e=this.activeEventId;this.stopComposing(),this.activeEventId=null,this.activeSessionId=null,this.chunkSeq=0,this.activeClientMsgId=null,this.clearIdleTimer(),e&&this.emit("eventDone",e)}}class y extends b{adapterSessionId;constructor(e){super(),this.adapterSessionId=e}emitDone(e){this.emit("done",e)}emitError(e){if(this.listenerCount("error")===0){n.warn("codewhale-adapter",`Prompt handle error (no listeners): ${e.message}`);return}this.emit("error",e)}async cancel(){}}export{se as CodeWhaleAdapter};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createServer as u}from"node:http";import{log as c}from"../log/logger.js";class f{server=null;token;handler=null;upgradeHandler=null;probeHandler=null;installHandler=null;proxyHandler=null;constructor(e){this.token=e}setAgentHandler(e){this.handler=e}setUpgradeHandler(e){this.upgradeHandler=e}setProbeHandler(e){this.probeHandler=e}setInstallHandler(e){this.installHandler=e}setProxyHandler(e){this.proxyHandler=e}async start(e){return new Promise((n,t)=>{this.server=u((r,s)=>this.handleRequest(r,s)),this.server.listen(e,"127.0.0.1",()=>{c.info("admin",`Listening on 127.0.0.1:${e}`),n()}),this.server.on("error",t)})}async stop(){if(this.server)return new Promise(e=>{this.server.close(()=>e())})}handleRequest(e,n){const t=e.method??"",r=e.url??"";if(r==="/api/agents"&&t==="GET")this.handleList(n);else if(r==="/api/agents"&&t==="POST")this.readBody(e).then(s=>this.handleAdd(n,s)).catch(s=>this.error(n,s));else if(t==="DELETE"&&r.startsWith("/api/agents/")){const s=decodeURIComponent(r.slice(12));this.handleRemove(n,s)}else if(t==="POST"&&r.match(/^\/api\/agents\/[^/]+\/restart$/)){const s=decodeURIComponent(r.slice(12,r.lastIndexOf("/restart")));this.handleRestart(n,s)}else if(r==="/api/reload"&&t==="POST")this.handleReload(n);else if(r==="/api/upgrade"&&t==="GET")this.handleCheckUpgrade(n);else if(r==="/api/upgrade"&&t==="POST")this.handleTriggerUpgrade(n);else if(t==="GET"&&r.startsWith("/api/probe"))this.handleProbe(e,n,r);else if(r==="/api/install"&&t==="GET")this.handleInstallList(n);else if(r==="/api/install"&&t==="POST")this.readBody(e).then(s=>this.handleInstall(n,s)).catch(s=>this.error(n,s));else if(t==="GET"&&r.startsWith("/api/install/")){const s=decodeURIComponent(r.slice(13));this.handleInstallProgress(n,s)}else if(r==="/api/proxy"&&t==="GET")this.handleProxyStatus(n);else if(t==="PUT"&&r.match(/^\/api\/proxy\/agents\/[^/]+\/enabled$/)){const s=decodeURIComponent(r.slice(18,-8));this.readBody(e).then(o=>this.handleProxySetAgentRelay(n,s,o)).catch(o=>this.error(n,o))}else if(t==="PUT"&&r.match(/^\/api\/proxy\/agents\/[^/]+\/relay-credential$/)){const s=decodeURIComponent(r.slice(18,-17));this.readBody(e).then(o=>this.handleProxySetAgentRelayCredential(n,s,o)).catch(o=>this.error(n,o))}else if(t==="PUT"&&r==="/api/proxy/enabled")this.readBody(e).then(s=>this.handleProxySetEnabled(n,s)).catch(s=>this.error(n,s));else if(t==="PUT"&&r==="/api/proxy/default-route")this.readBody(e).then(s=>this.handleProxySetDefaultRoute(n,s)).catch(s=>this.error(n,s));else if(t==="PUT"&&r==="/api/proxy/intercept-hosts")this.readBody(e).then(s=>this.handleProxySetInterceptHosts(n,s)).catch(s=>this.error(n,s));else if(t==="PUT"&&r.match(/^\/api\/proxy\/routes\/[^/]+$/)){const s=decodeURIComponent(r.slice(18));this.readBody(e).then(o=>this.handleProxySetRoute(n,s,o)).catch(o=>this.error(n,o))}else if(t==="DELETE"&&r.startsWith("/api/proxy/routes/")){const s=decodeURIComponent(r.slice(18));this.handleProxyDeleteRoute(n,s)}else this.json(n,404,{error:"not_found"})}ensureProxy(e){return this.proxyHandler?!0:(this.json(e,501,{error:"proxy not configured"}),!1)}handleProxyStatus(e){if(this.ensureProxy(e))try{this.json(e,200,this.proxyHandler.status())}catch(n){this.error(e,n)}}async handleProxySetRoute(e,n,t){if(!this.ensureProxy(e))return;const r=t;if(!r||typeof r.targetBaseUrl!="string"||!r.targetBaseUrl.trim()){this.json(e,400,{error:"targetBaseUrl is required"});return}try{new URL(r.targetBaseUrl)}catch{this.json(e,400,{error:"targetBaseUrl must be a valid URL"});return}if(r.headers!==void 0&&!this.isStringRecord(r.headers)){this.json(e,400,{error:"headers must be a string map"});return}if(r.model!==void 0&&typeof r.model!="string"){this.json(e,400,{error:"model must be a string"});return}if(r.modelMap!==void 0&&!this.isStringRecord(r.modelMap)){this.json(e,400,{error:"modelMap must be a string map"});return}if(r.passthrough!==void 0&&typeof r.passthrough!="boolean"){this.json(e,400,{error:"passthrough must be a boolean"});return}if(r.relayOnly!==void 0&&typeof r.relayOnly!="boolean"){this.json(e,400,{error:"relayOnly must be a boolean"});return}if(r.codexResponsesToChat!==void 0&&typeof r.codexResponsesToChat!="boolean"){this.json(e,400,{error:"codexResponsesToChat must be a boolean"});return}if(r.passthrough===!0&&r.codexResponsesToChat===!0){this.json(e,400,{error:"passthrough and codexResponsesToChat are mutually exclusive"});return}try{const s=await this.proxyHandler.setRoute(n,t);this.json(e,200,s??{ok:!0})}catch(s){this.error(e,s)}}handleProxyDeleteRoute(e,n){this.ensureProxy(e)&&this.proxyHandler.deleteRoute(n).then(()=>{e.writeHead(204),e.end()}).catch(t=>this.error(e,t))}async handleProxySetAgentRelay(e,n,t){if(!this.ensureProxy(e))return;const r=n.trim();if(!r){this.json(e,400,{error:"agent name is required"});return}const s=t;if(typeof s?.enabled!="boolean"){this.json(e,400,{error:"enabled must be a boolean"});return}try{const o=await this.proxyHandler.setAgentRelay(r,s.enabled);this.json(e,200,o??{ok:!0})}catch(o){this.error(e,o)}}async handleProxySetAgentRelayCredential(e,n,t){if(!this.ensureProxy(e))return;const r=n.trim();if(!r){this.json(e,400,{error:"agent name is required",code:"INVALID_ARGUMENT"});return}const s=t;if(typeof s?.agent_id!="string"||!s.agent_id.trim()){this.json(e,400,{error:"agent_id is required",code:"MISSING_AGENT_ID"});return}if(typeof s?.virtual_key!="string"||!s.virtual_key.trim()){this.json(e,400,{error:"virtual_key is required",code:"MISSING_VIRTUAL_KEY"});return}if(s.anthropic_base_url!==void 0&&typeof s.anthropic_base_url!="string"){this.json(e,400,{error:"anthropic_base_url must be a string",code:"INVALID_ARGUMENT"});return}if(s.openai_base_url!==void 0&&typeof s.openai_base_url!="string"){this.json(e,400,{error:"openai_base_url must be a string",code:"INVALID_ARGUMENT"});return}if(s.model!==void 0&&typeof s.model!="string"){this.json(e,400,{error:"model must be a string",code:"INVALID_ARGUMENT"});return}try{const o=await this.proxyHandler.setAgentRelayCredential(r,{agentId:s.agent_id,virtualKey:s.virtual_key,anthropicBaseUrl:typeof s.anthropic_base_url=="string"?s.anthropic_base_url:void 0,openaiBaseUrl:typeof s.openai_base_url=="string"?s.openai_base_url:void 0,model:typeof s.model=="string"?s.model:void 0});this.json(e,200,o??{ok:!0})}catch(o){this.error(e,o)}}async handleProxySetEnabled(e,n){if(!this.ensureProxy(e))return;const t=n;if(typeof t?.enabled!="boolean"){this.json(e,400,{error:"enabled must be a boolean"});return}if(t.enabled){this.json(e,400,{error:'relay is per-agent; use PUT /api/proxy/agents/<name>/enabled {"enabled":true}'});return}try{const r=await this.proxyHandler.disableAll();this.json(e,200,r??{ok:!0})}catch(r){this.error(e,r)}}async handleProxySetDefaultRoute(e,n){if(!this.ensureProxy(e))return;const r=n?.routeKey??null;if(r!==null&&typeof r!="string"){this.json(e,400,{error:"routeKey must be a string or null"});return}try{const s=await this.proxyHandler.setDefaultRoute(r);this.json(e,200,s??{ok:!0})}catch(s){this.error(e,s)}}async handleProxySetInterceptHosts(e,n){if(!this.ensureProxy(e))return;const t=n;if(!Array.isArray(t?.hosts)||!t.hosts.every(r=>typeof r=="string")){this.json(e,400,{error:"hosts must be a string array"});return}try{const r=await this.proxyHandler.setInterceptHosts(t.hosts);this.json(e,200,r??{ok:!0})}catch(r){this.error(e,r)}}isStringRecord(e){return typeof e!="object"||e===null||Array.isArray(e)?!1:Object.values(e).every(n=>typeof n=="string")}handleList(e){try{const n=this.handler?.list()??[];this.json(e,200,n)}catch(n){this.error(e,n)}}async handleAdd(e,n){try{const t=await this.handler.add(n);this.json(e,201,t??{ok:!0})}catch(t){this.error(e,t)}}handleRemove(e,n){this.handler.remove(n).then(()=>{e.writeHead(204),e.end()}).catch(t=>this.error(e,t))}handleRestart(e,n){this.handler.restart(n).then(()=>{this.json(e,200,{ok:!0})}).catch(t=>this.error(e,t))}handleReload(e){this.handler.reload().then(n=>{this.json(e,200,{ok:!0,result:n})}).catch(n=>this.error(e,n))}handleCheckUpgrade(e){if(!this.upgradeHandler){this.json(e,501,{error:"upgrade not configured"});return}this.upgradeHandler.check().then(n=>{this.json(e,200,n)}).catch(n=>this.error(e,n))}handleTriggerUpgrade(e){if(!this.upgradeHandler){this.json(e,501,{error:"upgrade not configured"});return}this.upgradeHandler.trigger(),this.json(e,200,{ok:!0,message:"upgrade check triggered"})}error(e,n){const t=n;t.code==="NOT_FOUND"?this.json(e,404,{error:t.message??"not found"}):t.code==="RELOAD_UNSAFE"||t.code==="NO_RELAY_ROUTE"||t.code==="AGENT_ID_MISMATCH"?this.json(e,409,{error:t.message,code:t.code}):t.code==="UNKNOWN_AGENT"||t.code==="UNSUPPORTED_OS"||t.code==="UNSUPPORTED_CLIENT_TYPE"||t.code==="MISSING_AGENT_ID"||t.code==="MISSING_VIRTUAL_KEY"||t.code==="MISSING_TARGET_BASE_URL"||t.code==="MISSING_MODEL"||t.code==="INVALID_TARGET_BASE_URL"||t.code==="INVALID_ARGUMENT"?this.json(e,400,{error:t.message,code:t.code}):t.code==="ALREADY_INSTALLED"||t.code==="INSTALL_IN_PROGRESS"?this.json(e,409,{error:t.message,code:t.code}):t.code==="INSTALL_FAILED"||t.code==="INSTALL_TIMEOUT"||t.code==="PREFLIGHT_FAILED"||t.code==="VERIFICATION_FAILED"||t.code==="PREREQ_MISSING"||t.code==="PREREQ_INSTALL_FAILED"||t.code==="FALLBACK_EXHAUSTED"||t.code==="ENVIRONMENT_UNSUPPORTED"?this.json(e,500,{error:t.message,code:t.code}):(c.error("admin",`Handler error: ${t.message??n}`),this.json(e,500,{error:t.message??"internal error"}))}json(e,n,t){const r=JSON.stringify(t);e.writeHead(n,{"Content-Type":"application/json"}),e.end(r)}readBody(e){return new Promise((n,t)=>{let r="";e.setEncoding("utf8"),e.on("data",s=>{r+=s}),e.on("end",()=>{try{n(JSON.parse(r))}catch{t(new Error("invalid JSON body"))}}),e.on("error",t)})}handleProbe(e,n,t){if(!this.probeHandler){this.json(n,501,{error:"probe not configured"});return}const r=t.indexOf("?"),s=r>=0?t.slice(0,r):t,o=r>=0?new URLSearchParams(t.slice(r+1)):new URLSearchParams,a={};o.get("conversation")==="true"&&(a.conversation=!0),o.get("fresh")==="true"&&(a.fresh=!0);const l=Number(o.get("timeoutMs"));Number.isFinite(l)&&l>0&&(a.timeoutMs=l);const d=s.match(/^\/api\/probe\/(.+)$/);if(d){const i=decodeURIComponent(d[1]);this.probeHandler.probeOne(i,a).then(h=>{this.json(n,200,h)}).catch(h=>this.error(n,h));return}this.probeHandler.probeAll(a).then(i=>{this.json(n,200,i)}).catch(i=>this.error(n,i))}handleInstallList(e){if(!this.installHandler){this.json(e,501,{error:"install not configured"});return}try{const n=this.installHandler.listInstallable();this.json(e,200,n)}catch(n){this.error(e,n)}}handleInstallProgress(e,n){if(!this.installHandler){this.json(e,501,{error:"install not configured"});return}const t=this.installHandler.getInstallProgress(n);if(!t){this.json(e,200,{agentType:n,status:"unknown",inProgress:!1,progress:null});return}let r,s,o;switch(t.phase){case"completed":r="done",s="\u5B89\u88C5\u5B8C\u6210";break;case"failed":r="error",o=t.outputTail||"\u5B89\u88C5\u5931\u8D25";break;case"preflight":r="pending",s="\u68C0\u67E5\u524D\u7F6E\u4F9D\u8D56...";break;case"installing_prereq":r="downloading",s=t.currentPrereq?`\u6B63\u5728\u5B89\u88C5\u524D\u7F6E\u4F9D\u8D56: ${t.currentPrereq}`:"\u6B63\u5728\u5B89\u88C5\u524D\u7F6E\u4F9D\u8D56...";break;case"installing":r="installing",s=`\u6B63\u5728\u5B89\u88C5 ${n}...`;break;case"verifying":r="installing",s="\u9A8C\u8BC1\u5B89\u88C5...";break;default:r="unknown"}this.json(e,200,{agentType:n,status:r,inProgress:!0,progress:t.elapsedMs?Math.min(.9,t.elapsedMs/3e4):.1,message:s,error:o})}async handleInstall(e,n){if(!this.installHandler){this.json(e,501,{error:"install not configured"});return}try{const t=n;if(!t||typeof t.agentType!="string"||!t.agentType){this.json(e,400,{error:"agentType is required"});return}const r=await this.installHandler.installAgent(t);if(r.ok)this.json(e,200,r);else{const s=r.error?.code;s==="UNKNOWN_AGENT"||s==="UNSUPPORTED_OS"?this.json(e,400,r):s==="INSTALL_IN_PROGRESS"?this.json(e,409,r):this.json(e,500,r)}}catch(t){this.error(e,t)}}}export{f as AdminServer};
|
|
1
|
+
import{createServer as u}from"node:http";import{log as c}from"../log/logger.js";class g{server=null;token;handler=null;upgradeHandler=null;probeHandler=null;installHandler=null;proxyHandler=null;relayCredentialHandler=null;constructor(e){this.token=e}setAgentHandler(e){this.handler=e}setUpgradeHandler(e){this.upgradeHandler=e}setProbeHandler(e){this.probeHandler=e}setInstallHandler(e){this.installHandler=e}setProxyHandler(e){this.proxyHandler=e}setRelayCredentialHandler(e){this.relayCredentialHandler=e}async start(e){return new Promise((n,t)=>{this.server=u((r,o)=>this.handleRequest(r,o)),this.server.listen(e,"127.0.0.1",()=>{c.info("admin",`Listening on 127.0.0.1:${e}`),n()}),this.server.on("error",t)})}async stop(){if(this.server)return new Promise(e=>{this.server.close(()=>e())})}handleRequest(e,n){const t=e.method??"",r=e.url??"";if(r==="/api/agents"&&t==="GET")this.handleList(n);else if(r==="/api/agents"&&t==="POST")this.readBody(e).then(o=>this.handleAdd(n,o)).catch(o=>this.error(n,o));else if(t==="DELETE"&&r.startsWith("/api/agents/")){const o=decodeURIComponent(r.slice(12));this.handleRemove(n,o)}else if(t==="POST"&&r.match(/^\/api\/agents\/[^/]+\/restart$/)){const o=decodeURIComponent(r.slice(12,r.lastIndexOf("/restart")));this.handleRestart(n,o)}else if(r==="/api/reload"&&t==="POST")this.handleReload(n);else if(r==="/api/upgrade"&&t==="GET")this.handleCheckUpgrade(n);else if(r==="/api/upgrade"&&t==="POST")this.handleTriggerUpgrade(n);else if(t==="GET"&&r.startsWith("/api/probe"))this.handleProbe(e,n,r);else if(r==="/api/install"&&t==="GET")this.handleInstallList(n);else if(r==="/api/install"&&t==="POST")this.readBody(e).then(o=>this.handleInstall(n,o)).catch(o=>this.error(n,o));else if(t==="GET"&&r.startsWith("/api/install/")){const o=decodeURIComponent(r.slice(13));this.handleInstallProgress(n,o)}else if(r==="/api/proxy"&&t==="GET")this.handleProxyStatus(n);else if(t==="PUT"&&r.match(/^\/api\/proxy\/agents\/[^/]+\/enabled$/)){const o=decodeURIComponent(r.slice(18,-8));this.readBody(e).then(s=>this.handleProxySetAgentRelay(n,o,s)).catch(s=>this.error(n,s))}else if(t==="PUT"&&r.match(/^\/api\/proxy\/agents\/[^/]+\/relay-credential$/)){const o=decodeURIComponent(r.slice(18,-17));this.readBody(e).then(s=>this.handleProxySetAgentRelayCredential(n,o,s)).catch(s=>this.error(n,s))}else if(r==="/api/proxy/hermes-profiles"&&t==="GET")this.handleHermesProfileList(n);else if(t==="PUT"&&r.match(/^\/api\/proxy\/relay-credential\/[^/]+$/)){const o=decodeURIComponent(r.slice(28));this.readBody(e).then(s=>this.handleRelayCredentialEnable(n,o,s)).catch(s=>this.error(n,s))}else if(t==="DELETE"&&r.match(/^\/api\/proxy\/relay-credential\/[^/]+$/)){const o=decodeURIComponent(r.slice(28));this.handleRelayCredentialDisable(n,o)}else if(t==="PUT"&&r==="/api/proxy/enabled")this.readBody(e).then(o=>this.handleProxySetEnabled(n,o)).catch(o=>this.error(n,o));else if(t==="PUT"&&r==="/api/proxy/default-route")this.readBody(e).then(o=>this.handleProxySetDefaultRoute(n,o)).catch(o=>this.error(n,o));else if(t==="PUT"&&r==="/api/proxy/intercept-hosts")this.readBody(e).then(o=>this.handleProxySetInterceptHosts(n,o)).catch(o=>this.error(n,o));else if(t==="PUT"&&r.match(/^\/api\/proxy\/routes\/[^/]+$/)){const o=decodeURIComponent(r.slice(18));this.readBody(e).then(s=>this.handleProxySetRoute(n,o,s)).catch(s=>this.error(n,s))}else if(t==="DELETE"&&r.startsWith("/api/proxy/routes/")){const o=decodeURIComponent(r.slice(18));this.handleProxyDeleteRoute(n,o)}else this.json(n,404,{error:"not_found"})}ensureRelayCredential(e){return this.relayCredentialHandler?!0:(this.json(e,501,{error:"relay credential handler not configured"}),!1)}handleHermesProfileList(e){this.ensureRelayCredential(e)&&this.relayCredentialHandler.listHermesProfiles().then(n=>this.json(e,200,{profiles:n})).catch(n=>this.error(e,n))}async handleRelayCredentialEnable(e,n,t){if(!this.ensureRelayCredential(e))return;const r=t,o=s=>typeof s=="string"&&s.trim()?s.trim():void 0;try{const s=await this.relayCredentialHandler.enable(n.trim(),{virtualKey:o(r?.virtual_key)??"",anthropicBaseUrl:o(r?.anthropic_base_url),openaiBaseUrl:o(r?.openai_base_url),model:o(r?.model)});this.json(e,200,s??{ok:!0})}catch(s){this.error(e,s)}}handleRelayCredentialDisable(e,n){this.ensureRelayCredential(e)&&this.relayCredentialHandler.disable(n.trim()).then(t=>this.json(e,200,t??{ok:!0})).catch(t=>this.error(e,t))}ensureProxy(e){return this.proxyHandler?!0:(this.json(e,501,{error:"proxy not configured"}),!1)}handleProxyStatus(e){if(this.ensureProxy(e))try{this.json(e,200,this.proxyHandler.status())}catch(n){this.error(e,n)}}async handleProxySetRoute(e,n,t){if(!this.ensureProxy(e))return;const r=t;if(!r||typeof r.targetBaseUrl!="string"||!r.targetBaseUrl.trim()){this.json(e,400,{error:"targetBaseUrl is required"});return}try{new URL(r.targetBaseUrl)}catch{this.json(e,400,{error:"targetBaseUrl must be a valid URL"});return}if(r.headers!==void 0&&!this.isStringRecord(r.headers)){this.json(e,400,{error:"headers must be a string map"});return}if(r.model!==void 0&&typeof r.model!="string"){this.json(e,400,{error:"model must be a string"});return}if(r.modelMap!==void 0&&!this.isStringRecord(r.modelMap)){this.json(e,400,{error:"modelMap must be a string map"});return}if(r.passthrough!==void 0&&typeof r.passthrough!="boolean"){this.json(e,400,{error:"passthrough must be a boolean"});return}if(r.relayOnly!==void 0&&typeof r.relayOnly!="boolean"){this.json(e,400,{error:"relayOnly must be a boolean"});return}if(r.codexResponsesToChat!==void 0&&typeof r.codexResponsesToChat!="boolean"){this.json(e,400,{error:"codexResponsesToChat must be a boolean"});return}if(r.passthrough===!0&&r.codexResponsesToChat===!0){this.json(e,400,{error:"passthrough and codexResponsesToChat are mutually exclusive"});return}try{const o=await this.proxyHandler.setRoute(n,t);this.json(e,200,o??{ok:!0})}catch(o){this.error(e,o)}}handleProxyDeleteRoute(e,n){this.ensureProxy(e)&&this.proxyHandler.deleteRoute(n).then(()=>{e.writeHead(204),e.end()}).catch(t=>this.error(e,t))}async handleProxySetAgentRelay(e,n,t){if(!this.ensureProxy(e))return;const r=n.trim();if(!r){this.json(e,400,{error:"agent name is required"});return}const o=t;if(typeof o?.enabled!="boolean"){this.json(e,400,{error:"enabled must be a boolean"});return}try{const s=await this.proxyHandler.setAgentRelay(r,o.enabled);this.json(e,200,s??{ok:!0})}catch(s){this.error(e,s)}}async handleProxySetAgentRelayCredential(e,n,t){if(!this.ensureProxy(e))return;const r=n.trim();if(!r){this.json(e,400,{error:"agent name is required",code:"INVALID_ARGUMENT"});return}const o=t;if(typeof o?.agent_id!="string"||!o.agent_id.trim()){this.json(e,400,{error:"agent_id is required",code:"MISSING_AGENT_ID"});return}if(typeof o?.virtual_key!="string"||!o.virtual_key.trim()){this.json(e,400,{error:"virtual_key is required",code:"MISSING_VIRTUAL_KEY"});return}if(o.anthropic_base_url!==void 0&&typeof o.anthropic_base_url!="string"){this.json(e,400,{error:"anthropic_base_url must be a string",code:"INVALID_ARGUMENT"});return}if(o.openai_base_url!==void 0&&typeof o.openai_base_url!="string"){this.json(e,400,{error:"openai_base_url must be a string",code:"INVALID_ARGUMENT"});return}if(o.model!==void 0&&typeof o.model!="string"){this.json(e,400,{error:"model must be a string",code:"INVALID_ARGUMENT"});return}try{const s=await this.proxyHandler.setAgentRelayCredential(r,{agentId:o.agent_id,virtualKey:o.virtual_key,anthropicBaseUrl:typeof o.anthropic_base_url=="string"?o.anthropic_base_url:void 0,openaiBaseUrl:typeof o.openai_base_url=="string"?o.openai_base_url:void 0,model:typeof o.model=="string"?o.model:void 0});this.json(e,200,s??{ok:!0})}catch(s){this.error(e,s)}}async handleProxySetEnabled(e,n){if(!this.ensureProxy(e))return;const t=n;if(typeof t?.enabled!="boolean"){this.json(e,400,{error:"enabled must be a boolean"});return}if(t.enabled){this.json(e,400,{error:'relay is per-agent; use PUT /api/proxy/agents/<name>/enabled {"enabled":true}'});return}try{const r=await this.proxyHandler.disableAll();this.json(e,200,r??{ok:!0})}catch(r){this.error(e,r)}}async handleProxySetDefaultRoute(e,n){if(!this.ensureProxy(e))return;const r=n?.routeKey??null;if(r!==null&&typeof r!="string"){this.json(e,400,{error:"routeKey must be a string or null"});return}try{const o=await this.proxyHandler.setDefaultRoute(r);this.json(e,200,o??{ok:!0})}catch(o){this.error(e,o)}}async handleProxySetInterceptHosts(e,n){if(!this.ensureProxy(e))return;const t=n;if(!Array.isArray(t?.hosts)||!t.hosts.every(r=>typeof r=="string")){this.json(e,400,{error:"hosts must be a string array"});return}try{const r=await this.proxyHandler.setInterceptHosts(t.hosts);this.json(e,200,r??{ok:!0})}catch(r){this.error(e,r)}}isStringRecord(e){return typeof e!="object"||e===null||Array.isArray(e)?!1:Object.values(e).every(n=>typeof n=="string")}handleList(e){try{const n=this.handler?.list()??[];this.json(e,200,n)}catch(n){this.error(e,n)}}async handleAdd(e,n){try{const t=await this.handler.add(n);this.json(e,201,t??{ok:!0})}catch(t){this.error(e,t)}}handleRemove(e,n){this.handler.remove(n).then(()=>{e.writeHead(204),e.end()}).catch(t=>this.error(e,t))}handleRestart(e,n){this.handler.restart(n).then(()=>{this.json(e,200,{ok:!0})}).catch(t=>this.error(e,t))}handleReload(e){this.handler.reload().then(n=>{this.json(e,200,{ok:!0,result:n})}).catch(n=>this.error(e,n))}handleCheckUpgrade(e){if(!this.upgradeHandler){this.json(e,501,{error:"upgrade not configured"});return}this.upgradeHandler.check().then(n=>{this.json(e,200,n)}).catch(n=>this.error(e,n))}handleTriggerUpgrade(e){if(!this.upgradeHandler){this.json(e,501,{error:"upgrade not configured"});return}this.upgradeHandler.trigger(),this.json(e,200,{ok:!0,message:"upgrade check triggered"})}error(e,n){const t=n;t.code==="NOT_FOUND"?this.json(e,404,{error:t.message??"not found"}):t.code==="PROFILE_NOT_FOUND"?this.json(e,404,{error:t.message,code:t.code}):t.code==="PROXY_UNAVAILABLE"?this.json(e,503,{error:t.message,code:t.code}):t.code==="AMBIGUOUS_AGENT_ID"?this.json(e,409,{error:t.message,code:t.code}):t.code==="HERMES_NOT_FOUND"||t.code==="HERMES_IMPORT_FAILED"||t.code==="SCRIPT_MISSING"||t.code==="SCRIPT_FAILED"||t.code==="CONFIG_WRITE_FAILED"||t.code==="CONFIG_WRITE_VERIFY_FAILED"||t.code==="CONFIG_UNREADABLE"?this.json(e,500,{error:t.message,code:t.code}):t.code==="RELOAD_UNSAFE"||t.code==="NO_RELAY_ROUTE"||t.code==="AGENT_ID_MISMATCH"?this.json(e,409,{error:t.message,code:t.code}):t.code==="UNKNOWN_AGENT"||t.code==="UNSUPPORTED_OS"||t.code==="UNSUPPORTED_CLIENT_TYPE"||t.code==="MISSING_AGENT_ID"||t.code==="MISSING_VIRTUAL_KEY"||t.code==="MISSING_TARGET_BASE_URL"||t.code==="MISSING_MODEL"||t.code==="MISSING_BASE_URL"||t.code==="MISSING_API_KEY"||t.code==="INVALID_BASE_URL"||t.code==="BAD_REQUEST"||t.code==="INVALID_TARGET_BASE_URL"||t.code==="INVALID_ARGUMENT"?this.json(e,400,{error:t.message,code:t.code}):t.code==="ALREADY_INSTALLED"||t.code==="INSTALL_IN_PROGRESS"?this.json(e,409,{error:t.message,code:t.code}):t.code==="INSTALL_FAILED"||t.code==="INSTALL_TIMEOUT"||t.code==="PREFLIGHT_FAILED"||t.code==="VERIFICATION_FAILED"||t.code==="PREREQ_MISSING"||t.code==="PREREQ_INSTALL_FAILED"||t.code==="FALLBACK_EXHAUSTED"||t.code==="ENVIRONMENT_UNSUPPORTED"?this.json(e,500,{error:t.message,code:t.code}):(c.error("admin",`Handler error: ${t.message??n}`),this.json(e,500,{error:t.message??"internal error"}))}json(e,n,t){const r=JSON.stringify(t);e.writeHead(n,{"Content-Type":"application/json"}),e.end(r)}readBody(e){return new Promise((n,t)=>{let r="";e.setEncoding("utf8"),e.on("data",o=>{r+=o}),e.on("end",()=>{try{n(JSON.parse(r))}catch{t(new Error("invalid JSON body"))}}),e.on("error",t)})}handleProbe(e,n,t){if(!this.probeHandler){this.json(n,501,{error:"probe not configured"});return}const r=t.indexOf("?"),o=r>=0?t.slice(0,r):t,s=r>=0?new URLSearchParams(t.slice(r+1)):new URLSearchParams,a={};s.get("conversation")==="true"&&(a.conversation=!0),s.get("fresh")==="true"&&(a.fresh=!0);const l=Number(s.get("timeoutMs"));Number.isFinite(l)&&l>0&&(a.timeoutMs=l);const h=o.match(/^\/api\/probe\/(.+)$/);if(h){const i=decodeURIComponent(h[1]);this.probeHandler.probeOne(i,a).then(d=>{this.json(n,200,d)}).catch(d=>this.error(n,d));return}this.probeHandler.probeAll(a).then(i=>{this.json(n,200,i)}).catch(i=>this.error(n,i))}handleInstallList(e){if(!this.installHandler){this.json(e,501,{error:"install not configured"});return}try{const n=this.installHandler.listInstallable();this.json(e,200,n)}catch(n){this.error(e,n)}}handleInstallProgress(e,n){if(!this.installHandler){this.json(e,501,{error:"install not configured"});return}const t=this.installHandler.getInstallProgress(n);if(!t){this.json(e,200,{agentType:n,status:"unknown",inProgress:!1,progress:null});return}let r,o,s;switch(t.phase){case"completed":r="done",o="\u5B89\u88C5\u5B8C\u6210";break;case"failed":r="error",s=t.outputTail||"\u5B89\u88C5\u5931\u8D25";break;case"preflight":r="pending",o="\u68C0\u67E5\u524D\u7F6E\u4F9D\u8D56...";break;case"installing_prereq":r="downloading",o=t.currentPrereq?`\u6B63\u5728\u5B89\u88C5\u524D\u7F6E\u4F9D\u8D56: ${t.currentPrereq}`:"\u6B63\u5728\u5B89\u88C5\u524D\u7F6E\u4F9D\u8D56...";break;case"installing":r="installing",o=`\u6B63\u5728\u5B89\u88C5 ${n}...`;break;case"verifying":r="installing",o="\u9A8C\u8BC1\u5B89\u88C5...";break;default:r="unknown"}this.json(e,200,{agentType:n,status:r,inProgress:!0,progress:t.elapsedMs?Math.min(.9,t.elapsedMs/3e4):.1,message:o,error:s})}async handleInstall(e,n){if(!this.installHandler){this.json(e,501,{error:"install not configured"});return}try{const t=n;if(!t||typeof t.agentType!="string"||!t.agentType){this.json(e,400,{error:"agentType is required"});return}const r=await this.installHandler.installAgent(t);if(r.ok)this.json(e,200,r);else{const o=r.error?.code;o==="UNKNOWN_AGENT"||o==="UNSUPPORTED_OS"?this.json(e,400,r):o==="INSTALL_IN_PROGRESS"?this.json(e,409,r):this.json(e,500,r)}}catch(t){this.error(e,t)}}}export{g as AdminServer};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{getInstallCommand as f,getCliBinary as g}from"./registry.js";import{getPrereqManualInstructions as $}from"./prereq-installer.js";import{getMirrorInstallCommands as
|
|
1
|
+
import{getInstallCommand as f,getCliBinary as g}from"./registry.js";import{getPrereqManualInstructions as $}from"./prereq-installer.js";import{getMirrorInstallCommands as r}from"./npm-registry.js";function v(u){const{agentType:e,os:p,env:h,missingPrereqs:c}=u,s=[],a=g(e)??e,o=f(e,p),m=b(p);if(s.push(`=== ${e} \u4EBA\u5DE5\u5B89\u88C5\u6307\u5357 (${m}) ===`),s.push(""),s.push("\u5F53\u524D\u73AF\u5883:"),s.push(` \u7CFB\u7EDF: ${h.osVersion} (${h.arch})`),s.push(` Node.js: ${h.nodeVersion??"\u672A\u5B89\u88C5"}`),s.push(` npm: ${h.npmVersion??"\u672A\u5B89\u88C5"}`),s.push(""),u.error&&(s.push("\u81EA\u52A8\u5B89\u88C5\u5931\u8D25\u539F\u56E0:"),s.push(` ${u.error}`),s.push("")),c.length>0){s.push("--- \u6B65\u9AA4 1: \u5B89\u88C5\u524D\u7F6E\u4F9D\u8D56 ---"),s.push("");for(const n of c){const i=$(n.id,p);s.push(`\u25B8 ${n.label}${n.minVersion?` (\u9700\u8981 >= ${n.minVersion})`:""}`),i&&s.push(` \u5B89\u88C5\u65B9\u6CD5: ${i}`),s.push(` \u9A8C\u8BC1: ${n.id==="node"?"node --version":n.id==="npm"?"npm --version":`${n.id} --version`}`),s.push("")}}const l=c.length>0?"2":"1";if(s.push(`--- \u6B65\u9AA4 ${l}: \u5B89\u88C5 ${e} ---`),s.push(""),o){if(s.push(" \u63A8\u8350\u65B9\u5F0F:"),s.push(` ${o.command}`),s.push(""),o.mode==="npm"&&o.npmPackage){const n=r(o.npmPackage);s.push(" \u955C\u50CF\u5B89\u88C5\uFF08\u56FD\u5185\u7F51\u7EDC\u6162\u6216\u88AB\u5899\u65F6\u4F7F\u7528\uFF09:");for(const i of n)s.push(` # ${i.label}`),s.push(` ${i.command}`);s.push("")}if(o.fallback){if(s.push(" \u5907\u9009\u65B9\u5F0F\uFF08\u63A8\u8350\u5B89\u88C5\u5931\u8D25\u65F6\u4F7F\u7528\uFF09:"),s.push(` ${o.fallback.command}`),o.fallback.mode==="npm"&&o.fallback.npmPackage){const n=r(o.fallback.npmPackage);for(const i of n)s.push(` # ${i.label}`),s.push(` ${i.command}`)}s.push("")}}const d=c.length>0?"3":"2";s.push(`--- \u6B65\u9AA4 ${d}: \u9A8C\u8BC1\u5B89\u88C5 ---`),s.push(""),s.push(` ${a} --version`),s.push(""),s.push(" \u5982\u679C\u663E\u793A\u7248\u672C\u53F7\uFF0C\u8BF4\u660E\u5B89\u88C5\u6210\u529F\u3002"),s.push(""),s.push("--- \u5E38\u89C1\u95EE\u9898 ---"),s.push(""),s.push(" Q: \u5B89\u88C5\u540E\u547D\u4EE4\u627E\u4E0D\u5230\uFF1F"),s.push(" A: \u53EF\u80FD\u9700\u8981\u91CD\u65B0\u6253\u5F00\u7EC8\u7AEF\uFF0C\u6216\u8FD0\u884C:"),p==="macos"||p==="linux"?(s.push(" source ~/.zshrc # zsh"),s.push(" source ~/.bashrc # bash")):s.push(" \u91CD\u65B0\u6253\u5F00 PowerShell \u6216 CMD \u7A97\u53E3"),s.push(""),o?.mode==="npm"&&(s.push(" Q: npm install -g \u62A5\u6743\u9650\u9519\u8BEF\uFF1F"),p==="macos"||p==="linux"?(s.push(" A: \u4E0D\u8981\u7528 sudo npm install -g\u3002\u5EFA\u8BAE\u7528\u4EE5\u4E0B\u65B9\u5F0F\u4E4B\u4E00:"),s.push(" 1. brew install node (macOS\uFF0C\u63A8\u8350)"),s.push(" 2. \u4F7F\u7528 fnm/nvm \u7BA1\u7406 Node.js (Linux)"),s.push(" 3. \u914D\u7F6E npm \u5168\u5C40\u76EE\u5F55: npm config set prefix ~/.npm-global")):s.push(" A: \u4EE5\u7BA1\u7406\u5458\u8EAB\u4EFD\u6253\u5F00 PowerShell \u540E\u91CD\u8BD5"),s.push("")),s.push("--- \u5B98\u65B9\u6587\u6863 ---"),s.push("");const t={claude:"https://docs.anthropic.com/en/docs/claude-code/overview",codex:"https://github.com/openai/codex",gemini:"https://github.com/google-gemini/gemini-cli",qwen:"https://github.com/QwenLM/qwen-code",cursor:"https://cursor.com/docs/cli/installation",copilot:"https://github.com/github/copilot-cli",kiro:"https://kiro.dev/docs/cli/",kimi:"https://moonshotai.github.io/kimi-cli/",openclaw:"https://github.com/openclaw/openclaw",reasonix:"https://github.com/esengine/DeepSeek-Reasonix"};return t[e]&&s.push(` ${e}: ${t[e]}`),s.join(`
|
|
2
2
|
`)}function b(u){switch(u){case"macos":return"macOS";case"linux":return"Linux";case"windows":return"Windows"}}export{v as generateManualGuide};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
function e
|
|
1
|
+
function i(e,o){return{command:`npm install -g ${e}`,mode:"npm",npmPackage:e,timeoutMs:o?.timeoutMs??12e4,prerequisites:["node","npm"],minNodeVersion:o?.minNodeVersion}}function n(e,o){return{command:e,mode:"shell",timeoutMs:o?.timeoutMs??12e4,prerequisites:o?.prerequisites}}function a(e,o,s){return{command:e,mode:"exec",execArgs:o,timeoutMs:s?.timeoutMs??6e4,prerequisites:s?.prerequisites,skipVerification:s?.skipVerification}}const l={claude:{cliBinary:"claude",macos:i("@anthropic-ai/claude-code",{minNodeVersion:"18.0"}),linux:i("@anthropic-ai/claude-code",{minNodeVersion:"18.0"}),windows:i("@anthropic-ai/claude-code",{minNodeVersion:"18.0"})},codex:{cliBinary:"codex",macos:i("@openai/codex"),linux:i("@openai/codex"),windows:i("@openai/codex")},gemini:{cliBinary:"gemini",macos:{...i("@google/gemini-cli",{minNodeVersion:"18.0"}),fallback:n("npx @google/gemini-cli --yes",{timeoutMs:12e4})},linux:i("@google/gemini-cli",{minNodeVersion:"18.0"}),windows:i("@google/gemini-cli",{minNodeVersion:"18.0"})},qwen:{cliBinary:"qwen",macos:{...n("curl -fsSL https://qwen-code-assets.oss-cn-hangzhou.aliyuncs.com/installation/install-qwen.sh | bash",{prerequisites:["curl"]}),fallback:i("@qwen-code/qwen-code@latest",{minNodeVersion:"22.0"})},linux:{...n("curl -fsSL https://qwen-code-assets.oss-cn-hangzhou.aliyuncs.com/installation/install-qwen.sh | bash",{prerequisites:["curl"]}),fallback:i("@qwen-code/qwen-code@latest",{minNodeVersion:"22.0"})},windows:{...n(`powershell -Command "Invoke-WebRequest 'https://qwen-code-assets.oss-cn-hangzhou.aliyuncs.com/installation/install-qwen.bat' -OutFile (Join-Path $env:TEMP 'install-qwen.bat'); & (Join-Path $env:TEMP 'install-qwen.bat')"`),fallback:i("@qwen-code/qwen-code@latest",{minNodeVersion:"22.0"})}},cursor:{cliBinary:"agent",macos:n("curl https://cursor.com/install -fsS | bash",{prerequisites:["curl"]}),linux:n("curl https://cursor.com/install -fsS | bash",{prerequisites:["curl"]}),windows:n(`powershell -ExecutionPolicy ByPass -c "irm 'https://cursor.com/install?win32=true' | iex"`)},copilot:{cliBinary:"copilot",macos:i("@github/copilot",{minNodeVersion:"22.0"}),linux:i("@github/copilot",{minNodeVersion:"22.0"}),windows:i("@github/copilot",{minNodeVersion:"22.0"})},kiro:{cliBinary:"kiro-cli",macos:n("curl -fsSL https://cli.kiro.dev/install | bash",{prerequisites:["curl"]}),linux:n("curl -fsSL https://cli.kiro.dev/install | bash",{prerequisites:["curl"]}),windows:null},kimi:{cliBinary:"kimi",macos:n("curl -LsSf https://code.kimi.com/install.sh | bash",{prerequisites:["curl"],timeoutMs:3e5}),linux:n("curl -LsSf https://code.kimi.com/install.sh | bash",{prerequisites:["curl"],timeoutMs:3e5}),windows:n('powershell -ExecutionPolicy ByPass -c "irm https://code.kimi.com/install.ps1 | iex"',{timeoutMs:3e5})},openclaw:{cliBinary:"openclaw",macos:i("openclaw@latest",{minNodeVersion:"22.0"}),linux:i("openclaw@latest",{minNodeVersion:"22.0"}),windows:i("openclaw@latest",{minNodeVersion:"22.0"})},reasonix:{cliBinary:"reasonix",macos:i("reasonix"),linux:i("reasonix"),windows:i("reasonix")},pi:{cliBinary:"pi",macos:i("@earendil-works/pi-coding-agent",{minNodeVersion:"22.19"}),linux:i("@earendil-works/pi-coding-agent",{minNodeVersion:"22.19"}),windows:i("@earendil-works/pi-coding-agent",{minNodeVersion:"22.19"})},agy:{cliBinary:"agy",macos:n("curl -fsSL https://antigravity.google/cli/install.sh | bash",{prerequisites:["curl"]}),linux:n("curl -fsSL https://antigravity.google/cli/install.sh | bash",{prerequisites:["curl"]}),windows:n('powershell -ExecutionPolicy ByPass -c "irm https://antigravity.google/cli/install.ps1 | iex"')},hermes:{cliBinary:"hermes",macos:null,linux:null,windows:null},codewhale:{cliBinary:"codewhale",macos:i("codewhale"),linux:i("codewhale"),windows:i("codewhale")},opencode:{cliBinary:"opencode",macos:{...n("curl -fsSL https://opencode.ai/install | bash",{prerequisites:["curl"]}),fallback:i("opencode-ai")},linux:{...n("curl -fsSL https://opencode.ai/install | bash",{prerequisites:["curl"]}),fallback:i("opencode-ai")},windows:i("opencode-ai")}};function u(){switch(process.platform){case"darwin":return"macos";case"linux":return"linux";case"win32":return"windows";default:return"linux"}}function r(e){return e.mode==="exec"&&e.execArgs&&e.execArgs.length>0?[e.command,...e.execArgs].join(" "):e.command}function m(e,o){const s=l[e];return s?s[o]:null}function d(e){return l[e]?.cliBinary??null}function c(e,o){const s=l[e];if(!s)return null;const t=s[o];return{agentType:e,cliBinary:s.cliBinary,supported:t!==null,installCommand:t?r(t):null,prerequisites:t?.prerequisites}}function p(e){return Object.keys(l).sort().map(o=>c(o,e)).filter(o=>o!==null)}function w(e){return e in l}export{u as detectPlatformOS,r as formatInstallCommand,c as getAgentInstallInfo,p as getAllAgentInstallInfo,d as getCliBinary,m as getInstallCommand,w as isKnownAgent};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{execFile as h}from"node:child_process";import{existsSync as a}from"node:fs";import{readFile as g}from"node:fs/promises";import{homedir as f}from"node:os";import{delimiter as w,dirname as S,join as l}from"node:path";import{fileURLToPath as I}from"node:url";const R=3e4,v="hermes-profile-relay.py";class i extends Error{code;constructor(o,n){super(o),this.code=n,this.name="HermesProfileRelayError"}}function E(){return l(S(I(import.meta.url)),v)}function P(){const e=(process.env.PATH??"").split(w).filter(Boolean),o=process.platform==="win32"?["hermes.exe","hermes.cmd","hermes"]:["hermes"];for(const n of e)for(const r of o){const t=l(n,r);if(a(t))return t}}async function A(){const e=process.env.GRIX_HERMES_PYTHON?.trim();if(e){if(!a(e))throw new i(`GRIX_HERMES_PYTHON points to a missing interpreter: ${e}`,"HERMES_NOT_FOUND");return e}const o=P();if(o)try{const r=(await g(o,"utf8")).slice(0,512),t=/^#!\s*(\S+)/.exec(r)?.[1];if(t&&a(t))return t}catch{}const n=l(f(),".hermes","hermes-agent",".venv","bin","python3");if(a(n))return n;throw new i("cannot locate hermes' python interpreter (set GRIX_HERMES_PYTHON to override)","HERMES_NOT_FOUND")}async function d(e){const o=await A(),n=E();if(!a(n))throw new i(`relay script is missing: ${n}`,"SCRIPT_MISSING");const r=await new Promise((m,_)=>{h(o,[n],{timeout:R,cwd:f(),maxBuffer:8388608},(u,s,y)=>{if(s&&s.trim())return m(s);if(u)return _(new i(`hermes relay script failed: ${String(y||u.message).slice(0,400)}`,"SCRIPT_FAILED"));m(s)}).stdin?.end(JSON.stringify(e))});let t;try{t=JSON.parse(r.trim())}catch{throw new i(`hermes relay script returned non-JSON output: ${r.slice(0,200)}`,"SCRIPT_FAILED")}if(!t.ok)throw new i(String(t.error_msg??"hermes relay script reported failure"),String(t.error_code??"SCRIPT_FAILED"));return t}function c(e){return typeof e=="boolean"?e:void 0}function p(e){return{profile:String(e.profile??""),home:String(e.home??""),gatewayRunning:e.gateway_running===!0,restartRequired:e.restart_required===!0,removedProvider:c(e.removed_provider),restoredModel:c(e.restored_model),userSwitchedAway:c(e.user_switched_away),modelCleared:c(e.model_cleared),warning:typeof e.warning=="string"?e.warning:void 0,snapshot:typeof e.snapshot=="string"?e.snapshot:void 0}}async function G(){const e=await d({command:"discover"}),o=Array.isArray(e.profiles)?e.profiles:[],n=Array.isArray(e.unreadable)?e.unreadable:[];return{profiles:o.map(r=>N(r)),unreadable:n.map(r=>{const t=r;return{name:String(t.name??""),home:String(t.home??""),error:String(t.error??"")}})}}function N(e){return{name:String(e.name??""),home:String(e.home??""),agentId:String(e.agent_id??""),gatewayRunning:e.gateway_running===!0,relayActive:e.relay_active===!0,modelProvider:typeof e.model_provider=="string"?e.model_provider:void 0,modelDefault:typeof e.model_default=="string"?e.model_default:void 0}}async function L(){const e=await d({command:"discover"});return(Array.isArray(e.profiles)?e.profiles:[]).map(n=>{const r=n;return{name:String(r.name??""),home:String(r.home??""),agentId:String(r.agent_id??""),gatewayRunning:r.gateway_running===!0,relayActive:r.relay_active===!0,modelProvider:typeof r.model_provider=="string"?r.model_provider:void 0,modelDefault:typeof r.model_default=="string"?r.model_default:void 0}})}async function U(e){const o=e.agentId?.trim(),n=e.baseUrl?.trim(),r=e.virtualKey?.trim(),t=e.model?.trim();if(!o)throw new i("agent_id is required","MISSING_AGENT_ID");if(!r)throw new i("virtual_key is required","MISSING_VIRTUAL_KEY");if(!n)throw new i("openai_base_url is required","MISSING_BASE_URL");try{new URL(n)}catch{throw new i("openai_base_url must be a valid URL","INVALID_BASE_URL")}if(!t)throw new i("model is required for hermes","MISSING_MODEL");return p(await d({command:"apply",agent_id:o,base_url:n,api_key:r,model:t}))}async function q(e){const o=e?.trim();if(!o)throw new i("agent_id is required","MISSING_AGENT_ID");return p(await d({command:"clear",agent_id:o}))}export{i as HermesProfileRelayError,U as applyHermesProfileRelay,q as clearHermesProfileRelay,L as discoverHermesProfiles,G as discoverHermesProfilesRaw,A as resolveHermesPython};
|