2026-02-27 04:29:51 +00:00
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
|
|
2026-02-27 18:39:20 -08:00
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
|
|
|
|
pub struct EnvVar {
|
|
|
|
|
pub key: String,
|
|
|
|
|
pub value: String,
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-28 21:18:33 +00:00
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
|
|
|
|
pub struct ProjectPath {
|
|
|
|
|
pub host_path: String,
|
|
|
|
|
pub mount_name: String,
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-01 14:36:51 +00:00
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
|
|
|
|
pub struct PortMapping {
|
|
|
|
|
pub host_port: u16,
|
|
|
|
|
pub container_port: u16,
|
|
|
|
|
#[serde(default = "default_protocol")]
|
|
|
|
|
pub protocol: String,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn default_protocol() -> String {
|
|
|
|
|
"tcp".to_string()
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-13 08:58:13 -07:00
|
|
|
fn default_full_permissions() -> bool {
|
|
|
|
|
true
|
|
|
|
|
}
|
|
|
|
|
|
Add Claude Code settings infrastructure, TUI mode, session naming, and global defaults
Adds first-class support for Claude Code CLI features (2.1.71-2.1.110):
- New ClaudeCodeSettings struct with per-project and global defaults for
TUI mode, effort level, focus mode, thinking summaries, session recap,
auto-scroll, env scrub, and 1-hour prompt caching
- Settings injected as env vars (CLAUDE_CODE_NO_FLICKER, etc.) and
~/.claude/settings.json entries via entrypoint.sh merge block
- New ClaudeCodeSettingsModal component for configuring settings
- Session naming support (-n flag passed to claude CLI, shown in tabs)
- Relaxed reserved prefix filter: CLAUDE_CODE_* env vars now allowed in
custom env vars UI for power users
- Global SSH key path, git name, and git email now used as fallbacks
when per-project values are not set, with UI in SettingsPanel
- Fingerprint-based change detection triggers container recreation when
Claude Code settings change
- Updated README, HOW-TO-USE, and CLAUDE.md documentation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 08:46:03 -07:00
|
|
|
/// Settings for Claude Code CLI behavior inside the container.
|
|
|
|
|
/// These map to Claude Code env vars and ~/.claude/settings.json entries.
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
|
|
|
|
|
pub struct ClaudeCodeSettings {
|
|
|
|
|
/// TUI rendering mode: None = default, Some("fullscreen") = flicker-free alt-screen
|
|
|
|
|
#[serde(default)]
|
|
|
|
|
pub tui_mode: Option<String>,
|
|
|
|
|
/// Effort level: None = default, Some("low"|"medium"|"high")
|
|
|
|
|
#[serde(default)]
|
|
|
|
|
pub effort: Option<String>,
|
|
|
|
|
/// Disable auto-scroll in fullscreen TUI mode
|
|
|
|
|
#[serde(default)]
|
|
|
|
|
pub auto_scroll_disabled: bool,
|
|
|
|
|
/// Enable focus mode (collapsed tool output)
|
|
|
|
|
#[serde(default)]
|
|
|
|
|
pub focus_mode: bool,
|
|
|
|
|
/// Show thinking summaries in responses
|
|
|
|
|
#[serde(default)]
|
|
|
|
|
pub show_thinking_summaries: bool,
|
|
|
|
|
/// Enable session recap when returning to a session
|
|
|
|
|
#[serde(default)]
|
|
|
|
|
pub enable_session_recap: bool,
|
|
|
|
|
/// Strip credentials from subprocess environments
|
|
|
|
|
#[serde(default)]
|
|
|
|
|
pub env_scrub: bool,
|
|
|
|
|
/// Enable 1-hour prompt cache TTL (vs default 5-minute)
|
|
|
|
|
#[serde(default)]
|
|
|
|
|
pub prompt_caching_1h: bool,
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-27 04:29:51 +00:00
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
|
pub struct Project {
|
|
|
|
|
pub id: String,
|
|
|
|
|
pub name: String,
|
2026-02-28 21:18:33 +00:00
|
|
|
pub paths: Vec<ProjectPath>,
|
2026-02-27 04:29:51 +00:00
|
|
|
pub container_id: Option<String>,
|
|
|
|
|
pub status: ProjectStatus,
|
2026-03-12 09:26:58 -07:00
|
|
|
#[serde(alias = "auth_mode")]
|
|
|
|
|
pub backend: Backend,
|
2026-02-27 14:29:40 +00:00
|
|
|
pub bedrock_config: Option<BedrockConfig>,
|
2026-03-11 13:05:52 -07:00
|
|
|
pub ollama_config: Option<OllamaConfig>,
|
2026-03-13 06:16:05 -07:00
|
|
|
#[serde(alias = "litellm_config")]
|
|
|
|
|
pub openai_compatible_config: Option<OpenAiCompatibleConfig>,
|
2026-02-27 04:29:51 +00:00
|
|
|
pub allow_docker_access: bool,
|
2026-03-05 19:32:04 -08:00
|
|
|
#[serde(default)]
|
|
|
|
|
pub mission_control_enabled: bool,
|
2026-03-13 08:58:13 -07:00
|
|
|
#[serde(default = "default_full_permissions")]
|
|
|
|
|
pub full_permissions: bool,
|
2026-02-27 04:29:51 +00:00
|
|
|
pub ssh_key_path: Option<String>,
|
2026-03-05 17:39:34 -08:00
|
|
|
#[serde(skip_serializing, default)]
|
2026-02-27 04:29:51 +00:00
|
|
|
pub git_token: Option<String>,
|
|
|
|
|
pub git_user_name: Option<String>,
|
|
|
|
|
pub git_user_email: Option<String>,
|
2026-02-27 18:39:20 -08:00
|
|
|
#[serde(default)]
|
|
|
|
|
pub custom_env_vars: Vec<EnvVar>,
|
|
|
|
|
#[serde(default)]
|
2026-03-01 14:36:51 +00:00
|
|
|
pub port_mappings: Vec<PortMapping>,
|
|
|
|
|
#[serde(default)]
|
2026-02-27 18:39:20 -08:00
|
|
|
pub claude_instructions: Option<String>,
|
2026-03-04 08:57:12 -08:00
|
|
|
#[serde(default)]
|
|
|
|
|
pub enabled_mcp_servers: Vec<String>,
|
Add Claude Code settings infrastructure, TUI mode, session naming, and global defaults
Adds first-class support for Claude Code CLI features (2.1.71-2.1.110):
- New ClaudeCodeSettings struct with per-project and global defaults for
TUI mode, effort level, focus mode, thinking summaries, session recap,
auto-scroll, env scrub, and 1-hour prompt caching
- Settings injected as env vars (CLAUDE_CODE_NO_FLICKER, etc.) and
~/.claude/settings.json entries via entrypoint.sh merge block
- New ClaudeCodeSettingsModal component for configuring settings
- Session naming support (-n flag passed to claude CLI, shown in tabs)
- Relaxed reserved prefix filter: CLAUDE_CODE_* env vars now allowed in
custom env vars UI for power users
- Global SSH key path, git name, and git email now used as fallbacks
when per-project values are not set, with UI in SettingsPanel
- Fingerprint-based change detection triggers container recreation when
Claude Code settings change
- Updated README, HOW-TO-USE, and CLAUDE.md documentation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 08:46:03 -07:00
|
|
|
#[serde(default)]
|
|
|
|
|
pub claude_code_settings: Option<ClaudeCodeSettings>,
|
2026-02-27 04:29:51 +00:00
|
|
|
pub created_at: String,
|
|
|
|
|
pub updated_at: String,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
|
|
|
|
#[serde(rename_all = "lowercase")]
|
|
|
|
|
pub enum ProjectStatus {
|
|
|
|
|
Stopped,
|
|
|
|
|
Starting,
|
|
|
|
|
Running,
|
|
|
|
|
Stopping,
|
|
|
|
|
Error,
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-12 09:26:58 -07:00
|
|
|
/// Which AI model backend/provider the project uses.
|
|
|
|
|
/// - `Anthropic`: Direct Anthropic API (user runs `claude login` inside the container)
|
|
|
|
|
/// - `Bedrock`: AWS Bedrock with per-project AWS credentials
|
|
|
|
|
/// - `Ollama`: Local or remote Ollama server
|
2026-03-13 06:16:05 -07:00
|
|
|
/// - `OpenAiCompatible`: Any OpenAI API-compatible endpoint (e.g., LiteLLM, vLLM, etc.)
|
2026-02-27 04:29:51 +00:00
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
|
|
|
|
#[serde(rename_all = "snake_case")]
|
2026-03-12 09:26:58 -07:00
|
|
|
pub enum Backend {
|
2026-03-01 03:10:57 +00:00
|
|
|
/// Backward compat: old projects stored as "login" or "api_key" map to Anthropic.
|
|
|
|
|
#[serde(alias = "login", alias = "api_key")]
|
|
|
|
|
Anthropic,
|
2026-02-27 14:29:40 +00:00
|
|
|
Bedrock,
|
2026-03-11 13:05:52 -07:00
|
|
|
Ollama,
|
2026-03-13 06:16:05 -07:00
|
|
|
#[serde(alias = "lite_llm", alias = "litellm")]
|
|
|
|
|
OpenAiCompatible,
|
2026-02-27 04:29:51 +00:00
|
|
|
}
|
|
|
|
|
|
2026-03-12 09:26:58 -07:00
|
|
|
impl Default for Backend {
|
2026-02-27 04:29:51 +00:00
|
|
|
fn default() -> Self {
|
2026-03-01 03:10:57 +00:00
|
|
|
Self::Anthropic
|
2026-02-27 04:29:51 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-27 14:29:40 +00:00
|
|
|
/// How Bedrock authenticates with AWS.
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
|
|
|
|
#[serde(rename_all = "snake_case")]
|
|
|
|
|
pub enum BedrockAuthMethod {
|
|
|
|
|
StaticCredentials,
|
|
|
|
|
Profile,
|
|
|
|
|
BearerToken,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Default for BedrockAuthMethod {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
Self::StaticCredentials
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// AWS Bedrock configuration for a project.
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
|
pub struct BedrockConfig {
|
|
|
|
|
pub auth_method: BedrockAuthMethod,
|
|
|
|
|
pub aws_region: String,
|
2026-03-05 17:39:34 -08:00
|
|
|
#[serde(skip_serializing, default)]
|
2026-02-27 14:29:40 +00:00
|
|
|
pub aws_access_key_id: Option<String>,
|
2026-03-05 17:39:34 -08:00
|
|
|
#[serde(skip_serializing, default)]
|
2026-02-27 14:29:40 +00:00
|
|
|
pub aws_secret_access_key: Option<String>,
|
2026-03-05 17:39:34 -08:00
|
|
|
#[serde(skip_serializing, default)]
|
2026-02-27 14:29:40 +00:00
|
|
|
pub aws_session_token: Option<String>,
|
|
|
|
|
pub aws_profile: Option<String>,
|
2026-03-05 17:39:34 -08:00
|
|
|
#[serde(skip_serializing, default)]
|
2026-02-27 14:29:40 +00:00
|
|
|
pub aws_bearer_token: Option<String>,
|
|
|
|
|
pub model_id: Option<String>,
|
|
|
|
|
pub disable_prompt_caching: bool,
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-11 13:05:52 -07:00
|
|
|
/// Ollama configuration for a project.
|
|
|
|
|
/// Ollama exposes an Anthropic-compatible API endpoint.
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
|
pub struct OllamaConfig {
|
|
|
|
|
/// The base URL of the Ollama server (e.g., "http://host.docker.internal:11434" or "http://192.168.1.100:11434")
|
|
|
|
|
pub base_url: String,
|
|
|
|
|
/// Optional model override (e.g., "qwen3.5:27b")
|
|
|
|
|
pub model_id: Option<String>,
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-13 06:16:05 -07:00
|
|
|
/// OpenAI Compatible endpoint configuration for a project.
|
|
|
|
|
/// Routes Anthropic API calls through any OpenAI API-compatible endpoint
|
|
|
|
|
/// (e.g., LiteLLM, vLLM, or other compatible gateways).
|
2026-03-11 13:05:52 -07:00
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
2026-03-13 06:16:05 -07:00
|
|
|
pub struct OpenAiCompatibleConfig {
|
|
|
|
|
/// The base URL of the OpenAI-compatible endpoint (e.g., "http://host.docker.internal:4000" or "https://api.example.com")
|
2026-03-11 13:05:52 -07:00
|
|
|
pub base_url: String,
|
2026-03-13 06:16:05 -07:00
|
|
|
/// API key for the OpenAI-compatible endpoint
|
2026-03-11 13:05:52 -07:00
|
|
|
#[serde(skip_serializing, default)]
|
|
|
|
|
pub api_key: Option<String>,
|
|
|
|
|
/// Optional model override
|
|
|
|
|
pub model_id: Option<String>,
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-27 04:29:51 +00:00
|
|
|
impl Project {
|
2026-02-28 21:18:33 +00:00
|
|
|
pub fn new(name: String, paths: Vec<ProjectPath>) -> Self {
|
2026-02-27 04:29:51 +00:00
|
|
|
let now = chrono::Utc::now().to_rfc3339();
|
|
|
|
|
Self {
|
|
|
|
|
id: uuid::Uuid::new_v4().to_string(),
|
|
|
|
|
name,
|
2026-02-28 21:18:33 +00:00
|
|
|
paths,
|
2026-02-27 04:29:51 +00:00
|
|
|
container_id: None,
|
|
|
|
|
status: ProjectStatus::Stopped,
|
2026-03-12 09:26:58 -07:00
|
|
|
backend: Backend::default(),
|
2026-02-27 14:29:40 +00:00
|
|
|
bedrock_config: None,
|
2026-03-11 13:05:52 -07:00
|
|
|
ollama_config: None,
|
2026-03-13 06:16:05 -07:00
|
|
|
openai_compatible_config: None,
|
2026-02-27 04:29:51 +00:00
|
|
|
allow_docker_access: false,
|
2026-03-05 19:32:04 -08:00
|
|
|
mission_control_enabled: false,
|
2026-03-13 08:58:13 -07:00
|
|
|
full_permissions: false,
|
2026-02-27 04:29:51 +00:00
|
|
|
ssh_key_path: None,
|
|
|
|
|
git_token: None,
|
|
|
|
|
git_user_name: None,
|
|
|
|
|
git_user_email: None,
|
2026-02-27 18:39:20 -08:00
|
|
|
custom_env_vars: Vec::new(),
|
2026-03-01 14:36:51 +00:00
|
|
|
port_mappings: Vec::new(),
|
2026-02-27 18:39:20 -08:00
|
|
|
claude_instructions: None,
|
2026-03-04 08:57:12 -08:00
|
|
|
enabled_mcp_servers: Vec::new(),
|
Add Claude Code settings infrastructure, TUI mode, session naming, and global defaults
Adds first-class support for Claude Code CLI features (2.1.71-2.1.110):
- New ClaudeCodeSettings struct with per-project and global defaults for
TUI mode, effort level, focus mode, thinking summaries, session recap,
auto-scroll, env scrub, and 1-hour prompt caching
- Settings injected as env vars (CLAUDE_CODE_NO_FLICKER, etc.) and
~/.claude/settings.json entries via entrypoint.sh merge block
- New ClaudeCodeSettingsModal component for configuring settings
- Session naming support (-n flag passed to claude CLI, shown in tabs)
- Relaxed reserved prefix filter: CLAUDE_CODE_* env vars now allowed in
custom env vars UI for power users
- Global SSH key path, git name, and git email now used as fallbacks
when per-project values are not set, with UI in SettingsPanel
- Fingerprint-based change detection triggers container recreation when
Claude Code settings change
- Updated README, HOW-TO-USE, and CLAUDE.md documentation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 08:46:03 -07:00
|
|
|
claude_code_settings: None,
|
2026-02-27 04:29:51 +00:00
|
|
|
created_at: now.clone(),
|
|
|
|
|
updated_at: now,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn container_name(&self) -> String {
|
|
|
|
|
format!("triple-c-{}", self.id)
|
|
|
|
|
}
|
2026-02-28 21:18:33 +00:00
|
|
|
|
|
|
|
|
/// Migrate a project JSON value from old single-`path` format to new `paths` format.
|
|
|
|
|
/// If the value already has `paths`, it is returned unchanged.
|
|
|
|
|
pub fn migrate_from_value(mut val: serde_json::Value) -> serde_json::Value {
|
|
|
|
|
if let Some(obj) = val.as_object_mut() {
|
|
|
|
|
if obj.contains_key("paths") {
|
|
|
|
|
return val;
|
|
|
|
|
}
|
|
|
|
|
if let Some(path_val) = obj.remove("path") {
|
|
|
|
|
let path_str = path_val.as_str().unwrap_or("").to_string();
|
|
|
|
|
let mount_name = path_str
|
|
|
|
|
.trim_end_matches(['/', '\\'])
|
|
|
|
|
.rsplit(['/', '\\'])
|
|
|
|
|
.next()
|
|
|
|
|
.unwrap_or("workspace")
|
|
|
|
|
.to_string();
|
|
|
|
|
let project_path = serde_json::json!([{
|
|
|
|
|
"host_path": path_str,
|
|
|
|
|
"mount_name": if mount_name.is_empty() { "workspace".to_string() } else { mount_name },
|
|
|
|
|
}]);
|
|
|
|
|
obj.insert("paths".to_string(), project_path);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
val
|
|
|
|
|
}
|
2026-02-27 04:29:51 +00:00
|
|
|
}
|