Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions cortex-app-server/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,9 @@ pub struct RateLimitConfig {
/// Exempt paths from rate limiting.
#[serde(default)]
pub exempt_paths: Vec<String>,
/// Trust proxy headers (X-Forwarded-For, X-Real-IP) for client IP detection.
#[serde(default)]
pub trust_proxy: bool,
}

fn default_rpm() -> u32 {
Expand All @@ -286,6 +289,7 @@ impl Default for RateLimitConfig {
by_api_key: false,
by_user: false,
exempt_paths: vec!["/health".to_string()],
trust_proxy: false,
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion cortex-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ ctor = "0.5"
base64 = { workspace = true }

# For upgrade command (HTTP requests)
reqwest = { workspace = true, features = ["json"] }
reqwest = { workspace = true, features = ["json", "cookies"] }

# For upgrade command (archive extraction)
zip = { workspace = true }
Expand Down
100 changes: 100 additions & 0 deletions cortex-cli/src/debug_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,107 @@ struct ConfigLocations {
local_config_exists: bool,
}

/// Known valid providers for validation.
const VALID_PROVIDERS: &[&str] = &[
"anthropic",
"openai",
"google",
"groq",
"mistral",
"xai",
"deepseek",
"ollama",
"lmstudio",
"llamacpp",
"vllm",
"openrouter",
"cortex",
];

/// Known valid models for validation.
const VALID_MODELS: &[&str] = &[
"claude-sonnet-4-20250514",
"claude-opus-4-20250514",
"claude-3-5-sonnet-20241022",
"claude-3-5-haiku-20241022",
"gpt-4o",
"gpt-4o-mini",
"o1",
"o1-mini",
"o3-mini",
"gemini-2.0-flash",
"gemini-2.0-flash-thinking",
"gemini-1.5-pro",
"llama-3.3-70b-versatile",
"llama-3.1-8b-instant",
"mistral-large-latest",
"codestral-latest",
"grok-2",
"deepseek-chat",
"deepseek-reasoner",
"qwen2.5-coder:32b",
"llama3.3:70b",
];

/// Check if a provider value is valid.
fn is_valid_provider(provider: &str) -> bool {
let provider_lower = provider.to_lowercase();
VALID_PROVIDERS.iter().any(|&p| p == provider_lower)
}

/// Check if a model value is valid or looks like a valid model pattern.
fn is_valid_model(model: &str) -> bool {
// Check against known models
if VALID_MODELS.iter().any(|&m| m == model) {
return true;
}
// Allow provider/model format (e.g., "anthropic/claude-sonnet-4-20250514")
if model.contains('/') {
let parts: Vec<&str> = model.splitn(2, '/').collect();
if parts.len() == 2 && is_valid_provider(parts[0]) {
return true;
}
}
// Allow Ollama-style local models (contain colon, e.g., "qwen2.5:7b")
if model.contains(':') {
return true;
}
false
}

/// Validate environment variables and print warnings.
fn validate_env_vars() {
// Validate CORTEX_PROVIDER
if let Ok(provider) = std::env::var("CORTEX_PROVIDER") {
if !is_valid_provider(&provider) {
eprintln!(
"Warning: CORTEX_PROVIDER='{}' is not a recognized provider.",
provider
);
eprintln!(" Valid providers: {}", VALID_PROVIDERS.join(", "));
eprintln!(" This may cause connection failures when making API requests.");
eprintln!();
}
}

// Validate CORTEX_MODEL
if let Ok(model) = std::env::var("CORTEX_MODEL") {
if !is_valid_model(&model) {
eprintln!(
"Warning: CORTEX_MODEL='{}' is not a recognized model.",
model
);
eprintln!(" Use 'cortex models' to see available models.");
eprintln!(" This may cause API errors when making requests.");
eprintln!();
}
}
}

async fn run_config(args: ConfigArgs) -> Result<()> {
// Validate environment variables at startup (#2058, #2059)
validate_env_vars();

let config = cortex_engine::Config::default();

let global_config = config.cortex_home.join("config.toml");
Expand Down
15 changes: 15 additions & 0 deletions cortex-cli/src/exec_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,11 @@ pub struct ExecCli {
#[arg(long = "git-diff", alias = "diff")]
pub git_diff: bool,

/// Additional context string to include in the prompt.
/// Useful for providing background information or instructions.
#[arg(long = "context", value_name = "TEXT")]
pub context: Option<String>,

/// Include only files matching these patterns.
/// Supports glob patterns like "*.py" or "src/**/*.rs".
#[arg(long = "include", action = clap::ArgAction::Append, value_name = "PATTERN")]
Expand Down Expand Up @@ -453,6 +458,16 @@ impl ExecCli {
}
}

// Add additional context if provided (#2071)
if let Some(ref context_text) = self.context {
if !context_text.is_empty() {
context_sections.push(format!(
"--- Additional Context ---\n{}\n--- End Context ---",
context_text
));
}
}

// Read from clipboard if requested (#2727)
if self.clipboard {
if let Ok(clipboard_content) = read_clipboard() {
Expand Down
47 changes: 1 addition & 46 deletions cortex-cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ fn cleanup_temp_files(temp_dir: &std::path::Path) {
/// Install a panic hook that tracks panics in background threads.
/// This ensures the main thread can detect background panics and exit
/// with an appropriate error code (#2805).
fn install_panic_hook() {
pub fn install_panic_hook() {
// Only install once
if PANIC_HOOK_INSTALLED.swap(true, Ordering::SeqCst) {
return;
Expand Down Expand Up @@ -217,51 +217,6 @@ pub fn get_panic_exit_code() -> i32 {
PANIC_EXIT_CODE.load(Ordering::SeqCst)
}

/// Install a panic hook that tracks panics in background threads.
/// This ensures the main thread can detect background panics and exit
/// with an appropriate error code (#2805).
fn install_panic_hook() {
// Only install once
if PANIC_HOOK_INSTALLED.swap(true, Ordering::SeqCst) {
return;
}

let original_hook = panic::take_hook();

panic::set_hook(Box::new(move |panic_info| {
// Mark that a panic occurred
BACKGROUND_PANIC_OCCURRED.store(true, Ordering::SeqCst);

// Restore terminal state before printing panic message
restore_terminal();

// Log the panic location for debugging
if let Some(location) = panic_info.location() {
eprintln!(
"Panic in thread '{}' at {}:{}:{}",
std::thread::current().name().unwrap_or("<unnamed>"),
location.file(),
location.line(),
location.column()
);
}

// Call original hook for standard panic output
original_hook(panic_info);
}));
}

/// Check if any background thread has panicked.
/// Call this before exiting to ensure proper exit code propagation.
pub fn has_background_panic() -> bool {
BACKGROUND_PANIC_OCCURRED.load(Ordering::SeqCst)
}

/// Get the exit code to use if a background panic occurred.
pub fn get_panic_exit_code() -> i32 {
PANIC_EXIT_CODE.load(Ordering::SeqCst)
}

/// Restore terminal state (cursor visibility, etc.).
/// Called on Ctrl+C or panic to ensure clean terminal state.
pub fn restore_terminal() {
Expand Down
Loading