1+ import os
2+ from pathlib import Path
3+ from typing import Dict , List , Optional
4+
5+ import yaml
6+
7+ from .base import CLITool
8+
9+
10+ class ContinueTool (CLITool ):
11+ """Continue.dev CLI wrapper."""
12+
13+ command_name = "cn"
14+ tool_key = "continue"
15+ install_description = "Continue.dev CLI"
16+
17+ def _get_filtered_endpoints (self ) -> List [str ]:
18+ """Collect endpoints that support the continue client."""
19+ endpoints = self .config .get_sections (exclude_common = True )
20+ return [
21+ ep
22+ for ep in endpoints
23+ if self .endpoint_manager ._is_client_supported (ep , "continue" )
24+ ]
25+
26+ def _process_endpoint (self , endpoint_name : str ) -> Optional [str ]:
27+ """Process a single endpoint and return selected model if successful."""
28+ success , endpoint_config = self .endpoint_manager .get_endpoint_config (
29+ endpoint_name
30+ )
31+ if not success :
32+ return None
33+
34+ # Get models from list_models_cmd
35+ models = []
36+ if "list_models_cmd" in endpoint_config :
37+ try :
38+ import subprocess
39+ result = subprocess .run (
40+ endpoint_config ["list_models_cmd" ],
41+ shell = True ,
42+ capture_output = True ,
43+ text = True ,
44+ timeout = 30
45+ )
46+ if result .returncode == 0 and result .stdout .strip ():
47+ models = [line .strip () for line in result .stdout .split ('\n ' ) if line .strip ()]
48+ except Exception as e :
49+ print (f"Warning: Failed to execute list_models_cmd for { endpoint_name } : { e } " )
50+ return None
51+ else :
52+ # Fallback if no list_models_cmd
53+ models = [endpoint_name .replace (":" , "-" ).replace ("_" , "-" )]
54+
55+ if not models :
56+ print (f"Warning: No models found for { endpoint_name } \n " )
57+ return None
58+
59+ ep_url = endpoint_config .get ("endpoint" , "" )
60+ ep_desc = endpoint_config .get ("description" , "" ) or ep_url
61+ endpoint_info = f"{ endpoint_name } -> { ep_url } -> { ep_desc } "
62+
63+ # Import package-level helper so tests can patch code_assistant_manager.tools.select_model
64+ from . import select_model
65+
66+ success , model = select_model (
67+ models , f"Select model from { endpoint_info } (or skip):"
68+ )
69+ if success and model :
70+ return model
71+ else :
72+ print (f"Skipped { endpoint_name } \n " )
73+ return None
74+
75+ def _write_continue_config (self , selected_models : List [tuple ]) -> Path :
76+ """Write Continue.dev configuration to ~/.continue/config.yaml."""
77+ # Create config structure
78+ continue_config = {
79+ "name" : "Code Assistant Manager Config" ,
80+ "version" : "0.0.1" ,
81+ "schema" : "v1" ,
82+ "models" : []
83+ }
84+
85+ # Create models from selected endpoints
86+ for endpoint_name , model_name in selected_models :
87+ success , endpoint_config = self .endpoint_manager .get_endpoint_config (endpoint_name )
88+ if not success :
89+ continue
90+
91+ # Create model entry
92+ model_entry = {
93+ "name" : f"{ endpoint_name } - { model_name } " ,
94+ "provider" : "openai" ,
95+ "model" : model_name ,
96+ "apiBase" : endpoint_config ["endpoint" ],
97+ "requestOptions" : {
98+ "verifySsl" : False
99+ }
100+ }
101+
102+ # Handle API key configuration - use actual resolved key
103+ api_key = endpoint_config .get ("actual_api_key" , "" )
104+ if api_key :
105+ model_entry ["apiKey" ] = api_key
106+
107+ continue_config ["models" ].append (model_entry )
108+
109+ # Write the config
110+ config_file = Path .home () / ".continue" / "config.yaml"
111+ config_file .parent .mkdir (parents = True , exist_ok = True )
112+ with open (config_file , "w" ) as f :
113+ yaml .dump (continue_config , f , default_flow_style = False , sort_keys = False )
114+
115+ return config_file
116+
117+ def run (self , args : List [str ] = None ) -> int :
118+ """
119+ Configure and launch the Continue.dev CLI.
120+
121+ Args:
122+ args: List of arguments to pass to the Continue CLI
123+
124+ Returns:
125+ Exit code of the Continue CLI process
126+ """
127+ args = args or []
128+
129+ # Load environment variables first
130+ self ._load_environment ()
131+
132+ # Check if Continue.dev is installed
133+ if not self ._ensure_tool_installed (
134+ self .command_name , self .tool_key , self .install_description
135+ ):
136+ return 1
137+
138+ # Get filtered endpoints that support continue
139+ filtered_endpoints = self ._get_filtered_endpoints ()
140+
141+ if not filtered_endpoints :
142+ print ("Warning: No endpoints configured for continue client." )
143+ print ("Continue.dev will use its default configuration." )
144+ else :
145+ print ("\n Configuring Continue.dev with models from all endpoints...\n " )
146+
147+ # Process each endpoint to collect selected models
148+ selected_models : List [tuple ] = [] # (endpoint_name, model_name)
149+ for endpoint_name in filtered_endpoints :
150+ model = self ._process_endpoint (endpoint_name )
151+ if model :
152+ selected_models .append ((endpoint_name , model ))
153+
154+ if not selected_models :
155+ print ("No models selected" )
156+ return 1
157+
158+ print (f"Total models selected: { len (selected_models )} \n " )
159+
160+ # Generate Continue.dev config file
161+ config_file = self ._write_continue_config (selected_models )
162+ print (f"Continue.dev config written to { config_file } " )
163+
164+ # Set up environment for Continue
165+ env = os .environ .copy ()
166+ # Set TLS environment for Node.js
167+ self ._set_node_tls_env (env )
168+
169+ # Execute the Continue CLI with the configured environment
170+ command = [self .command_name , * args ]
171+ return self ._run_tool_with_env (command , env , self .command_name , interactive = True )
0 commit comments