@agiflowai/agent-cli 0.0.7 → 0.0.8
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/CHANGELOG.md +4 -0
- package/README.md +54 -2
- package/dist/{cli-2Wesya-Z.js → cli-4pvA3zg9.js} +2 -2
- package/dist/{cli-2Wesya-Z.js.map → cli-4pvA3zg9.js.map} +1 -1
- package/dist/cli.js +1 -1
- package/dist/index.js +1 -1
- package/dist/package.json +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -6,7 +6,9 @@
|
|
|
6
6
|
|
|
7
7
|
@agiflowai/agent-cli - Command-line interface for connecting and managing AI agents (Claude Code, Gemini) with Agiflow remotely.
|
|
8
8
|
|
|
9
|
-
**Note**:
|
|
9
|
+
**Note**:
|
|
10
|
+
- For `agent-cli claude` and `agent-cli router` you don't need to authenticate with Agiflow server.
|
|
11
|
+
- Use `agent-cli claude --standalone` to run Claude Code locally without any backend connection.
|
|
10
12
|
|
|
11
13
|
**🔗 Related Project**: Check out the [AgiFlow AI Code Toolkit](https://github.com/AgiFlow/aicode-toolkit) - an open-source collection of AI development tools and utilities.
|
|
12
14
|
|
|
@@ -51,7 +53,10 @@ agent-cli connect --verbose
|
|
|
51
53
|
Launch a Claude Code agent with full functionality.
|
|
52
54
|
|
|
53
55
|
```bash
|
|
54
|
-
# Basic Claude agent launch (
|
|
56
|
+
# Basic Claude agent launch in standalone mode (no backend)
|
|
57
|
+
agent-cli claude --standalone
|
|
58
|
+
|
|
59
|
+
# With backend connection (requires session ID)
|
|
55
60
|
agent-cli claude --agent-session-id your-session-id
|
|
56
61
|
|
|
57
62
|
# With custom alias for easy identification
|
|
@@ -71,6 +76,9 @@ agent-cli claude \
|
|
|
71
76
|
--llm-provider chatgpt \
|
|
72
77
|
--llm-model gpt-5 \
|
|
73
78
|
--alias "gpt5-experiment"
|
|
79
|
+
|
|
80
|
+
# Standalone mode (no backend connection)
|
|
81
|
+
agent-cli claude --standalone --verbose
|
|
74
82
|
```
|
|
75
83
|
|
|
76
84
|
**Options:**
|
|
@@ -85,6 +93,7 @@ agent-cli claude \
|
|
|
85
93
|
- `--args <args...>` - Additional arguments to pass to Claude
|
|
86
94
|
- `-v, --verbose` - Enable verbose logging
|
|
87
95
|
- `--alias <alias>` - Custom session alias for easy identification
|
|
96
|
+
- `--standalone` - Run in standalone mode without backend connection
|
|
88
97
|
|
|
89
98
|
**LLM Routing Options:**
|
|
90
99
|
- `--llm-provider <provider>` - LLM provider to route requests to (e.g., `chatgpt`, `openai`)
|
|
@@ -92,6 +101,39 @@ agent-cli claude \
|
|
|
92
101
|
|
|
93
102
|
**Note:** LLM routing configuration is saved to `~/.agiflow/sessions.json` and can be changed later using the `agent-cli router` command without restarting the agent.
|
|
94
103
|
|
|
104
|
+
##### Standalone Mode
|
|
105
|
+
|
|
106
|
+
Run Claude Code agent locally without connecting to the Agiflow backend server. This is useful for:
|
|
107
|
+
- Development and testing
|
|
108
|
+
- Running in environments without backend access
|
|
109
|
+
- Privacy-focused local-only usage
|
|
110
|
+
|
|
111
|
+
**Usage:**
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
# Explicit standalone mode
|
|
115
|
+
agent-cli claude --standalone
|
|
116
|
+
|
|
117
|
+
# Standalone mode with verbose logging
|
|
118
|
+
agent-cli claude --standalone --verbose
|
|
119
|
+
|
|
120
|
+
# Standalone mode with custom working directory
|
|
121
|
+
agent-cli claude --standalone --working-directory /path/to/project
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
**How It Works:**
|
|
125
|
+
|
|
126
|
+
When `--standalone` is enabled:
|
|
127
|
+
- ✅ Claude Code launches normally with full functionality
|
|
128
|
+
- ✅ All local features work (file operations, code analysis, etc.)
|
|
129
|
+
- ❌ No WebSocket connection to Agiflow backend
|
|
130
|
+
- ❌ No remote session management
|
|
131
|
+
- ❌ No backend authentication required
|
|
132
|
+
|
|
133
|
+
**Automatic Standalone Detection:**
|
|
134
|
+
|
|
135
|
+
If no server URL is configured (via `--server-url` or environment variables), the agent automatically runs in standalone mode even without the `--standalone` flag.
|
|
136
|
+
|
|
95
137
|
##### Running Claude with ChatGPT/OpenAI
|
|
96
138
|
|
|
97
139
|
You can route Claude Code requests to ChatGPT/OpenAI using the LLM routing feature. This allows you to use GPT models while keeping the Claude Code interface.
|
|
@@ -313,6 +355,16 @@ agent-cli logout --verbose
|
|
|
313
355
|
- Check network connectivity
|
|
314
356
|
- Verify server URL is accessible
|
|
315
357
|
- Use `--verbose` flag for detailed logging
|
|
358
|
+
- Use `--standalone` flag to run without backend connection
|
|
359
|
+
|
|
360
|
+
5. **WebSocket 401 Unauthorized (Production)**
|
|
361
|
+
```
|
|
362
|
+
Error: Unexpected server response: 401
|
|
363
|
+
```
|
|
364
|
+
Solution: Use `--standalone` flag to run without backend authentication:
|
|
365
|
+
```bash
|
|
366
|
+
agent-cli claude --standalone --verbose
|
|
367
|
+
```
|
|
316
368
|
|
|
317
369
|
**LLM Routing Issues:**
|
|
318
370
|
|
|
@@ -74,7 +74,7 @@ ${s.stderr}
|
|
|
74
74
|
`)}
|
|
75
75
|
|
|
76
76
|
**Suggestion**: Please check the hook commands and ensure all dependencies are installed correctly.`;await this.httpService.createTaskComment(e,i,void 0,{type:"hook_failure",failedHooks:n}),this.logger.info("Hook failure reported to task")}catch(r){this.logger.warn("Failed to report hook errors to task:",r)}}async setupDockerRepository(e){const n=e.payload?.agentConfig;if(!(n?.docker===!0)){this.logger.debug("Not using Docker, skipping repository setup");return}const i=n?.workingDirectory||this.agentInfo?.workingDir;if(!i){this.logger.warn("No working directory specified for Docker repository setup");return}try{this.logger.info("Setting up Docker repository with environment files...");const s=process.cwd(),a=await new $t({targetDirectory:i,originalDirectory:s,logger:this.logger}).setup();if(a.envFilesCopied.length>0){const p=a.envFilesCopied.filter(E=>E.copied).length;this.logger.success(`Copied ${p} environment files to Docker container`)}a.errors.length>0&&(this.logger.warn(`Docker repository setup completed with ${a.errors.length} errors`),a.errors.forEach(p=>this.logger.debug(`Setup error: ${p}`)));const l=n?.agentSessionId||this.agentInfo?.agentSessionId,g=n?.hooks?.preAgentStartHooks||n?.hooks?.preAgentStart;await this.executePreAgentStartHooks(g,i,l)}catch(s){this.logger.error(`Failed to setup Docker repository: ${s}`)}}setupInitialPtySize(){try{const e=process.stdout.columns||80,n=process.stdout.rows||24;this.resizePty(e,n),this.logger.info(`🖥️ PTY resized to fit terminal: ${e}x${n}`)}catch(e){this.logger.warn("Failed to detect terminal size, using defaults:",e),this.resizePty(80,24)}}}class $r extends Ir{claudePath;claudeArgs=[];optionBuffer="";optionTimeout=null;isCollectingOptions=!1;OPTION_WAIT_MS=1e3;agentInputReady=!1;startupCommands=[];startupCommandsExecuted=!1;tempMcpConfigPath=null;tempSettingsPath=null;claudeVersion=null;supportsSettingsFlag=!1;ctrlCCount=0;ctrlCTimeout=null;CTRL_C_RESET_MS=2e3;constructor(e){super({...e,agentType:J.CLAUDE_CODE}),this.claudePath=e.claudePath||"claude"}detectClaudeType(){const{execSync:e}=require("child_process");try{const n=e(`which ${this.claudePath}`,{encoding:"utf-8"}).trim();try{const r=e(`file "${n}"`,{encoding:"utf-8"});if(r.includes("Mach-O")||r.includes("executable"))return this.logger.info(`Detected standalone binary at ${n}`),this.logger.info("Using npx @anthropic-ai/claude-code instead to enable hooks"),{command:"npx",args:["-y","@anthropic-ai/claude-code"]}}catch{}try{const r=T.readFileSync(n,"utf-8");if(r.startsWith("#!/usr/bin/env node")||r.includes("npx"))return this.logger.info(`Detected npm package script at ${n}`),{command:this.claudePath,args:[]}}catch{}return this.logger.info("Using npx @anthropic-ai/claude-code to enable hooks"),{command:"npx",args:["-y","@anthropic-ai/claude-code"]}}catch{return this.logger.info("Claude binary not found, using npx @anthropic-ai/claude-code"),{command:"npx",args:["-y","@anthropic-ai/claude-code"]}}}extractOptions(e){const n=[],r=e.split(`
|
|
77
|
-
`);for(const i of r){const s=i.trim(),o=s.match(/❯\s*(\d+)\.\s+(.+)$/);if(o){n.push({number:parseInt(o[1],10),text:o[2].trim(),selected:!0});continue}const a=s.match(/^│.*?(\d+)\.\s+(.+)$/);a&&n.push({number:parseInt(a[1],10),text:a[2].trim(),selected:!1})}return n}processDataForOptions(e){this.optionBuffer+=e;const n=we(e);(/❯\s*\d+\./m.test(n)||/Do you want to proceed\?/m.test(n)||/│.*\d+\.\s+/.test(n))&&!this.isCollectingOptions&&this.startOptionCollection()}startOptionCollection(){this.isCollectingOptions=!0,this.optionTimeout=setTimeout(async()=>{await this.processCollectedOptions()},this.OPTION_WAIT_MS)}async processCollectedOptions(){const e=we(this.optionBuffer),n=this.extractOptions(e);n.length>0&&await this.handleDetectedOptions(n),this.resetOptionState()}resetOptionState(){this.optionBuffer="",this.optionTimeout=null,this.isCollectingOptions=!1}async handleDetectedOptions(e){if(e.length===0)return;const n=new Date().toISOString();if(this.agentInfo&&this.httpService){D.SubEnvManager.isContainerMode&&await new Promise(i=>setTimeout(i,3e3));try{let i=e.map(l=>({label:l.text,value:l.number,selected:l.selected||!1}));i=this.deduplicateOptions(i);const s=D.createSelectComponent(i),o=D.createToolInvocationInput(!1,s),a=[D.createTextPart("Claude has detected available options. Please select one:"),D.createToolInvocationPart({toolName:"selectOption",toolCallId:`option-selection-${Date.now()}`,args:{timestamp:n},state:"call"},o)];await this.httpService.sendMessage({sessionId:this.agentInfo.agentSessionId,messageType:"output",parts:a,metadata:{optionDetection:!0,optionsCount:i.length,timestamp:n}})}catch(i){this.logger.error("Failed to send structured options message:",i)}}}deduplicateOptions(e){if(e.length===0)return e;const n=new Set;let r=-1;for(let a=0;a<e.length;a++){if(n.has(e[a].value)){r=a;break}n.add(e[a].value)}if(r===-1)return e;const i=e[r].value;let s=-1;for(let a=e.length-1;a>=0;a--)if(e[a].value===i){s=a;break}return e.slice(s)}verifyAgentReady(e){if(this.agentInputReady)return;const n=we(e);[/Type (your )?(message|prompt)/i,/Enter (your )?(message|prompt)/i,/Press Enter to send/i,/╭[\s\S]*?╰/m,/┌[\s\S]*?└/m].some(s=>s.test(n))&&(this.agentInputReady=!0,this.sendAgentStatus({agentType:J.CLAUDE_CODE,status:H.IDLE,message:"Agent input box detected. Ready for commands."}),this.executeStartupCommands())}async executeStartupCommands(){if(!this.startupCommandsExecuted&&this.startupCommands.length){this.startupCommandsExecuted=!0;for(const e of this.startupCommands)try{await this.sendInputToAgent(`${e}\r`),await new Promise(n=>setTimeout(n,250))}catch(n){this.logger.warn(`Failed executing startup command: ${e}`,n)}}}handleCtrlC(){return this.ctrlCCount++,this.ctrlCTimeout&&clearTimeout(this.ctrlCTimeout),this.ctrlCCount===1?(this.ctrlCTimeout=setTimeout(()=>{this.ctrlCCount=0,this.ctrlCTimeout=null},this.CTRL_C_RESET_MS),!1):this.ctrlCCount>=2?(this.ctrlCTimeout&&(clearTimeout(this.ctrlCTimeout),this.ctrlCTimeout=null),this.ctrlCCount=0,!0):!1}writeMcpConfigFile(e,n){if(!e)return null;try{const r={};if(Array.isArray(e))for(const a of e){if(!a||typeof a!="object")continue;const l=a.name||a.id||a.key;l&&(r[l]={type:a.type||"stdio",command:a.command,args:a.args||a.arguments||[],url:a.url,env:a.env||{},disabled:a.disabled??!1,headers:a.headers||{}})}else if(typeof e=="object")for(const[a,l]of Object.entries(e))l&&typeof l=="object"&&(r[a]={...l,headers:l.headers||{}});if(Object.keys(r).length===0)return null;const i={mcpServers:r},s=`claude-mcps-${n}.json`,o=C.join(ae.tmpdir(),s);return T.writeFileSync(o,JSON.stringify(i,null,2),"utf8"),this.tempMcpConfigPath=o,o}catch(r){return this.logger.warn("Failed to write MCP config file",r),null}}async discoverSubagents(e){const n=[];try{const r=C.join(e,".claude","agents");if(T.existsSync(r)){const o=T.readdirSync(r).filter(a=>a.endsWith(".md"));for(const a of o){const l=C.basename(a,".md");n.push(`${l} (project)`)}}const i=C.join(ae.homedir(),".claude","agents");if(T.existsSync(i)){const o=T.readdirSync(i).filter(a=>a.endsWith(".md"));for(const a of o){const l=C.basename(a,".md");n.some(p=>p.startsWith(`${l} (`))||n.push(`${l} (user)`)}}}catch(r){this.logger.warn("Failed to discover subagents:",r)}return n}async detectClaudeVersion(){try{const{execSync:e}=await import("child_process"),r=e(`${this.claudePath} --version`,{encoding:"utf-8"}).match(/(\d+\.\d+\.\d+)/);if(r){this.claudeVersion=r[1];const[i]=this.claudeVersion.split(".").map(Number);this.supportsSettingsFlag=i>=2,this.logger.info(`Detected Claude Code version ${this.claudeVersion} (--settings flag: ${this.supportsSettingsFlag})`)}}catch(e){this.logger.warn("Failed to detect Claude Code version, assuming legacy version",e),this.supportsSettingsFlag=!1}}async createTempSettingsFile(e){try{const n=[C.join(__dirname,"..","..","dist","claudePostToolUse.js"),C.join(__dirname,"..","dist","claudePostToolUse.js"),C.join(__dirname,"dist","claudePostToolUse.js"),C.join(process.cwd(),"dist","claudePostToolUse.js")];let r=null;for(const a of n)if(T.existsSync(a)){r=a;break}if(!r)return this.logger.warn("PostToolUse hook file not found at any expected location"),null;const i={hooks:{PostToolUse:[{matcher:"*",hooks:[{type:"command",command:`node ${r}`}]}]}},s=`claude-settings-${e}.json`,o=C.join(ae.tmpdir(),s);return await pe.writeFile(o,JSON.stringify(i,null,2),"utf8"),this.tempSettingsPath=o,this.logger.info(`Created temporary settings file at ${o}`),o}catch(n){return this.logger.error(`Failed to create temporary settings file: ${n}`),null}}async addPostToolUseHook(e){const n=C.join(e,".claude","settings.local.json"),r=C.dirname(n);try{await pe.mkdir(r,{recursive:!0});let i={};try{const g=await pe.readFile(n,"utf-8");i=JSON.parse(g)}catch{this.logger.debug("No existing settings.local.json found, creating new one")}i.hooks||(i.hooks={}),i.hooks.PostToolUse||(i.hooks.PostToolUse=[]);let s=null;const o=[C.join(__dirname,"..","..","dist","claudePostToolUse.js"),C.join(__dirname,"..","dist","claudePostToolUse.js"),C.join(__dirname,"dist","claudePostToolUse.js"),C.join(process.cwd(),"dist","claudePostToolUse.js")];for(const g of o)if(T.existsSync(g)){s=g;break}if(!s){this.logger.warn("PostToolUse hook file not found at any expected location");return}const a={matcher:"*",hooks:[{type:"command",command:`node ${s}`}]};i.hooks.PostToolUse.some(g=>g.hooks&&Array.isArray(g.hooks)?g.hooks.some(p=>p.type==="command"&&p.command===`node ${s}`):!1)||(i.hooks.PostToolUse.push(a),await pe.writeFile(n,JSON.stringify(i,null,2)),this.logger.info(`Added PostToolUse hook to ${n}`))}catch(i){this.logger.error(`Failed to add PostToolUse hook: ${i}`)}}async removePostToolUseHook(e){const n=C.join(e,".claude","settings.local.json");try{const r=await pe.readFile(n,"utf-8"),i=JSON.parse(r);if(i.hooks?.PostToolUse){let s=null;const o=[C.join(__dirname,"..","..","dist","claudePostToolUse.js"),C.join(__dirname,"..","dist","claudePostToolUse.js"),C.join(__dirname,"dist","claudePostToolUse.js"),C.join(process.cwd(),"dist","claudePostToolUse.js")];for(const l of o)if(T.existsSync(l)){s=l;break}if(!s)return;const a=`node ${s}`;i.hooks.PostToolUse=i.hooks.PostToolUse.filter(l=>l.hooks&&Array.isArray(l.hooks)?(l.hooks=l.hooks.filter(g=>!(g.type==="command"&&g.command===a)),l.hooks.length>0):!0),i.hooks.PostToolUse.length===0&&delete i.hooks.PostToolUse,Object.keys(i.hooks).length===0&&delete i.hooks,await pe.writeFile(n,JSON.stringify(i,null,2)),this.logger.info(`Removed PostToolUse hook from ${n}`)}}catch(r){this.logger.debug(`Could not remove PostToolUse hook: ${r}`)}}writeSessionSettings(e,n,r,i,s){try{const o=C.join(ae.homedir(),".agiflow"),a=C.join(o,"sessions.json");T.existsSync(o)||T.mkdirSync(o,{recursive:!0});let l={};if(T.existsSync(a))try{l=JSON.parse(T.readFileSync(a,"utf-8"))}catch{this.logger.warn("Invalid sessions.json, creating new one")}l[e]={...n&&{provider:n},...r&&{model:r},...i&&{alias:i},...s&&{reasoningEffort:s}},T.writeFileSync(a,JSON.stringify(l,null,2),"utf-8"),this.logger.info(`Session settings written to ${a}`),n&&r?this.logger.info(`Session: ${i||e} (${n}/${r})`):this.logger.info(`Session: ${i||e} (default Claude)`)}catch(o){this.logger.error("Failed to write session settings",o)}}async launchAgent(e){const{payload:n}=e,r=n.agentSessionId,i=n.agentConfig?.workingDirectory?D.FileSystemUtils.getAbsolutePath(n.agentConfig.workingDirectory):process.cwd();this.startupCommands=n.agentConfig?.commands??[],this.startupCommandsExecuted=!1;const s=n.agentConfig?.xterm??!1;this.setXtermEnabled(s);const o=n.agentConfig?.llmConfig,a=n.agentConfig?.sessionAlias;o?.provider?this.writeSessionSettings(r,o.provider,o.model||"default",a,void 0):this.writeSessionSettings(r,void 0,void 0,a,void 0);const{command:l,args:g}=this.detectClaudeType();this.claudePath=l,this.claudeArgs=g,await this.detectClaudeVersion();const p=n.agentConfig?.hooks?.preAgentStartHooks||n.agentConfig?.hooks?.preAgentStart;await this.executePreAgentStartHooks(p,i,r),D.SubEnvManager.isContainerMode&&s&&this.setFullscreen(!0),this.serverUrl?this.supportsSettingsFlag?await this.createTempSettingsFile(r):await this.addPostToolUseHook(i):this.logger.info("Running in standalone mode - PostToolUse hook will not be configured");const m=await this.discoverSubagents(i);m.length>0&&this.logger.info(`Discovered ${m.length} Claude subagent(s): ${m.join(", ")}`);const F=this.writeMcpConfigFile(n.agentConfig?.mcps,r),k=C.join(i,".mcp.json");await this.connectToSession(r),await this.setupDockerRepository(e);const u=this.buildClaudeArgs(e),c=[];T.existsSync(k)&&c.push(k),F&&c.push(F),c.length&&u.push("--mcp-config",...c);try{const h=this.getAgentEnvironmentVariables(),d=this.configureNodeOptions(h),f=process.listeners("SIGINT"),_=()=>{this.handleCtrlC()?(this.logger.info("Double Ctrl+C detected, terminating Claude agent..."),process.removeListener("SIGINT",_),f.forEach(P=>{typeof P=="function"&&process.on("SIGINT",P)}),this.ptyRunner.kill("SIGINT")):this.ptyRunner.sendInput("")};process.removeAllListeners("SIGINT"),process.on("SIGINT",_);const O=this.ptyRunner.spawn({command:this.claudePath,args:u,workingDir:i,agentSessionId:r,logger:this.logger,agentApiKey:this.apiKey,agentOrganizationId:this.organizationId,agentServerUrl:this.serverUrl,agentType:this.agentType,env:{...h,NODE_OPTIONS:d,[D.ENV_KEYS.AGENT_SESSION_ID]:r,[D.ENV_KEYS.AGENT_TYPE]:this.agentType,...F?{CLAUDE_MCP_CONFIG_FILE:F}:{},...n.data?{[D.ENV_KEYS.CONTEXT_DATA]:n.data}:{}}},{onExit:async(x,P)=>{if(process.removeListener("SIGINT",_),f.forEach(M=>{typeof M=="function"&&process.on("SIGINT",M)}),this.ctrlCTimeout&&(clearTimeout(this.ctrlCTimeout),this.ctrlCTimeout=null),this.optionTimeout&&(clearTimeout(this.optionTimeout),this.resetOptionState()),this.serverUrl)if(this.supportsSettingsFlag){if(this.tempSettingsPath){try{T.unlinkSync(this.tempSettingsPath),this.logger.info(`Removed temporary settings file: ${this.tempSettingsPath}`)}catch(M){this.logger.warn("Failed to delete temp settings file",M)}this.tempSettingsPath=null}}else await this.removePostToolUseHook(i);if(this.tempMcpConfigPath){try{T.unlinkSync(this.tempMcpConfigPath)}catch(M){this.logger.warn("Failed to delete temp MCP config file",M)}this.tempMcpConfigPath=null}this.logger.info(`Claude Code PTY exited with code ${x}, signal: ${P}`),this.sendAgentStatus({agentType:J.CLAUDE_CODE,status:x===0?H.STOPPED:H.ERROR,message:`Claude Code session ended (exit code: ${x})`}),this.wsService&&this.wsService.disconnect(),this.ptySessionId=null,this.agentInfo&&await this.terminationService.handlePtyTermination({agentSessionId:this.agentInfo.agentSessionId,reason:P===2?"agent_sigint":"natural",exitCode:x,signal:P})},onData:async x=>{this.processDataForOptions(x),this.verifyAgentReady(x),this.xtermEnabled&&this.sendStdoutEvent(x)}});this.logger.success(`Claude Code launched successfully for session agentSessionId: ${r}`),this.ptySessionId=O.id,s?this.resizePty(80,24):this.setupInitialPtySize();const A={agentSessionId:r,agentType:J.CLAUDE_CODE,status:H.IDLE,workingDir:i,process:O.ptyProcess,metadata:{agentSessionId:r,agentType:n.agentType,config:n.agentConfig,ptySessionId:O.id}};return this.setAgentInfo(A),A}catch(h){throw new Error(`Failed to launch Claude Code: ${h instanceof Error?h.message:String(h)}`)}}async isAvailable(){return!0}async getVersion(){return"claude-pty-agent"}buildClaudeArgs(e){const n=[...this.claudeArgs,...e.payload?.agentConfig?.args||[]],r=e.payload?.agentConfig?.conversationSessionId;return r&&n.push("--session-id",r),this.supportsSettingsFlag&&this.tempSettingsPath&&n.push("--settings",this.tempSettingsPath),n}}class lh{session;logger;constructor(e){this.logger=e??new D.Logger({verbose:!1})}async isDockerAvailable(){return new Promise(e=>{const n=ie.spawn("docker",["--version"],{stdio:"pipe"});n.on("close",r=>{e(r===0)}),n.on("error",()=>{e(!1)})})}spawn(e,n){const r=e.agentSessionId||xn.ulid(),i=e.workingDir??process.cwd(),s=e.dockerImage||"agiflow/claude-agent:latest",o=this.buildDockerArgs(r,e,s),a=`docker ${o.join(" ")}`;this.logger.info(`🐳 Docker Command: ${a}`),this.logger.debug("Spawning Docker container with args:",o);const l=ie.spawn("docker",o,{stdio:["pipe","pipe","pipe"]}),g=`claude-agent-${r}`,p={id:r,dockerProcess:l,containerId:g,command:`docker ${o.join(" ")}`,workingDir:i,status:"running",createdAt:new Date,lastActivity:new Date,logStream:e.logStream};return l.stdout?.on("data",E=>{p.lastActivity=new Date;const m=E.toString();p.logStream&&p.logStream.write(m),process.stdout.write(m),n?.onData&&n.onData(m)}),l.stderr?.on("data",E=>{const m=E.toString();this.logger.error(`🐳 Container ${g} stderr: ${m.trim()}`),process.stderr.write(m)}),l.on("close",E=>{p.status=E===0?"stopped":"error",p.lastActivity=new Date,this.logger.info(`Container ${g} exited with code ${E}`),p.logStream&&p.logStream.end(),n?.onExit&&n.onExit(E||0,void 0)}),l.on("error",E=>{p.status="error",this.logger.error(`Container ${g} error:`,E),n?.onError&&n.onError(E)}),this.session=p,p}buildDockerArgs(e,n,r){const s=["run","--rm","--name",`claude-agent-${e}`,"-i"];if(n.privileged&&s.push("--privileged"),n.user&&s.push("--user",n.user),n.networkMode&&s.push("--network",n.networkMode),n.ports&&n.ports.length>0)for(const m of n.ports)s.push("-p",`${m.host}:${m.container}`);const o=n.agentServerUrl||D.SubEnvManager.serverUrl;s.push("-e",`${D.ENV_KEYS.AGENT_SERVER_URL}=${o}`);const a=n.agentApiKey||D.SubEnvManager.apiKey;a&&s.push("-e",`${D.ENV_KEYS.AGENT_API_KEY}=${a}`);const l=n.agentOrganizationId||D.SubEnvManager.organizationId;l&&s.push("-e",`${D.ENV_KEYS.AGENT_ORGANIZATION_ID}=${l}`);const g=n.agentType||D.SubEnvManager.agentType;g&&s.push("-e",`${D.ENV_KEYS.AGENT_TYPE}=${g}`);const p=e||D.SubEnvManager.sessionId;if(p&&s.push("-e",`${D.ENV_KEYS.AGENT_SESSION_ID}=${p}`),n.additionalEnvVars)for(const[m,F]of Object.entries(n.additionalEnvVars))s.push("-e",`${m}=${F}`);n.workingDir&&(s.push("-v",`${n.workingDir}:/workspace`),s.push("-w","/workspace"));const E=C.join(ae.homedir(),".agiflow");if(ei.existsSync(E)?(s.push("-v",`${E}:/home/node/.agiflow:ro`),this.logger.debug(`Mounting credentials directory: ${E} -> /home/node/.agiflow`)):this.logger.warn(`Credentials directory not found at: ${E}`),n.additionalVolumes&&n.additionalVolumes.length>0)for(const m of n.additionalVolumes){let F;m.type==="volume"?(F=m.readonly?`${m.host}:${m.container}:ro`:`${m.host}:${m.container}`,this.logger.debug(`Mounting named volume: ${m.host} -> ${m.container}`)):(F=m.readonly?`${m.host}:${m.container}:ro`:`${m.host}:${m.container}`,this.logger.debug(`Mounting bind volume: ${m.host} -> ${m.container}`)),s.push("-v",F)}if(D.SubEnvManager.isDebugMode&&s.push("-v","/var/run/docker.sock:/var/run/docker.sock"),s.push(r),s.push("claude"),s.push("--server-url",o),n.agentApiUrl&&s.push("--api-url",n.agentApiUrl),a&&s.push("--api-key",a),l&&s.push("--organization-id",l),p&&s.push("--agent-session-id",p),n.workingDir&&s.push("--working-directory","/workspace"),n.agentConfig){const m=encodeURIComponent(n.agentConfig);s.push("--agent-config",m)}return n.verbose&&s.push("--verbose"),n.args&&n.args.length>0&&s.push("--args",...n.args),s}sendInput(e){if(!this.session?.dockerProcess?.stdin)return this.logger.warn("No active Docker container to send input to"),!1;try{return this.session.dockerProcess.stdin.write(e),this.session.lastActivity=new Date,!0}catch(n){return this.logger.error("Failed to send input to Docker container:",n),!1}}kill(e){if(!this.session?.dockerProcess)return this.logger.warn("No active Docker container to kill"),!1;try{return this.logger.info(`Stopping Docker container: ${this.session.containerId}`),this.session.dockerProcess.kill(e||"SIGTERM"),setTimeout(()=>{this.session.dockerProcess&&!this.session.dockerProcess.killed&&(this.logger.warn(`Force killing container ${this.session.containerId}`),this.session.dockerProcess.kill("SIGKILL"))},5e3),this.session.status="stopped",!0}catch(n){return this.logger.error(`Failed to kill Docker container: ${n}`),!1}}getSession(){return this.session}isSessionRunning(){return this.session?.status==="running"}}class hh{agentType;serverUrl;apiKey;verbose;organizationId;logger;terminationService;dockerImage;onFullscreenChange;dockerRunner;agentInfo=null;isFullscreen=!1;backgroundOutputBuffer=[];BACKGROUND_BUFFER_SIZE=5;BACKGROUND_CHUNK_MAX_SIZE=2048;constructor(e){this.agentType=e.agentType,this.serverUrl=e.serverUrl,this.apiKey=e.apiKey,this.verbose=e.verbose??!1,this.organizationId=e.organizationId,this.logger=e.logger??new D.Logger({verbose:this.verbose}),this.terminationService=e.terminationService??new Le(this.logger),this.dockerImage=e.dockerImage||"agiflow/agent:latest",this.onFullscreenChange=e.onFullscreenChange,this.dockerRunner=new lh(this.logger)}async isAvailable(){return await this.dockerRunner.isDockerAvailable()}getAgentInfo(){return this.agentInfo}setAgentInfo(e){this.agentInfo=e}getAgentEnvironmentVariables(){const e={};for(const[n,r]of Object.entries(process.env))r!==void 0&&(e[n]=r);return this.apiKey&&(e[D.ENV_KEYS.AGENT_API_KEY]=this.apiKey),this.organizationId&&(e[D.ENV_KEYS.AGENT_ORGANIZATION_ID]=this.organizationId),this.serverUrl&&(e[D.ENV_KEYS.AGENT_SERVER_URL]=this.serverUrl),e[D.ENV_KEYS.AGENT_TYPE]=this.agentType,e[D.ENV_KEYS.VERBOSE]=this.verbose?"true":"false",e}addToBackgroundBuffer(e){const n=e.length>this.BACKGROUND_CHUNK_MAX_SIZE?e.substring(e.length-this.BACKGROUND_CHUNK_MAX_SIZE):e;this.backgroundOutputBuffer.push(n),this.backgroundOutputBuffer.length>this.BACKGROUND_BUFFER_SIZE&&this.backgroundOutputBuffer.shift(),this.logger.debug(`🐳 Added ${n.length} chars to Docker background buffer (${this.backgroundOutputBuffer.length}/${this.BACKGROUND_BUFFER_SIZE} chunks)`)}flushBackgroundBuffer(){if(this.backgroundOutputBuffer.length===0)return;this.logger.debug(`🐳 Flushing Docker background buffer: ${this.backgroundOutputBuffer.length} chunks`);const e=this.backgroundOutputBuffer.join("");e&&(console.log(e),this.logger.debug(`🐳 Docker background buffer flushed: ${e.length} chars`)),this.backgroundOutputBuffer=[]}setFullscreen(e,n=!1){const r=this.isFullscreen;this.isFullscreen=e,this.logger.debug(`Docker agent fullscreen mode ${e?"enabled":"disabled"}`),e&&!r&&this.agentInfo&&this.flushBackgroundBuffer(),!n&&r!==e&&this.agentInfo&&this.onFullscreenChange&&this.onFullscreenChange(this.agentInfo.agentSessionId,e)}getFullscreen(){return this.isFullscreen}disconnect(){this.logger.info(`Disconnecting ${this.agentType} Docker agent`),this.dockerRunner.kill("SIGTERM"),this.agentInfo=null}}class Or extends hh{onBeforeTerminate;constructor(e){super({...e,agentType:J.CLAUDE_CODE,dockerImage:e.dockerImage||ue.VITE_AGENT_CLI_DOCKER_IMAGE}),this.onBeforeTerminate=e.onBeforeTerminate}async getVersion(){return"claude-docker-agent"}async launchAgent(e){const{payload:n}=e,r=n.agentSessionId,i=n.agentConfig?.workingDirectory?D.FileSystemUtils.getAbsolutePath(n.agentConfig.workingDirectory):process.cwd();if(!await this.isAvailable())throw new Error("Docker is not available. Please install Docker to use containerized agents.");this.logger.info(`Launching Claude agent in Docker container for session: ${r}`);const s=this.getDockerOptions(e);try{const o=this.dockerRunner.spawn(s,{onExit:async(l,g)=>{if(this.logger.info(`Docker Claude agent exited with code ${l}, signal: ${g}`),this.onBeforeTerminate)try{this.logger.info("Executing cleanup callback before termination..."),await this.onBeforeTerminate(),this.logger.info("Cleanup callback completed successfully")}catch(p){this.logger.error("Error during cleanup callback:",p)}this.agentInfo&&await this.terminationService.handlePtyTermination({agentSessionId:this.agentInfo.agentSessionId,reason:g===2?"agent_sigint":"natural",exitCode:l,signal:g})},onData:async l=>{this.logger.debug("Docker container output:",l)},onError:l=>{this.logger.error("Docker container error:",l)}});this.logger.success(`Docker Claude agent launched successfully for session: ${r}`);const a={agentSessionId:r,agentType:J.CLAUDE_CODE,status:H.IDLE,workingDir:i,containerId:o.containerId,dockerSessionId:o.id,process:o.dockerProcess,metadata:{agentSessionId:r,agentType:n.agentType,config:n.agentConfig,docker:!0,containerId:o.containerId,dockerSessionId:o.id}};return this.setAgentInfo(a),a}catch(o){throw new Error(`Failed to launch Claude Docker agent: ${o instanceof Error?o.message:String(o)}`)}}disconnect(){this.logger.info("Disconnecting Claude Docker agent");try{this.dockerRunner.isSessionRunning()&&(this.dockerRunner.kill("SIGTERM"),setTimeout(()=>{this.dockerRunner.isSessionRunning()&&(this.logger.warn("Force killing Docker container..."),this.dockerRunner.kill("SIGKILL"))},3e3)),this.agentInfo=null,this.logger.info("Claude Docker agent disconnected successfully")}catch(e){this.logger.error("Error during Docker agent disconnect:",e)}}getDockerOptions(e){const{payload:n}=e,r=n.agentSessionId,i=n.agentConfig?.workingDirectory?D.FileSystemUtils.getAbsolutePath(n.agentConfig.workingDirectory):process.cwd(),s=ue.VITE_INJECT_AGIFLOW_APP_DOCKER_ENDPOINT||this.serverUrl,o=s?s.replace(/^http/,"ws"):void 0;let a=null;n.agentConfig?.mcps&&(a=this.processAgentConfig(n.agentConfig.mcps));const l={...n.agentConfig,workingDirectory:"/workspace"};return a&&(l.mcps=a),{agentSessionId:r,dockerImage:n.agentConfig?.dockerImage||this.dockerImage,workingDir:i,agentApiKey:this.apiKey,agentOrganizationId:this.organizationId,agentServerUrl:o,agentApiUrl:s,agentType:J.CLAUDE_CODE,agentConfig:JSON.stringify(l),verbose:this.verbose,additionalEnvVars:{[D.ENV_KEYS.NODE_ENV]:D.SubEnvManager.nodeEnv,[D.ENV_KEYS.AGENT_SESSION_ID]:r,[D.ENV_KEYS.AGENT_CONTAINER_MODE]:D.SubEnvManager.isContainerMode?"true":"false",IS_SANDBOX:"1"},additionalVolumes:this.getAdditionalVolumes(n),networkMode:ue.VITE_AGENT_CLI_DOCKER_NETWORK,user:void 0}}getAdditionalVolumes(e){const n=[],r=D.SubEnvManager.homeDir;if(r)if(n.push({host:`${r}/.ssh`,container:"/root/.ssh",readonly:!0,type:"bind"}),n.push({host:"agiflow-pnpm-cache",container:"/root/.pnpm-store",readonly:!1,type:"volume"}),n.push({host:"agiflow-npm-cache",container:"/root/.npm",readonly:!1,type:"volume"}),n.push({host:"agiflow-node-gyp-cache",container:"/root/.cache/node-gyp",readonly:!1,type:"volume"}),D.SubEnvManager.isMacOS){const s=`${r}/.claude_tmp.json`;T.existsSync(s)||T.writeFileSync(s,"{}","utf8"),n.push({host:s,container:"/root/.claude.json",readonly:!1,type:"bind"});const o=`${r}/.claude_tmp`;T.existsSync(o)||T.mkdirSync(o,{recursive:!0}),n.push({host:o,container:"/root/.claude",readonly:!1,type:"bind"})}else{const s=`${r}/.claude.json`;T.existsSync(s)||T.writeFileSync(s,"{}","utf8"),n.push({host:s,container:"/root/.claude.json",readonly:!1,type:"bind"});const o=`${r}/.claude`;T.existsSync(o)||T.mkdirSync(o,{recursive:!0}),n.push({host:o,container:"/root/.claude",readonly:!1,type:"bind"})}return n}async sendInputToAgent(e){this.logger.debug("sendInputToAgent called on Docker agent - no action needed")}getContainerStatus(){const e=this.dockerRunner.getSession();return{isRunning:this.dockerRunner.isSessionRunning(),containerId:e?.containerId,status:e?.status,lastActivity:e?.lastActivity}}async isHealthy(){try{return!!this.dockerRunner.isSessionRunning()}catch(e){return this.logger.error("Health check failed:",e),!1}}getDockerSession(){return this.dockerRunner.getSession()}processAgentConfig(e){const n={};if(Array.isArray(e))for(const r of e){if(!r||typeof r!="object")continue;const i=r.name||r.id||r.key;i&&(n[i]={type:r.type||"stdio",command:r.command,args:r.args||r.arguments||[],url:r.url,env:r.env||{},disabled:r.disabled??!1,headers:r.headers||{}})}else if(typeof e=="object")for(const[r,i]of Object.entries(e))i&&typeof i=="object"&&(n[r]={...i,headers:i.headers||{}});return n}}class xr extends ze{register(e){e.command("claude").description("Launch Claude Code agent with full functionality").option("-s, --server-url <url>","WebSocket server URL").option("-a, --api-url <url>","HTTP API URL").option("-k, --api-key <key>","API key for authentication").option("-o, --organization-id <id>","Organization ID").option("-v, --verbose","Enable verbose logging",!1).option("-p, --claude-path <path>","Path to Claude executable","claude").option("-w, --working-directory <dir>","Working directory",process.cwd()).option("-i, --agent-session-id <id>","Agent session ID").option("-c, --agent-config <json>","Agent configuration as JSON string").option("--args <args...>","Additional arguments to pass to Claude").option("--docker","Run Claude agent in Docker container",!1).option("--docker-image <image>","Docker image to use for containerized agent").option("--llm-provider <provider>","LLM provider to route requests to (e.g., openai)").option("--llm-model <model>","LLM model to use (e.g., gpt-4-turbo, gpt-5)").option("--alias <alias>","Custom session alias for easy identification").action(async n=>{try{await this.execute(n)}catch(r){this.handleError(r)}})}async execute(e){let n;if(e.llmProvider){const c=["codex","gpt","openai","chatgpt"],h=e.llmProvider.toLowerCase();c.includes(h)||(this.error(`Invalid LLM provider: ${e.llmProvider}`),this.error(`Allowed providers: ${c.join(", ")}`),process.exit(1)),n={provider:h,model:e.llmModel}}const r=e.serverUrl||ue.VITE_INJECT_AGIFLOW_APP_ENDPOINT,i=e.apiUrl||ue.VITE_INJECT_AGIFLOW_APP_ENDPOINT,s=e.apiKey||D.SubEnvManager.apiKey,o=e.organizationId||D.SubEnvManager.organizationId;let a=e.agentSessionId||D.SubEnvManager.sessionId;if(a||(a=On.randomUUID(),e.verbose&&this.info(`Generated session ID: ${a}`)),!r)e.verbose&&(this.info("Running in standalone mode (no WebSocket connection)"),this.info("Agent will launch without remote connectivity"));else try{new URL(r)}catch{this.error("Invalid server URL. Must be a valid WebSocket URL (ws:// or wss://)."),process.exit(1)}if(i)try{new URL(i)}catch{this.error("Invalid API URL. Must be a valid HTTP URL."),process.exit(1)}else!r&&e.verbose&&this.info("No API URL provided - HTTP features will be unavailable");const l=()=>{const h=new Date().toISOString().replace(/[-:]/g,"").replace("T","-").split(".")[0],d=e.llmProvider||"claude",f=e.llmModel?.replace(/[^a-z0-9]/gi,"")||"default";return`${d}-${f}-${h}`},g=e.alias||l(),p=new D.Logger({verbose:e.verbose});e.verbose&&(this.info(`Starting Claude agent${e.docker?" in Docker container":""}...`),this.info(`Session: ${g} (ID: ${a})`),p.debug("Options:",e),e.llmProvider&&this.info(`LLM Routing enabled: ${e.llmProvider} → ${e.llmModel||"default model"}`));const E=new D.CredentialsService,m=new Le(p),F=e.docker?new Or({agentType:J.CLAUDE_CODE,serverUrl:r,apiKey:s,organizationId:o,verbose:e.verbose,dockerImage:e.dockerImage,logger:p,terminationService:m}):new $r({agentType:J.CLAUDE_CODE,serverUrl:r,apiUrl:i,apiKey:s,organizationId:o,verbose:e.verbose,claudePath:e.claudePath,standalone:!0,logger:p,credentialsService:E,terminationService:m});let k={workingDirectory:e.workingDirectory?D.FileSystemUtils.getAbsolutePath(e.workingDirectory):process.cwd(),args:e.args||[],sessionAlias:g,...n&&{llmConfig:n}};if(e.agentConfig)try{const c=decodeURIComponent(e.agentConfig),h=JSON.parse(c);k={...k,...h}}catch(c){try{const h=JSON.parse(e.agentConfig);k={...k,...h}}catch{this.error(`Failed to parse agent config JSON: ${c instanceof Error?c.message:String(c)}`),process.exit(1)}}const u=zt.parse({payload:{sessionId:a,agentType:J.CLAUDE_CODE,agentSessionId:a,agentConfig:k}});try{await F.launchAgent(u),this.success(`Claude agent${e.docker?" (Docker)":""} launched successfully for session: ${g}`);const c=async h=>{e.verbose&&this.info(`Received ${h}, shutting down Claude agent${e.docker?" container":""}...`);try{F.disconnect(),this.info(`Claude agent${e.docker?" container":""} shutdown complete`),process.exit(0)}catch(d){this.error(`Error during shutdown: ${d instanceof Error?d.message:String(d)}`),process.exit(1)}};process.on("SIGINT",()=>c("SIGINT")),process.on("SIGTERM",()=>c("SIGTERM")),this.info(`Claude agent${e.docker?" container":""} is running. Press Ctrl+C to stop.`),await new Promise(()=>{})}catch(c){this.error(`Failed to start Claude agent: ${c instanceof Error?c.message:String(c)}`),process.exit(1)}}}const dh=Qr.promisify(ie.exec);class gh{claudeAgentsPath;constructor(e={}){this.claudeAgentsPath=e.claudeAgentsPath||C.join(ae.homedir(),".claude","agents")}async findAgentsDirectory(){const e=C.join(process.cwd(),".claude","agents");try{if((await T.promises.stat(e)).isDirectory())return e}catch{}try{if((await T.promises.stat(this.claudeAgentsPath)).isDirectory())return this.claudeAgentsPath}catch{}try{const n=ae.homedir(),r=[`"${n}/.config"`,`"${n}/.local"`,`"${n}/workspace"`,`"${n}/projects"`,`"${n}/dev"`,`"${n}/code"`,`"${n}/.claude"`,`"${n}"`];for(const i of r)try{const o=i===`"${n}"`?1:5,a=`find ${i} -maxdepth ${o} -type d -name "agents" -path "*/.claude/agents" 2>/dev/null | head -1`,{stdout:l}=await dh(a),g=l.trim();if(g&&g.length>0&&(await T.promises.stat(g)).isDirectory())return g}catch{}}catch(n){console.error("Error searching for .claude/agents directory:",n)}return null}async listAgentFiles(){const e=await this.findAgentsDirectory();if(!e)return[];try{return(await T.promises.readdir(e)).filter(r=>r.endsWith(".md")).map(r=>C.join(e,r))}catch(n){return console.error("Error reading agents directory:",n),[]}}async parseAgentFile(e){try{const n=await T.promises.readFile(e,"utf-8");return this.parseAgentContent(n,e)}catch(n){return console.error(`Error reading agent file ${e}:`,n),null}}parseAgentContent(e,n){const r=e.split(`
|
|
77
|
+
`);for(const i of r){const s=i.trim(),o=s.match(/❯\s*(\d+)\.\s+(.+)$/);if(o){n.push({number:parseInt(o[1],10),text:o[2].trim(),selected:!0});continue}const a=s.match(/^│.*?(\d+)\.\s+(.+)$/);a&&n.push({number:parseInt(a[1],10),text:a[2].trim(),selected:!1})}return n}processDataForOptions(e){this.optionBuffer+=e;const n=we(e);(/❯\s*\d+\./m.test(n)||/Do you want to proceed\?/m.test(n)||/│.*\d+\.\s+/.test(n))&&!this.isCollectingOptions&&this.startOptionCollection()}startOptionCollection(){this.isCollectingOptions=!0,this.optionTimeout=setTimeout(async()=>{await this.processCollectedOptions()},this.OPTION_WAIT_MS)}async processCollectedOptions(){const e=we(this.optionBuffer),n=this.extractOptions(e);n.length>0&&await this.handleDetectedOptions(n),this.resetOptionState()}resetOptionState(){this.optionBuffer="",this.optionTimeout=null,this.isCollectingOptions=!1}async handleDetectedOptions(e){if(e.length===0)return;const n=new Date().toISOString();if(this.agentInfo&&this.httpService){D.SubEnvManager.isContainerMode&&await new Promise(i=>setTimeout(i,3e3));try{let i=e.map(l=>({label:l.text,value:l.number,selected:l.selected||!1}));i=this.deduplicateOptions(i);const s=D.createSelectComponent(i),o=D.createToolInvocationInput(!1,s),a=[D.createTextPart("Claude has detected available options. Please select one:"),D.createToolInvocationPart({toolName:"selectOption",toolCallId:`option-selection-${Date.now()}`,args:{timestamp:n},state:"call"},o)];await this.httpService.sendMessage({sessionId:this.agentInfo.agentSessionId,messageType:"output",parts:a,metadata:{optionDetection:!0,optionsCount:i.length,timestamp:n}})}catch(i){this.logger.error("Failed to send structured options message:",i)}}}deduplicateOptions(e){if(e.length===0)return e;const n=new Set;let r=-1;for(let a=0;a<e.length;a++){if(n.has(e[a].value)){r=a;break}n.add(e[a].value)}if(r===-1)return e;const i=e[r].value;let s=-1;for(let a=e.length-1;a>=0;a--)if(e[a].value===i){s=a;break}return e.slice(s)}verifyAgentReady(e){if(this.agentInputReady)return;const n=we(e);[/Type (your )?(message|prompt)/i,/Enter (your )?(message|prompt)/i,/Press Enter to send/i,/╭[\s\S]*?╰/m,/┌[\s\S]*?└/m].some(s=>s.test(n))&&(this.agentInputReady=!0,this.sendAgentStatus({agentType:J.CLAUDE_CODE,status:H.IDLE,message:"Agent input box detected. Ready for commands."}),this.executeStartupCommands())}async executeStartupCommands(){if(!this.startupCommandsExecuted&&this.startupCommands.length){this.startupCommandsExecuted=!0;for(const e of this.startupCommands)try{await this.sendInputToAgent(`${e}\r`),await new Promise(n=>setTimeout(n,250))}catch(n){this.logger.warn(`Failed executing startup command: ${e}`,n)}}}handleCtrlC(){return this.ctrlCCount++,this.ctrlCTimeout&&clearTimeout(this.ctrlCTimeout),this.ctrlCCount===1?(this.ctrlCTimeout=setTimeout(()=>{this.ctrlCCount=0,this.ctrlCTimeout=null},this.CTRL_C_RESET_MS),!1):this.ctrlCCount>=2?(this.ctrlCTimeout&&(clearTimeout(this.ctrlCTimeout),this.ctrlCTimeout=null),this.ctrlCCount=0,!0):!1}writeMcpConfigFile(e,n){if(!e)return null;try{const r={};if(Array.isArray(e))for(const a of e){if(!a||typeof a!="object")continue;const l=a.name||a.id||a.key;l&&(r[l]={type:a.type||"stdio",command:a.command,args:a.args||a.arguments||[],url:a.url,env:a.env||{},disabled:a.disabled??!1,headers:a.headers||{}})}else if(typeof e=="object")for(const[a,l]of Object.entries(e))l&&typeof l=="object"&&(r[a]={...l,headers:l.headers||{}});if(Object.keys(r).length===0)return null;const i={mcpServers:r},s=`claude-mcps-${n}.json`,o=C.join(ae.tmpdir(),s);return T.writeFileSync(o,JSON.stringify(i,null,2),"utf8"),this.tempMcpConfigPath=o,o}catch(r){return this.logger.warn("Failed to write MCP config file",r),null}}async discoverSubagents(e){const n=[];try{const r=C.join(e,".claude","agents");if(T.existsSync(r)){const o=T.readdirSync(r).filter(a=>a.endsWith(".md"));for(const a of o){const l=C.basename(a,".md");n.push(`${l} (project)`)}}const i=C.join(ae.homedir(),".claude","agents");if(T.existsSync(i)){const o=T.readdirSync(i).filter(a=>a.endsWith(".md"));for(const a of o){const l=C.basename(a,".md");n.some(p=>p.startsWith(`${l} (`))||n.push(`${l} (user)`)}}}catch(r){this.logger.warn("Failed to discover subagents:",r)}return n}async detectClaudeVersion(){try{const{execSync:e}=await import("child_process"),r=e(`${this.claudePath} --version`,{encoding:"utf-8"}).match(/(\d+\.\d+\.\d+)/);if(r){this.claudeVersion=r[1];const[i]=this.claudeVersion.split(".").map(Number);this.supportsSettingsFlag=i>=2,this.logger.info(`Detected Claude Code version ${this.claudeVersion} (--settings flag: ${this.supportsSettingsFlag})`)}}catch(e){this.logger.warn("Failed to detect Claude Code version, assuming legacy version",e),this.supportsSettingsFlag=!1}}async createTempSettingsFile(e){try{const n=[C.join(__dirname,"..","..","dist","claudePostToolUse.js"),C.join(__dirname,"..","dist","claudePostToolUse.js"),C.join(__dirname,"dist","claudePostToolUse.js"),C.join(process.cwd(),"dist","claudePostToolUse.js")];let r=null;for(const a of n)if(T.existsSync(a)){r=a;break}if(!r)return this.logger.warn("PostToolUse hook file not found at any expected location"),null;const i={hooks:{PostToolUse:[{matcher:"*",hooks:[{type:"command",command:`node ${r}`}]}]}},s=`claude-settings-${e}.json`,o=C.join(ae.tmpdir(),s);return await pe.writeFile(o,JSON.stringify(i,null,2),"utf8"),this.tempSettingsPath=o,this.logger.info(`Created temporary settings file at ${o}`),o}catch(n){return this.logger.error(`Failed to create temporary settings file: ${n}`),null}}async addPostToolUseHook(e){const n=C.join(e,".claude","settings.local.json"),r=C.dirname(n);try{await pe.mkdir(r,{recursive:!0});let i={};try{const g=await pe.readFile(n,"utf-8");i=JSON.parse(g)}catch{this.logger.debug("No existing settings.local.json found, creating new one")}i.hooks||(i.hooks={}),i.hooks.PostToolUse||(i.hooks.PostToolUse=[]);let s=null;const o=[C.join(__dirname,"..","..","dist","claudePostToolUse.js"),C.join(__dirname,"..","dist","claudePostToolUse.js"),C.join(__dirname,"dist","claudePostToolUse.js"),C.join(process.cwd(),"dist","claudePostToolUse.js")];for(const g of o)if(T.existsSync(g)){s=g;break}if(!s){this.logger.warn("PostToolUse hook file not found at any expected location");return}const a={matcher:"*",hooks:[{type:"command",command:`node ${s}`}]};i.hooks.PostToolUse.some(g=>g.hooks&&Array.isArray(g.hooks)?g.hooks.some(p=>p.type==="command"&&p.command===`node ${s}`):!1)||(i.hooks.PostToolUse.push(a),await pe.writeFile(n,JSON.stringify(i,null,2)),this.logger.info(`Added PostToolUse hook to ${n}`))}catch(i){this.logger.error(`Failed to add PostToolUse hook: ${i}`)}}async removePostToolUseHook(e){const n=C.join(e,".claude","settings.local.json");try{const r=await pe.readFile(n,"utf-8"),i=JSON.parse(r);if(i.hooks?.PostToolUse){let s=null;const o=[C.join(__dirname,"..","..","dist","claudePostToolUse.js"),C.join(__dirname,"..","dist","claudePostToolUse.js"),C.join(__dirname,"dist","claudePostToolUse.js"),C.join(process.cwd(),"dist","claudePostToolUse.js")];for(const l of o)if(T.existsSync(l)){s=l;break}if(!s)return;const a=`node ${s}`;i.hooks.PostToolUse=i.hooks.PostToolUse.filter(l=>l.hooks&&Array.isArray(l.hooks)?(l.hooks=l.hooks.filter(g=>!(g.type==="command"&&g.command===a)),l.hooks.length>0):!0),i.hooks.PostToolUse.length===0&&delete i.hooks.PostToolUse,Object.keys(i.hooks).length===0&&delete i.hooks,await pe.writeFile(n,JSON.stringify(i,null,2)),this.logger.info(`Removed PostToolUse hook from ${n}`)}}catch(r){this.logger.debug(`Could not remove PostToolUse hook: ${r}`)}}writeSessionSettings(e,n,r,i,s){try{const o=C.join(ae.homedir(),".agiflow"),a=C.join(o,"sessions.json");T.existsSync(o)||T.mkdirSync(o,{recursive:!0});let l={};if(T.existsSync(a))try{l=JSON.parse(T.readFileSync(a,"utf-8"))}catch{this.logger.warn("Invalid sessions.json, creating new one")}l[e]={...n&&{provider:n},...r&&{model:r},...i&&{alias:i},...s&&{reasoningEffort:s}},T.writeFileSync(a,JSON.stringify(l,null,2),"utf-8"),this.logger.info(`Session settings written to ${a}`),n&&r?this.logger.info(`Session: ${i||e} (${n}/${r})`):this.logger.info(`Session: ${i||e} (default Claude)`)}catch(o){this.logger.error("Failed to write session settings",o)}}async launchAgent(e){const{payload:n}=e,r=n.agentSessionId,i=n.agentConfig?.workingDirectory?D.FileSystemUtils.getAbsolutePath(n.agentConfig.workingDirectory):process.cwd();this.startupCommands=n.agentConfig?.commands??[],this.startupCommandsExecuted=!1;const s=n.agentConfig?.xterm??!1;this.setXtermEnabled(s);const o=n.agentConfig?.llmConfig,a=n.agentConfig?.sessionAlias;o?.provider?this.writeSessionSettings(r,o.provider,o.model||"default",a,void 0):this.writeSessionSettings(r,void 0,void 0,a,void 0);const{command:l,args:g}=this.detectClaudeType();this.claudePath=l,this.claudeArgs=g,await this.detectClaudeVersion();const p=n.agentConfig?.hooks?.preAgentStartHooks||n.agentConfig?.hooks?.preAgentStart;await this.executePreAgentStartHooks(p,i,r),D.SubEnvManager.isContainerMode&&s&&this.setFullscreen(!0),this.serverUrl?this.supportsSettingsFlag?await this.createTempSettingsFile(r):await this.addPostToolUseHook(i):this.logger.info("Running in standalone mode - PostToolUse hook will not be configured");const m=await this.discoverSubagents(i);m.length>0&&this.logger.info(`Discovered ${m.length} Claude subagent(s): ${m.join(", ")}`);const F=this.writeMcpConfigFile(n.agentConfig?.mcps,r),k=C.join(i,".mcp.json");this.serverUrl?await this.connectToSession(r):this.logger.info("Running in standalone mode - skipping WebSocket connection"),await this.setupDockerRepository(e);const u=this.buildClaudeArgs(e),c=[];T.existsSync(k)&&c.push(k),F&&c.push(F),c.length&&u.push("--mcp-config",...c);try{const h=this.getAgentEnvironmentVariables(),d=this.configureNodeOptions(h),f=process.listeners("SIGINT"),_=()=>{this.handleCtrlC()?(this.logger.info("Double Ctrl+C detected, terminating Claude agent..."),process.removeListener("SIGINT",_),f.forEach(P=>{typeof P=="function"&&process.on("SIGINT",P)}),this.ptyRunner.kill("SIGINT")):this.ptyRunner.sendInput("")};process.removeAllListeners("SIGINT"),process.on("SIGINT",_);const O=this.ptyRunner.spawn({command:this.claudePath,args:u,workingDir:i,agentSessionId:r,logger:this.logger,agentApiKey:this.apiKey,agentOrganizationId:this.organizationId,agentServerUrl:this.serverUrl,agentType:this.agentType,env:{...h,NODE_OPTIONS:d,[D.ENV_KEYS.AGENT_SESSION_ID]:r,[D.ENV_KEYS.AGENT_TYPE]:this.agentType,...F?{CLAUDE_MCP_CONFIG_FILE:F}:{},...n.data?{[D.ENV_KEYS.CONTEXT_DATA]:n.data}:{}}},{onExit:async(x,P)=>{if(process.removeListener("SIGINT",_),f.forEach(M=>{typeof M=="function"&&process.on("SIGINT",M)}),this.ctrlCTimeout&&(clearTimeout(this.ctrlCTimeout),this.ctrlCTimeout=null),this.optionTimeout&&(clearTimeout(this.optionTimeout),this.resetOptionState()),this.serverUrl)if(this.supportsSettingsFlag){if(this.tempSettingsPath){try{T.unlinkSync(this.tempSettingsPath),this.logger.info(`Removed temporary settings file: ${this.tempSettingsPath}`)}catch(M){this.logger.warn("Failed to delete temp settings file",M)}this.tempSettingsPath=null}}else await this.removePostToolUseHook(i);if(this.tempMcpConfigPath){try{T.unlinkSync(this.tempMcpConfigPath)}catch(M){this.logger.warn("Failed to delete temp MCP config file",M)}this.tempMcpConfigPath=null}this.logger.info(`Claude Code PTY exited with code ${x}, signal: ${P}`),this.sendAgentStatus({agentType:J.CLAUDE_CODE,status:x===0?H.STOPPED:H.ERROR,message:`Claude Code session ended (exit code: ${x})`}),this.wsService&&this.wsService.disconnect(),this.ptySessionId=null,this.agentInfo&&await this.terminationService.handlePtyTermination({agentSessionId:this.agentInfo.agentSessionId,reason:P===2?"agent_sigint":"natural",exitCode:x,signal:P})},onData:async x=>{this.processDataForOptions(x),this.verifyAgentReady(x),this.xtermEnabled&&this.sendStdoutEvent(x)}});this.logger.success(`Claude Code launched successfully for session agentSessionId: ${r}`),this.ptySessionId=O.id,s?this.resizePty(80,24):this.setupInitialPtySize();const A={agentSessionId:r,agentType:J.CLAUDE_CODE,status:H.IDLE,workingDir:i,process:O.ptyProcess,metadata:{agentSessionId:r,agentType:n.agentType,config:n.agentConfig,ptySessionId:O.id}};return this.setAgentInfo(A),A}catch(h){throw new Error(`Failed to launch Claude Code: ${h instanceof Error?h.message:String(h)}`)}}async isAvailable(){return!0}async getVersion(){return"claude-pty-agent"}buildClaudeArgs(e){const n=[...this.claudeArgs,...e.payload?.agentConfig?.args||[]],r=e.payload?.agentConfig?.conversationSessionId;return r&&n.push("--session-id",r),this.supportsSettingsFlag&&this.tempSettingsPath&&n.push("--settings",this.tempSettingsPath),n}}class lh{session;logger;constructor(e){this.logger=e??new D.Logger({verbose:!1})}async isDockerAvailable(){return new Promise(e=>{const n=ie.spawn("docker",["--version"],{stdio:"pipe"});n.on("close",r=>{e(r===0)}),n.on("error",()=>{e(!1)})})}spawn(e,n){const r=e.agentSessionId||xn.ulid(),i=e.workingDir??process.cwd(),s=e.dockerImage||"agiflow/claude-agent:latest",o=this.buildDockerArgs(r,e,s),a=`docker ${o.join(" ")}`;this.logger.info(`🐳 Docker Command: ${a}`),this.logger.debug("Spawning Docker container with args:",o);const l=ie.spawn("docker",o,{stdio:["pipe","pipe","pipe"]}),g=`claude-agent-${r}`,p={id:r,dockerProcess:l,containerId:g,command:`docker ${o.join(" ")}`,workingDir:i,status:"running",createdAt:new Date,lastActivity:new Date,logStream:e.logStream};return l.stdout?.on("data",E=>{p.lastActivity=new Date;const m=E.toString();p.logStream&&p.logStream.write(m),process.stdout.write(m),n?.onData&&n.onData(m)}),l.stderr?.on("data",E=>{const m=E.toString();this.logger.error(`🐳 Container ${g} stderr: ${m.trim()}`),process.stderr.write(m)}),l.on("close",E=>{p.status=E===0?"stopped":"error",p.lastActivity=new Date,this.logger.info(`Container ${g} exited with code ${E}`),p.logStream&&p.logStream.end(),n?.onExit&&n.onExit(E||0,void 0)}),l.on("error",E=>{p.status="error",this.logger.error(`Container ${g} error:`,E),n?.onError&&n.onError(E)}),this.session=p,p}buildDockerArgs(e,n,r){const s=["run","--rm","--name",`claude-agent-${e}`,"-i"];if(n.privileged&&s.push("--privileged"),n.user&&s.push("--user",n.user),n.networkMode&&s.push("--network",n.networkMode),n.ports&&n.ports.length>0)for(const m of n.ports)s.push("-p",`${m.host}:${m.container}`);const o=n.agentServerUrl||D.SubEnvManager.serverUrl;s.push("-e",`${D.ENV_KEYS.AGENT_SERVER_URL}=${o}`);const a=n.agentApiKey||D.SubEnvManager.apiKey;a&&s.push("-e",`${D.ENV_KEYS.AGENT_API_KEY}=${a}`);const l=n.agentOrganizationId||D.SubEnvManager.organizationId;l&&s.push("-e",`${D.ENV_KEYS.AGENT_ORGANIZATION_ID}=${l}`);const g=n.agentType||D.SubEnvManager.agentType;g&&s.push("-e",`${D.ENV_KEYS.AGENT_TYPE}=${g}`);const p=e||D.SubEnvManager.sessionId;if(p&&s.push("-e",`${D.ENV_KEYS.AGENT_SESSION_ID}=${p}`),n.additionalEnvVars)for(const[m,F]of Object.entries(n.additionalEnvVars))s.push("-e",`${m}=${F}`);n.workingDir&&(s.push("-v",`${n.workingDir}:/workspace`),s.push("-w","/workspace"));const E=C.join(ae.homedir(),".agiflow");if(ei.existsSync(E)?(s.push("-v",`${E}:/home/node/.agiflow:ro`),this.logger.debug(`Mounting credentials directory: ${E} -> /home/node/.agiflow`)):this.logger.warn(`Credentials directory not found at: ${E}`),n.additionalVolumes&&n.additionalVolumes.length>0)for(const m of n.additionalVolumes){let F;m.type==="volume"?(F=m.readonly?`${m.host}:${m.container}:ro`:`${m.host}:${m.container}`,this.logger.debug(`Mounting named volume: ${m.host} -> ${m.container}`)):(F=m.readonly?`${m.host}:${m.container}:ro`:`${m.host}:${m.container}`,this.logger.debug(`Mounting bind volume: ${m.host} -> ${m.container}`)),s.push("-v",F)}if(D.SubEnvManager.isDebugMode&&s.push("-v","/var/run/docker.sock:/var/run/docker.sock"),s.push(r),s.push("claude"),s.push("--server-url",o),n.agentApiUrl&&s.push("--api-url",n.agentApiUrl),a&&s.push("--api-key",a),l&&s.push("--organization-id",l),p&&s.push("--agent-session-id",p),n.workingDir&&s.push("--working-directory","/workspace"),n.agentConfig){const m=encodeURIComponent(n.agentConfig);s.push("--agent-config",m)}return n.verbose&&s.push("--verbose"),n.args&&n.args.length>0&&s.push("--args",...n.args),s}sendInput(e){if(!this.session?.dockerProcess?.stdin)return this.logger.warn("No active Docker container to send input to"),!1;try{return this.session.dockerProcess.stdin.write(e),this.session.lastActivity=new Date,!0}catch(n){return this.logger.error("Failed to send input to Docker container:",n),!1}}kill(e){if(!this.session?.dockerProcess)return this.logger.warn("No active Docker container to kill"),!1;try{return this.logger.info(`Stopping Docker container: ${this.session.containerId}`),this.session.dockerProcess.kill(e||"SIGTERM"),setTimeout(()=>{this.session.dockerProcess&&!this.session.dockerProcess.killed&&(this.logger.warn(`Force killing container ${this.session.containerId}`),this.session.dockerProcess.kill("SIGKILL"))},5e3),this.session.status="stopped",!0}catch(n){return this.logger.error(`Failed to kill Docker container: ${n}`),!1}}getSession(){return this.session}isSessionRunning(){return this.session?.status==="running"}}class hh{agentType;serverUrl;apiKey;verbose;organizationId;logger;terminationService;dockerImage;onFullscreenChange;dockerRunner;agentInfo=null;isFullscreen=!1;backgroundOutputBuffer=[];BACKGROUND_BUFFER_SIZE=5;BACKGROUND_CHUNK_MAX_SIZE=2048;constructor(e){this.agentType=e.agentType,this.serverUrl=e.serverUrl,this.apiKey=e.apiKey,this.verbose=e.verbose??!1,this.organizationId=e.organizationId,this.logger=e.logger??new D.Logger({verbose:this.verbose}),this.terminationService=e.terminationService??new Le(this.logger),this.dockerImage=e.dockerImage||"agiflow/agent:latest",this.onFullscreenChange=e.onFullscreenChange,this.dockerRunner=new lh(this.logger)}async isAvailable(){return await this.dockerRunner.isDockerAvailable()}getAgentInfo(){return this.agentInfo}setAgentInfo(e){this.agentInfo=e}getAgentEnvironmentVariables(){const e={};for(const[n,r]of Object.entries(process.env))r!==void 0&&(e[n]=r);return this.apiKey&&(e[D.ENV_KEYS.AGENT_API_KEY]=this.apiKey),this.organizationId&&(e[D.ENV_KEYS.AGENT_ORGANIZATION_ID]=this.organizationId),this.serverUrl&&(e[D.ENV_KEYS.AGENT_SERVER_URL]=this.serverUrl),e[D.ENV_KEYS.AGENT_TYPE]=this.agentType,e[D.ENV_KEYS.VERBOSE]=this.verbose?"true":"false",e}addToBackgroundBuffer(e){const n=e.length>this.BACKGROUND_CHUNK_MAX_SIZE?e.substring(e.length-this.BACKGROUND_CHUNK_MAX_SIZE):e;this.backgroundOutputBuffer.push(n),this.backgroundOutputBuffer.length>this.BACKGROUND_BUFFER_SIZE&&this.backgroundOutputBuffer.shift(),this.logger.debug(`🐳 Added ${n.length} chars to Docker background buffer (${this.backgroundOutputBuffer.length}/${this.BACKGROUND_BUFFER_SIZE} chunks)`)}flushBackgroundBuffer(){if(this.backgroundOutputBuffer.length===0)return;this.logger.debug(`🐳 Flushing Docker background buffer: ${this.backgroundOutputBuffer.length} chunks`);const e=this.backgroundOutputBuffer.join("");e&&(console.log(e),this.logger.debug(`🐳 Docker background buffer flushed: ${e.length} chars`)),this.backgroundOutputBuffer=[]}setFullscreen(e,n=!1){const r=this.isFullscreen;this.isFullscreen=e,this.logger.debug(`Docker agent fullscreen mode ${e?"enabled":"disabled"}`),e&&!r&&this.agentInfo&&this.flushBackgroundBuffer(),!n&&r!==e&&this.agentInfo&&this.onFullscreenChange&&this.onFullscreenChange(this.agentInfo.agentSessionId,e)}getFullscreen(){return this.isFullscreen}disconnect(){this.logger.info(`Disconnecting ${this.agentType} Docker agent`),this.dockerRunner.kill("SIGTERM"),this.agentInfo=null}}class Or extends hh{onBeforeTerminate;constructor(e){super({...e,agentType:J.CLAUDE_CODE,dockerImage:e.dockerImage||ue.VITE_AGENT_CLI_DOCKER_IMAGE}),this.onBeforeTerminate=e.onBeforeTerminate}async getVersion(){return"claude-docker-agent"}async launchAgent(e){const{payload:n}=e,r=n.agentSessionId,i=n.agentConfig?.workingDirectory?D.FileSystemUtils.getAbsolutePath(n.agentConfig.workingDirectory):process.cwd();if(!await this.isAvailable())throw new Error("Docker is not available. Please install Docker to use containerized agents.");this.logger.info(`Launching Claude agent in Docker container for session: ${r}`);const s=this.getDockerOptions(e);try{const o=this.dockerRunner.spawn(s,{onExit:async(l,g)=>{if(this.logger.info(`Docker Claude agent exited with code ${l}, signal: ${g}`),this.onBeforeTerminate)try{this.logger.info("Executing cleanup callback before termination..."),await this.onBeforeTerminate(),this.logger.info("Cleanup callback completed successfully")}catch(p){this.logger.error("Error during cleanup callback:",p)}this.agentInfo&&await this.terminationService.handlePtyTermination({agentSessionId:this.agentInfo.agentSessionId,reason:g===2?"agent_sigint":"natural",exitCode:l,signal:g})},onData:async l=>{this.logger.debug("Docker container output:",l)},onError:l=>{this.logger.error("Docker container error:",l)}});this.logger.success(`Docker Claude agent launched successfully for session: ${r}`);const a={agentSessionId:r,agentType:J.CLAUDE_CODE,status:H.IDLE,workingDir:i,containerId:o.containerId,dockerSessionId:o.id,process:o.dockerProcess,metadata:{agentSessionId:r,agentType:n.agentType,config:n.agentConfig,docker:!0,containerId:o.containerId,dockerSessionId:o.id}};return this.setAgentInfo(a),a}catch(o){throw new Error(`Failed to launch Claude Docker agent: ${o instanceof Error?o.message:String(o)}`)}}disconnect(){this.logger.info("Disconnecting Claude Docker agent");try{this.dockerRunner.isSessionRunning()&&(this.dockerRunner.kill("SIGTERM"),setTimeout(()=>{this.dockerRunner.isSessionRunning()&&(this.logger.warn("Force killing Docker container..."),this.dockerRunner.kill("SIGKILL"))},3e3)),this.agentInfo=null,this.logger.info("Claude Docker agent disconnected successfully")}catch(e){this.logger.error("Error during Docker agent disconnect:",e)}}getDockerOptions(e){const{payload:n}=e,r=n.agentSessionId,i=n.agentConfig?.workingDirectory?D.FileSystemUtils.getAbsolutePath(n.agentConfig.workingDirectory):process.cwd(),s=ue.VITE_INJECT_AGIFLOW_APP_DOCKER_ENDPOINT||this.serverUrl,o=s?s.replace(/^http/,"ws"):void 0;let a=null;n.agentConfig?.mcps&&(a=this.processAgentConfig(n.agentConfig.mcps));const l={...n.agentConfig,workingDirectory:"/workspace"};return a&&(l.mcps=a),{agentSessionId:r,dockerImage:n.agentConfig?.dockerImage||this.dockerImage,workingDir:i,agentApiKey:this.apiKey,agentOrganizationId:this.organizationId,agentServerUrl:o,agentApiUrl:s,agentType:J.CLAUDE_CODE,agentConfig:JSON.stringify(l),verbose:this.verbose,additionalEnvVars:{[D.ENV_KEYS.NODE_ENV]:D.SubEnvManager.nodeEnv,[D.ENV_KEYS.AGENT_SESSION_ID]:r,[D.ENV_KEYS.AGENT_CONTAINER_MODE]:D.SubEnvManager.isContainerMode?"true":"false",IS_SANDBOX:"1"},additionalVolumes:this.getAdditionalVolumes(n),networkMode:ue.VITE_AGENT_CLI_DOCKER_NETWORK,user:void 0}}getAdditionalVolumes(e){const n=[],r=D.SubEnvManager.homeDir;if(r)if(n.push({host:`${r}/.ssh`,container:"/root/.ssh",readonly:!0,type:"bind"}),n.push({host:"agiflow-pnpm-cache",container:"/root/.pnpm-store",readonly:!1,type:"volume"}),n.push({host:"agiflow-npm-cache",container:"/root/.npm",readonly:!1,type:"volume"}),n.push({host:"agiflow-node-gyp-cache",container:"/root/.cache/node-gyp",readonly:!1,type:"volume"}),D.SubEnvManager.isMacOS){const s=`${r}/.claude_tmp.json`;T.existsSync(s)||T.writeFileSync(s,"{}","utf8"),n.push({host:s,container:"/root/.claude.json",readonly:!1,type:"bind"});const o=`${r}/.claude_tmp`;T.existsSync(o)||T.mkdirSync(o,{recursive:!0}),n.push({host:o,container:"/root/.claude",readonly:!1,type:"bind"})}else{const s=`${r}/.claude.json`;T.existsSync(s)||T.writeFileSync(s,"{}","utf8"),n.push({host:s,container:"/root/.claude.json",readonly:!1,type:"bind"});const o=`${r}/.claude`;T.existsSync(o)||T.mkdirSync(o,{recursive:!0}),n.push({host:o,container:"/root/.claude",readonly:!1,type:"bind"})}return n}async sendInputToAgent(e){this.logger.debug("sendInputToAgent called on Docker agent - no action needed")}getContainerStatus(){const e=this.dockerRunner.getSession();return{isRunning:this.dockerRunner.isSessionRunning(),containerId:e?.containerId,status:e?.status,lastActivity:e?.lastActivity}}async isHealthy(){try{return!!this.dockerRunner.isSessionRunning()}catch(e){return this.logger.error("Health check failed:",e),!1}}getDockerSession(){return this.dockerRunner.getSession()}processAgentConfig(e){const n={};if(Array.isArray(e))for(const r of e){if(!r||typeof r!="object")continue;const i=r.name||r.id||r.key;i&&(n[i]={type:r.type||"stdio",command:r.command,args:r.args||r.arguments||[],url:r.url,env:r.env||{},disabled:r.disabled??!1,headers:r.headers||{}})}else if(typeof e=="object")for(const[r,i]of Object.entries(e))i&&typeof i=="object"&&(n[r]={...i,headers:i.headers||{}});return n}}class xr extends ze{register(e){e.command("claude").description("Launch Claude Code agent with full functionality").option("-s, --server-url <url>","WebSocket server URL").option("-a, --api-url <url>","HTTP API URL").option("-k, --api-key <key>","API key for authentication").option("-o, --organization-id <id>","Organization ID").option("-v, --verbose","Enable verbose logging",!1).option("-p, --claude-path <path>","Path to Claude executable","claude").option("-w, --working-directory <dir>","Working directory",process.cwd()).option("-i, --agent-session-id <id>","Agent session ID").option("-c, --agent-config <json>","Agent configuration as JSON string").option("--args <args...>","Additional arguments to pass to Claude").option("--docker","Run Claude agent in Docker container",!1).option("--docker-image <image>","Docker image to use for containerized agent").option("--llm-provider <provider>","LLM provider to route requests to (e.g., openai)").option("--llm-model <model>","LLM model to use (e.g., gpt-4-turbo, gpt-5)").option("--alias <alias>","Custom session alias for easy identification").option("--standalone","Run in standalone mode without backend connection",!1).action(async n=>{try{await this.execute(n)}catch(r){this.handleError(r)}})}async execute(e){let n;if(e.llmProvider){const c=["codex","gpt","openai","chatgpt"],h=e.llmProvider.toLowerCase();c.includes(h)||(this.error(`Invalid LLM provider: ${e.llmProvider}`),this.error(`Allowed providers: ${c.join(", ")}`),process.exit(1)),n={provider:h,model:e.llmModel}}const r=e.standalone?void 0:e.serverUrl||ue.VITE_INJECT_AGIFLOW_APP_ENDPOINT,i=e.standalone?void 0:e.apiUrl||ue.VITE_INJECT_AGIFLOW_APP_ENDPOINT,s=e.standalone?void 0:e.apiKey||D.SubEnvManager.apiKey,o=e.standalone?void 0:e.organizationId||D.SubEnvManager.organizationId;let a=e.agentSessionId||D.SubEnvManager.sessionId;if(a||(a=On.randomUUID(),e.verbose&&this.info(`Generated session ID: ${a}`)),!r)e.verbose&&(e.standalone?this.info("Running in standalone mode (--standalone flag)"):this.info("Running in standalone mode (no server URL configured)"),this.info("Agent will launch without remote connectivity"));else try{new URL(r)}catch{this.error("Invalid server URL. Must be a valid WebSocket URL (ws:// or wss://)."),process.exit(1)}if(i)try{new URL(i)}catch{this.error("Invalid API URL. Must be a valid HTTP URL."),process.exit(1)}else!r&&e.verbose&&this.info("No API URL provided - HTTP features will be unavailable");const l=()=>{const h=new Date().toISOString().replace(/[-:]/g,"").replace("T","-").split(".")[0],d=e.llmProvider||"claude",f=e.llmModel?.replace(/[^a-z0-9]/gi,"")||"default";return`${d}-${f}-${h}`},g=e.alias||l(),p=new D.Logger({verbose:e.verbose});e.verbose&&(this.info(`Starting Claude agent${e.docker?" in Docker container":""}...`),this.info(`Session: ${g} (ID: ${a})`),p.debug("Options:",e),e.llmProvider&&this.info(`LLM Routing enabled: ${e.llmProvider} → ${e.llmModel||"default model"}`));const E=new D.CredentialsService,m=new Le(p),F=e.docker?new Or({agentType:J.CLAUDE_CODE,serverUrl:r,apiKey:s,organizationId:o,verbose:e.verbose,dockerImage:e.dockerImage,logger:p,terminationService:m}):new $r({agentType:J.CLAUDE_CODE,serverUrl:r,apiUrl:i,apiKey:s,organizationId:o,verbose:e.verbose,claudePath:e.claudePath,standalone:!0,logger:p,credentialsService:E,terminationService:m});let k={workingDirectory:e.workingDirectory?D.FileSystemUtils.getAbsolutePath(e.workingDirectory):process.cwd(),args:e.args||[],sessionAlias:g,...n&&{llmConfig:n}};if(e.agentConfig)try{const c=decodeURIComponent(e.agentConfig),h=JSON.parse(c);k={...k,...h}}catch(c){try{const h=JSON.parse(e.agentConfig);k={...k,...h}}catch{this.error(`Failed to parse agent config JSON: ${c instanceof Error?c.message:String(c)}`),process.exit(1)}}const u=zt.parse({payload:{sessionId:a,agentType:J.CLAUDE_CODE,agentSessionId:a,agentConfig:k}});try{await F.launchAgent(u),this.success(`Claude agent${e.docker?" (Docker)":""} launched successfully for session: ${g}`);const c=async h=>{e.verbose&&this.info(`Received ${h}, shutting down Claude agent${e.docker?" container":""}...`);try{F.disconnect(),this.info(`Claude agent${e.docker?" container":""} shutdown complete`),process.exit(0)}catch(d){this.error(`Error during shutdown: ${d instanceof Error?d.message:String(d)}`),process.exit(1)}};process.on("SIGINT",()=>c("SIGINT")),process.on("SIGTERM",()=>c("SIGTERM")),this.info(`Claude agent${e.docker?" container":""} is running. Press Ctrl+C to stop.`),await new Promise(()=>{})}catch(c){this.error(`Failed to start Claude agent: ${c instanceof Error?c.message:String(c)}`),process.exit(1)}}}const dh=Qr.promisify(ie.exec);class gh{claudeAgentsPath;constructor(e={}){this.claudeAgentsPath=e.claudeAgentsPath||C.join(ae.homedir(),".claude","agents")}async findAgentsDirectory(){const e=C.join(process.cwd(),".claude","agents");try{if((await T.promises.stat(e)).isDirectory())return e}catch{}try{if((await T.promises.stat(this.claudeAgentsPath)).isDirectory())return this.claudeAgentsPath}catch{}try{const n=ae.homedir(),r=[`"${n}/.config"`,`"${n}/.local"`,`"${n}/workspace"`,`"${n}/projects"`,`"${n}/dev"`,`"${n}/code"`,`"${n}/.claude"`,`"${n}"`];for(const i of r)try{const o=i===`"${n}"`?1:5,a=`find ${i} -maxdepth ${o} -type d -name "agents" -path "*/.claude/agents" 2>/dev/null | head -1`,{stdout:l}=await dh(a),g=l.trim();if(g&&g.length>0&&(await T.promises.stat(g)).isDirectory())return g}catch{}}catch(n){console.error("Error searching for .claude/agents directory:",n)}return null}async listAgentFiles(){const e=await this.findAgentsDirectory();if(!e)return[];try{return(await T.promises.readdir(e)).filter(r=>r.endsWith(".md")).map(r=>C.join(e,r))}catch(n){return console.error("Error reading agents directory:",n),[]}}async parseAgentFile(e){try{const n=await T.promises.readFile(e,"utf-8");return this.parseAgentContent(n,e)}catch(n){return console.error(`Error reading agent file ${e}:`,n),null}}parseAgentContent(e,n){const r=e.split(`
|
|
78
78
|
`),i=r.findIndex(p=>p.trim()==="---"),s=r.findIndex((p,E)=>E>i&&p.trim()==="---");if(i===-1||s===-1)return console.error(`Invalid frontmatter in ${n}`),null;const o=r.slice(i+1,s),a={};for(const p of o){const E=p.indexOf(":");if(E>0){const m=p.substring(0,E).trim(),F=p.substring(E+1).trim();a[m]=F}}const g=r.slice(s+1).join(`
|
|
79
79
|
`).trim();return!a.name||!a.description?(console.error(`Missing required fields in ${n}`),null):{name:a.name,description:a.description,color:a.color||"default",prompt:g,filePath:n}}async getAllAgentProfiles(){const e=await this.listAgentFiles(),n=[];for(const r of e){const i=await this.parseAgentFile(r);i&&n.push(i)}return n}async getAgentProfile(e){return(await this.getAllAgentProfiles()).find(r=>r.name===e)||null}async searchAgentProfiles(e){const n=await this.getAllAgentProfiles(),r=e.toLowerCase();return n.filter(i=>i.name.toLowerCase().includes(r)||i.description.toLowerCase().includes(r))}}class Mt{options;apiKeyPath;logger;constructor(e){this.options=e,this.logger=e.logger||new D.Logger({verbose:e.verbose||!1}),this.apiKeyPath=C.join(D.FileSystemUtils.AGIFLOW_DIR,"api-key.json")}async getValidApiKey(){try{const e=await this.loadApiKey();if(e&&this.isTokenValid(e.expires_at))return this.logger.info("Using existing API key"),e;this.logger.info("Starting device code authentication flow...");const n=await this.performDeviceCodeFlow();return await this.storeApiKey(n),n}catch(e){throw new Error(`Authentication failed: ${e instanceof Error?e.message:e}`)}}async performDeviceCodeFlow(){const e=await fetch(`${this.options.baseUrl}/api/v1/oauth/device/authorize`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({client_id:this.options.clientId,scope:"openid profile email"})});if(!e.ok){const l=await e.text();throw new Error(`Device authorization failed: ${e.status} ${l}`)}const n=await e.json(),r=`${n.verification_uri}?code=${encodeURIComponent(n.user_code)}`;this.logger.info("To authenticate your CLI:"),this.logger.info(`1. Open (or we opened): ${r}`),this.logger.info("2. Sign in (if needed) and confirm device authorization"),this.logger.info("3. Copy the verification token (JWT) shown after confirmation"),this.logger.info("4. Paste it below to complete authentication"),this.logger.info(""),this.logger.info(`User Code: ${n.user_code}`);const i=await et.input({message:"Paste verification token (JWT):",validate:l=>!l||l.trim().split(".").length!==3?"Invalid verification token format. Please enter a valid JWT token.":!0}),s=await fetch(`${this.options.baseUrl}/api/v1/oauth/device/token`,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"urn:ietf:params:oauth:grant-type:device_code",device_code:n.device_code,verification_token:i.trim(),client_id:this.options.clientId})});if(!s.ok){const l=await s.text();throw new Error(`Token exchange failed: ${s.status} ${l}`)}const o=await s.json();if(!o.api_key)throw new Error("Token exchange response missing api_key");if(!o.organization_id)throw new Error("Token exchange response missing organization_id");this.logger.success(`Authentication successful!
|
|
80
80
|
`);const a={api_key:o.api_key,key_id:"device-flow",expires_at:Date.now()+o.expires_in*1e3,organization_id:o.organization_id,prefix:"agiflow_"};return await this.checkAndOfferAgentProfiles(a),a}async loadApiKey(){try{return await D.FileSystemUtils.exists(this.apiKeyPath)?await D.FileSystemUtils.readJson(this.apiKeyPath):null}catch{return null}}async storeApiKey(e){await this.ensureAgiflowDir(),await D.FileSystemUtils.writeJson(this.apiKeyPath,e),this.logger.debug(`API key stored at ${this.apiKeyPath}`)}async ensureAgiflowDir(){await D.FileSystemUtils.ensureDir(D.FileSystemUtils.AGIFLOW_DIR)}isTokenValid(e){return Date.now()<e-3e5}async clearTokens(){try{await D.FileSystemUtils.exists(this.apiKeyPath)&&await D.FileSystemUtils.remove(this.apiKeyPath)}catch{}this.logger.debug("Cleared stored tokens")}async getOrganizationId(){return(await this.loadApiKey())?.organization_id||null}async getDeviceId(){return(await this.loadApiKey())?.device_id||null}async updateDeviceId(e){const n=await this.loadApiKey();n&&(n.device_id=e,await this.storeApiKey(n),this.logger.debug(`Updated device ID in stored API key: ${e}`))}async checkAndOfferAgentProfiles(e){try{this.logger.info("Checking for local agent profiles...");const r=await new gh().getAllAgentProfiles();if(r.length===0){this.logger.info("No local agent profiles found.");return}this.logger.info(`Found ${r.length} local agent profile(s):`),r.forEach((s,o)=>{this.logger.info(` ${o+1}. ${s.name} - ${s.description.substring(0,80)}...`)}),await et.confirm({message:"Would you like to add these agent profiles to your organization?",default:!1})?await this.uploadAgentProfiles(r,e):this.logger.info("Skipping agent profile upload.")}catch(n){this.logger.warn(`Could not check agent profiles: ${n instanceof Error?n.message:n}`)}}async uploadAgentProfiles(e,n){this.logger.info("Uploading agent profiles to organization...");try{const r=await fetch(`${this.options.baseUrl}/api/v1/organizations/${n.organization_id}/members/agents`,{method:"POST",headers:{"Content-Type":"application/json","x-api-key":n.api_key},body:JSON.stringify({agents:e.map(i=>({name:i.name,description:i.description,prompt:i.prompt}))})});if(r.ok){const i=await r.json(),s=i.created?.length||0,o=i.errors?.length||0;s>0&&(this.logger.success(`Successfully uploaded ${s} agent profile(s) to your organization!`),i.created.forEach(a=>{this.logger.info(`✅ Created: ${a.user.name}`)})),o>0&&(this.logger.warn(`Failed to upload ${o} agent profile(s):`),i.errors.forEach(a=>{this.logger.warn(`❌ ${a.name}: ${a.error}`)}))}else{const i=await r.text();this.logger.warn(`❌ Failed to upload agent profiles: ${r.status} ${i}`)}}catch(r){this.logger.warn(`❌ Error uploading agent profiles: ${r instanceof Error?r.message:r}`)}}}class fh{permits;waitQueue=[];constructor(e=1){this.permits=e}async acquire(){return this.permits>0?(this.permits--,Promise.resolve()):new Promise(e=>{this.waitQueue.push(e)})}release(){if(this.waitQueue.length>0){const e=this.waitQueue.shift();e&&e()}else this.permits++}async runExclusive(e){await this.acquire();try{return await e()}finally{this.release()}}get available(){return this.permits}get waiting(){return this.waitQueue.length}}class ph{semaphores=new Map;permits;constructor(e=1){this.permits=e}getSemaphore(e){let n=this.semaphores.get(e);return n||(n=new fh(this.permits),this.semaphores.set(e,n)),n}async acquire(e){return this.getSemaphore(e).acquire()}release(e){const n=this.semaphores.get(e);n&&(n.release(),n.available===this.permits&&n.waiting===0&&this.semaphores.delete(e))}async runExclusive(e,n){return this.getSemaphore(e).runExclusive(n)}isLocked(e){const n=this.semaphores.get(e);return n?n.available===0:!1}clear(){this.semaphores.clear()}}class mh extends Ir{geminiPath;tempExtensionConfigPath=null;agentInputReady=!1;optionBuffer="";optionTimeout=null;isCollectingOptions=!1;OPTION_WAIT_MS=1e3;constructor(e){super({...e,agentType:J.GEMINI_CLI}),this.geminiPath=e.geminiPath||"gemini"}async launchAgent(e){const{payload:n}=e,r=n.agentSessionId,i=n.agentConfig?.workingDirectory?D.FileSystemUtils.getAbsolutePath(n.agentConfig.workingDirectory):process.cwd(),s=n.agentConfig?.xterm??!1;this.setXtermEnabled(s);const o=n.agentConfig?.hooks?.preAgentStartHooks||n.agentConfig?.hooks?.preAgentStart;await this.executePreAgentStartHooks(o,i,r),this.logger.debug("Agent config MCPs:",n.agentConfig?.mcps);const a=await this.writeGeminiExtensionFile(n.agentConfig?.mcps,i);this.logger.debug("Custom Gemini extension config result:",a);const l=C.join(i,"agiflow-extension.json");this.logger.debug("Project Gemini extension config path:",l),await this.connectToSession(r),await this.setupDockerRepository(e);const g=this.buildGeminiArgs(e);T.existsSync(l)?this.logger.debug(`Found project Gemini extension config: ${l}`):this.logger.debug("No project agiflow-extension.json found in working directory"),a&&this.logger.debug(`Created Gemini extension config: ${a}`);try{const p=this.getAgentEnvironmentVariables(),E=this.configureNodeOptions(p),m=this.ptyRunner.spawn({command:this.geminiPath,args:g,workingDir:i,agentSessionId:r,logger:this.logger,agentApiKey:this.apiKey,agentOrganizationId:this.organizationId,agentServerUrl:this.serverUrl,agentType:this.agentType,env:{...p,NODE_OPTIONS:E,GEMINI_SESSION_ID:r,GEMINI_AGENT_SESSION_ID:r,GEMINI_AGENT_MODE:"true",...a?{GEMINI_EXTENSION_CONFIG_FILE:a}:{}}},{onData:async k=>{this.verifyAgentReady(k),this.processDataForOptions(k),this.xtermEnabled&&this.sendStdoutEvent(k)},onExit:async(k,u)=>{if(this.tempExtensionConfigPath){try{T.unlinkSync(this.tempExtensionConfigPath),this.logger.debug(`Deleted temp Gemini extension config file: ${this.tempExtensionConfigPath}`)}catch(c){this.logger.warn("Failed to delete temp Gemini extension config file",c)}this.tempExtensionConfigPath=null}this.logger.info(`Gemini PTY exited with code ${k}, signal: ${u}`),this.sendAgentStatus({agentType:J.GEMINI_CLI,status:k===0?H.STOPPED:H.ERROR,message:`Gemini session ended (exit code: ${k})`}),this.wsService&&this.wsService.disconnect(),this.ptySessionId=null,this.agentInfo&&await this.terminationService.handlePtyTermination({agentSessionId:this.agentInfo.agentSessionId,reason:"natural",exitCode:k,signal:u})}});this.logger.success(`Gemini launched successfully for session agentSessionId: ${r}`),this.ptySessionId=m.id,s?this.resizePty(80,24):this.setupInitialPtySize();const F={agentSessionId:r,agentType:J.GEMINI_CLI,status:H.IDLE,workingDir:i,process:m.ptyProcess,metadata:{agentSessionId:r,agentType:n.agentType,config:n.agentConfig,ptySessionId:m.id}};return this.setAgentInfo(F),F}catch(p){throw new Error(`Failed to launch Gemini: ${p instanceof Error?p.message:String(p)}`)}}async isAvailable(){return!0}async getVersion(){return"gemini-pty-agent"}verifyAgentReady(e){if(this.agentInputReady)return;const r=(o=>o.replace(/\x1b\[[0-9;]*m/g,""))(e);[/Type (your )?(message|prompt)/i,/Enter (your )?(message|command)/i,/gemini>/i,/Ready for input/i,/Session started/i,/╭[\s\S]*?╰/m,/┌[\s\S]*?└/m,/>/].some(o=>o.test(r))&&(this.agentInputReady=!0,this.logger.debug("Gemini agent detected as ready for input"),this.sendAgentStatus({agentType:J.GEMINI_CLI,status:H.IDLE,message:"Gemini ready for commands"}))}buildGeminiArgs(e){const n=e.payload?.agentConfig?.args||[],r=e.payload?.agentConfig?.conversationSessionId;return r&&n.push("--session-id",r),n}processDataForOptions(e){this.optionBuffer+=e;const n=we(e);(/Do you want to proceed\?/m.test(n)||/●\s*\d+\.\s+/.test(n)||/\n\s{0,4}\d+\.\s+[^\n]+/.test(n))&&!this.isCollectingOptions&&this.startOptionCollection()}startOptionCollection(){this.isCollectingOptions=!0,this.optionTimeout&&clearTimeout(this.optionTimeout),this.optionTimeout=setTimeout(()=>{this.processCollectedOptions()},this.OPTION_WAIT_MS)}async processCollectedOptions(){const e=we(this.optionBuffer),n=this.extractOptions(e);n.length&&await this.handleDetectedOptions(n),this.resetOptionState()}resetOptionState(){this.optionBuffer="",this.optionTimeout=null,this.isCollectingOptions=!1}extractOptions(e){const n=[],r=e.split(`
|
|
@@ -126,4 +126,4 @@ The workspace is created but may require manual setup.`,metadata:{eventType:"wor
|
|
|
126
126
|
Authentication cancelled`),process.exit(0)):(this.error(`Authentication failed: ${p}`),process.exit(1))}const a=new _h({serverUrl:n,apiUrl:r,authToken:o.api_key,organizationId:o.organization_id,verbose:e.verbose,reconnectInterval:i,stableDeviceId:!e.uniqueInstance,options:{clientId:"agiflow-cli-daemon",baseUrl:r,verbose:e.verbose}}),l=async g=>{console.log(`
|
|
127
127
|
📡 Received ${g}, initiating graceful shutdown...`);try{await a.stop()}catch(p){this.error(`Error during shutdown: ${p}`),process.exit(1)}};process.on("SIGINT",()=>l("SIGINT")),process.on("SIGTERM",()=>l("SIGTERM")),process.on("SIGHUP",()=>l("SIGHUP"));try{await a.start();const g=a.getStats();this.success("Agiflow Daemon connected successfully"),this.info(`🆔 Daemon ID: ${g.daemonId}`),this.info(`💻 Device GUID: ${g.deviceGuid}`),this.info(`Press Ctrl+C to disconnect
|
|
128
128
|
`)}catch(g){this.error(`Failed to connect daemon: ${g}`),process.exit(1)}}}class Br extends ze{register(e){e.command("logout").description("Log out and clean up stored credentials").option("-v, --verbose","Enable verbose logging").option("--remove-all","Remove entire .agiflow directory (default: remove only credential files)").action(async n=>{try{await this.execute(n)}catch(r){this.handleError(r)}})}async execute(e){e.verbose&&console.log("🔐 Logging out and cleaning up credentials...");try{if(!await D.FileSystemUtils.exists(D.FileSystemUtils.AGIFLOW_DIR)){this.info("No stored credentials found. You are already logged out.");return}if(e.removeAll)await D.FileSystemUtils.remove(D.FileSystemUtils.AGIFLOW_DIR),e.verbose&&this.info(`Removed entire directory: ${D.FileSystemUtils.AGIFLOW_DIR}`),this.success("Successfully logged out and removed all Agiflow configuration");else{const r=["api-key.json","credentials.json"];let i=0;for(const s of r){const o=require("path").join(D.FileSystemUtils.AGIFLOW_DIR,s);await D.FileSystemUtils.exists(o)&&(await D.FileSystemUtils.remove(o),i++,e.verbose&&this.info(`Removed: ${s}`))}i===0?this.info("No credential files found. You are already logged out."):this.success(`Successfully logged out and removed ${i} credential file(s)`)}}catch(n){const r=n instanceof Error?n.message:String(n);this.error(`Failed to clean up credentials: ${r}`),process.exit(1)}}}class Ur extends ze{register(e){e.command("router").description("Manage LLM routing configuration for active sessions").option("-v, --verbose","Enable verbose logging").option("--clear","Clear all session routing configurations").action(async n=>{try{await this.execute(n)}catch(r){this.handleError(r)}})}async execute(e){try{if(e.clear){await D.SessionSettingsManager.clearAllSessions(),this.success("All session routing configurations have been cleared.");return}const n=await D.SessionSettingsManager.getAllSessions(),r=Object.keys(n);if(r.length===0){this.warn("No active sessions found."),this.info("Start a Claude agent first using: agiflow claude");return}const i=r.map(p=>{const E=n[p],m=E?.alias||p,F=E?.provider&&E?.model?`${E.provider}/${E.model}${E.reasoningEffort?` (${E.reasoningEffort})`:""}`:"default";return{name:`${m} [${F}]`,value:p,description:`Current routing: ${F}`}}),s=await et.select({message:"Select a session to configure routing:",choices:i});e.verbose&&this.info(`Selected session: ${s}`);const o=[{name:"Default (Claude)",value:null,description:"Use default Claude routing (no LLM proxy)"},{name:"GPT-5 (Reasoning: High)",value:{provider:"chatgpt",model:"gpt-5",reasoningEffort:"high"},description:"Route to GPT-5 with high reasoning effort"},{name:"GPT-5 (Reasoning: Medium)",value:{provider:"chatgpt",model:"gpt-5",reasoningEffort:"medium"},description:"Route to GPT-5 with medium reasoning effort"},{name:"GPT-5 (Reasoning: Low)",value:{provider:"chatgpt",model:"gpt-5",reasoningEffort:"low"},description:"Route to GPT-5 with low reasoning effort"},{name:"GPT-5 (Reasoning: Minimal)",value:{provider:"chatgpt",model:"gpt-5",reasoningEffort:"minimal"},description:"Route to GPT-5 with minimal reasoning effort"},{name:"GPT-5 Codex (Reasoning: High)",value:{provider:"chatgpt",model:"gpt-5-codex",reasoningEffort:"high"},description:"Route to GPT-5 Codex with high reasoning effort"},{name:"GPT-5 Codex (Reasoning: Medium)",value:{provider:"chatgpt",model:"gpt-5-codex",reasoningEffort:"medium"},description:"Route to GPT-5 Codex with medium reasoning effort"},{name:"GPT-5 Codex (Reasoning: Low)",value:{provider:"chatgpt",model:"gpt-5-codex",reasoningEffort:"low"},description:"Route to GPT-5 Codex with low reasoning effort"}],a=await et.select({message:"Select LLM routing configuration:",choices:o.map(p=>({name:p.name,value:p.value,description:p.description}))}),l=n[s],g=l?.alias||s;if(a===null){const p={...l?.alias&&{alias:l.alias}};await D.SessionSettingsManager.setSessionSettings(s,p),this.success(`Routing cleared for session "${g}". Using default Claude.`)}else{const p={...a,alias:l?.alias};await D.SessionSettingsManager.setSessionSettings(s,p),this.success(`Routing configured for session "${g}": ${a.provider}/${a.model}${a.reasoningEffort?` (${a.reasoningEffort})`:""}`)}this.info("Note: Restart the agent for changes to take effect.")}catch(n){if(n instanceof Error&&n.message.includes("User force closed")){this.info("Operation cancelled by user");return}throw n}}}Nn.config();async function Sh(){const t=new ui;t.name("agiflow").description("@agiflowai/agent-cli").option("-v, --verbose","enable verbose logging").option("--debug","enable debug mode");const e=new xr;"register"in e&&typeof e.register=="function"?e.register(t):t.command("claude").description("Launch Claude Code agent").action(async()=>{await e.run?.([])});const n=new Nr;"register"in n&&typeof n.register=="function"?n.register(t):t.command("connect").description("Connect agent").action(async()=>{await n.run?.([])});const r=new Br;"register"in r&&typeof r.register=="function"?r.register(t):t.command("logout").description("Logout and clean up credentials").action(async()=>{await r.run?.([])});const i=new Ur;"register"in i&&typeof i.register=="function"?i.register(t):t.command("router").description("Manage Agiflow routing configuration").action(async()=>{await i.run?.([])});try{await t.parseAsync(process.argv)}catch(s){console.error("[cli] error",s.message),t.opts().debug&&s instanceof Error&&console.error(s.stack),process.exit(1)}}Sh();exports.BaseCommand=ze;exports.ClaudeCommand=xr;exports.ConfigSchema=Fr;exports.ConnectCommand=Nr;exports.DaemonHttpService=Pr;exports.DaemonWsService=Rr;exports.DeviceCodeAuthService=Mt;exports.LogoutCommand=Br;exports.RouterCommand=Ur;exports.config=ue;exports.createHeartbeat=Tr;exports.generateDeviceId=Cr;exports.getServerUrl=wr;exports.getSystemInfo=br;exports.getWebSocketUrl=oh;exports.httpToWebSocket=ah;exports.webSocketToHttp=Ar;
|
|
129
|
-
//# sourceMappingURL=cli-
|
|
129
|
+
//# sourceMappingURL=cli-4pvA3zg9.js.map
|