Files
Triple-C/app/src-tauri/src/models/project.rs
Josh Knapp 552aaebf16
Some checks failed
Build App / build-linux (push) Failing after 1m40s
Build App / build-windows (push) Failing after 1m43s
Simplify auth modes to Anthropic and Bedrock, fix Windows taskbar icon
Replace the three auth modes (Login, API Key, Bedrock) with two
(Anthropic, Bedrock). The Anthropic mode uses OAuth via `claude login`
inside the terminal, which generates and stores its own API key in the
persistent config volume. The separate API Key mode is removed because
Claude Code now requires interactive approval of externally-provided
keys, making the injected ANTHROPIC_API_KEY approach unreliable.

Old projects stored as "login" or "api_key" are automatically migrated
to "anthropic" via serde aliases.

Also fix the Windows taskbar icon showing as a black square by loading
icon.png instead of icon.ico for the runtime window icon.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 03:10:57 +00:00

152 lines
4.5 KiB
Rust

use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct EnvVar {
pub key: String,
pub value: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ProjectPath {
pub host_path: String,
pub mount_name: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Project {
pub id: String,
pub name: String,
pub paths: Vec<ProjectPath>,
pub container_id: Option<String>,
pub status: ProjectStatus,
pub auth_mode: AuthMode,
pub bedrock_config: Option<BedrockConfig>,
pub allow_docker_access: bool,
pub ssh_key_path: Option<String>,
#[serde(skip_serializing)]
pub git_token: Option<String>,
pub git_user_name: Option<String>,
pub git_user_email: Option<String>,
#[serde(default)]
pub custom_env_vars: Vec<EnvVar>,
#[serde(default)]
pub claude_instructions: Option<String>,
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,
}
/// How the project authenticates with Claude.
/// - `Anthropic`: User runs `claude login` inside the container (OAuth via Anthropic Console,
/// persisted in the config volume)
/// - `Bedrock`: Uses AWS Bedrock with per-project AWS credentials
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum AuthMode {
/// Backward compat: old projects stored as "login" or "api_key" map to Anthropic.
#[serde(alias = "login", alias = "api_key")]
Anthropic,
Bedrock,
}
impl Default for AuthMode {
fn default() -> Self {
Self::Anthropic
}
}
/// 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,
#[serde(skip_serializing)]
pub aws_access_key_id: Option<String>,
#[serde(skip_serializing)]
pub aws_secret_access_key: Option<String>,
#[serde(skip_serializing)]
pub aws_session_token: Option<String>,
pub aws_profile: Option<String>,
#[serde(skip_serializing)]
pub aws_bearer_token: Option<String>,
pub model_id: Option<String>,
pub disable_prompt_caching: bool,
}
impl Project {
pub fn new(name: String, paths: Vec<ProjectPath>) -> Self {
let now = chrono::Utc::now().to_rfc3339();
Self {
id: uuid::Uuid::new_v4().to_string(),
name,
paths,
container_id: None,
status: ProjectStatus::Stopped,
auth_mode: AuthMode::default(),
bedrock_config: None,
allow_docker_access: false,
ssh_key_path: None,
git_token: None,
git_user_name: None,
git_user_email: None,
custom_env_vars: Vec::new(),
claude_instructions: None,
created_at: now.clone(),
updated_at: now,
}
}
pub fn container_name(&self) -> String {
format!("triple-c-{}", self.id)
}
/// 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
}
}