diff options
Diffstat (limited to 'makima/src')
| -rw-r--r-- | makima/src/bin/makima.rs | 7 | ||||
| -rw-r--r-- | makima/src/daemon/api/client.rs | 8 | ||||
| -rw-r--r-- | makima/src/daemon/cli/contract.rs | 2 | ||||
| -rw-r--r-- | makima/src/daemon/cli/supervisor.rs | 2 | ||||
| -rw-r--r-- | makima/src/daemon/task/manager.rs | 18 | ||||
| -rw-r--r-- | makima/src/daemon/worktree/manager.rs | 61 | ||||
| -rw-r--r-- | makima/src/server/handlers/repository_history.rs | 19 | ||||
| -rw-r--r-- | makima/src/server/mod.rs | 8 |
8 files changed, 111 insertions, 14 deletions
diff --git a/makima/src/bin/makima.rs b/makima/src/bin/makima.rs index 9c9ac77..47e627b 100644 --- a/makima/src/bin/makima.rs +++ b/makima/src/bin/makima.rs @@ -168,6 +168,12 @@ async fn run_daemon( } else { None }; + // Derive HTTP API URL from WebSocket server URL (wss://... -> https://...) + let api_url = config + .server + .url + .replace("wss://", "https://") + .replace("ws://", "http://"); let task_config = TaskConfig { max_concurrent_tasks: config.process.max_concurrent_tasks, worktree_base_dir: config.worktree.base_dir.clone(), @@ -178,6 +184,7 @@ async fn run_daemon( enable_permissions: config.process.enable_permissions, disable_verbose: config.process.disable_verbose, bubblewrap: bubblewrap_config, + api_url, }; // Create task manager diff --git a/makima/src/daemon/api/client.rs b/makima/src/daemon/api/client.rs index b27d606..2318d5a 100644 --- a/makima/src/daemon/api/client.rs +++ b/makima/src/daemon/api/client.rs @@ -42,7 +42,9 @@ impl ApiClient { let url = format!("{}{}", self.base_url, path); let response = self.client .get(&url) + // Send both headers - server will try tool key first, then API key .header("X-Makima-Tool-Key", &self.api_key) + .header("X-Makima-API-Key", &self.api_key) .send() .await?; @@ -58,7 +60,9 @@ impl ApiClient { let url = format!("{}{}", self.base_url, path); let response = self.client .post(&url) + // Send both headers - server will try tool key first, then API key .header("X-Makima-Tool-Key", &self.api_key) + .header("X-Makima-API-Key", &self.api_key) .header("Content-Type", "application/json") .json(body) .send() @@ -72,7 +76,9 @@ impl ApiClient { let url = format!("{}{}", self.base_url, path); let response = self.client .post(&url) + // Send both headers - server will try tool key first, then API key .header("X-Makima-Tool-Key", &self.api_key) + .header("X-Makima-API-Key", &self.api_key) .send() .await?; @@ -88,7 +94,9 @@ impl ApiClient { let url = format!("{}{}", self.base_url, path); let response = self.client .put(&url) + // Send both headers - server will try tool key first, then API key .header("X-Makima-Tool-Key", &self.api_key) + .header("X-Makima-API-Key", &self.api_key) .header("Content-Type", "application/json") .json(body) .send() diff --git a/makima/src/daemon/cli/contract.rs b/makima/src/daemon/cli/contract.rs index 5fef5ec..a443b85 100644 --- a/makima/src/daemon/cli/contract.rs +++ b/makima/src/daemon/cli/contract.rs @@ -7,7 +7,7 @@ use uuid::Uuid; #[derive(Args, Debug, Clone)] pub struct ContractArgs { /// API URL - #[arg(long, env = "MAKIMA_API_URL", default_value = "http://localhost:8080", global = true)] + #[arg(long, env = "MAKIMA_API_URL", default_value = "https://api.makima.jp", global = true)] pub api_url: String, /// API key for authentication diff --git a/makima/src/daemon/cli/supervisor.rs b/makima/src/daemon/cli/supervisor.rs index ba4fb2b..db30cf1 100644 --- a/makima/src/daemon/cli/supervisor.rs +++ b/makima/src/daemon/cli/supervisor.rs @@ -7,7 +7,7 @@ use uuid::Uuid; #[derive(Args, Debug, Clone)] pub struct SupervisorArgs { /// API URL - #[arg(long, env = "MAKIMA_API_URL", default_value = "http://localhost:8080")] + #[arg(long, env = "MAKIMA_API_URL", default_value = "https://api.makima.jp")] pub api_url: String, /// API key for authentication diff --git a/makima/src/daemon/task/manager.rs b/makima/src/daemon/task/manager.rs index 5491934..fccebc5 100644 --- a/makima/src/daemon/task/manager.rs +++ b/makima/src/daemon/task/manager.rs @@ -978,6 +978,8 @@ pub struct TaskConfig { pub disable_verbose: bool, /// Bubblewrap sandbox configuration. pub bubblewrap: Option<crate::daemon::config::BubblewrapConfig>, + /// API URL for spawned tasks (HTTP endpoint for makima CLI). + pub api_url: String, } impl Default for TaskConfig { @@ -992,6 +994,7 @@ impl Default for TaskConfig { enable_permissions: false, disable_verbose: false, bubblewrap: None, + api_url: "https://api.makima.jp".to_string(), } } } @@ -1583,6 +1586,7 @@ impl TaskManager { active_pids: self.active_pids.clone(), git_user_email: self.git_user_email.clone(), git_user_name: self.git_user_name.clone(), + api_url: self.config.api_url.clone(), } } @@ -2877,6 +2881,7 @@ struct TaskManagerInner { active_pids: Arc<RwLock<HashMap<Uuid, u32>>>, git_user_email: Arc<RwLock<Option<String>>>, git_user_name: Arc<RwLock<Option<String>>>, + api_url: String, } impl TaskManagerInner { @@ -3196,8 +3201,7 @@ impl TaskManagerInner { // Set up environment variables for makima CLI let mut env = HashMap::new(); - // TODO: Make API URL configurable - env.insert("MAKIMA_API_URL".to_string(), "http://localhost:8080".to_string()); + env.insert("MAKIMA_API_URL".to_string(), self.api_url.clone()); env.insert("MAKIMA_API_KEY".to_string(), tool_key.clone()); env.insert("MAKIMA_TASK_ID".to_string(), task_id.to_string()); // Supervisor needs contract ID for its tools @@ -3207,7 +3211,7 @@ impl TaskManagerInner { tracing::info!( task_id = %task_id, - api_url = "http://localhost:8080", + api_url = %self.api_url, tool_key_preview = &tool_key[..8.min(tool_key.len())], "Set supervisor environment variables" ); @@ -3252,14 +3256,13 @@ impl TaskManagerInner { // Set up environment variables for makima CLI let mut env = HashMap::new(); - // TODO: Make API URL configurable - env.insert("MAKIMA_API_URL".to_string(), "http://localhost:8080".to_string()); + env.insert("MAKIMA_API_URL".to_string(), self.api_url.clone()); env.insert("MAKIMA_API_KEY".to_string(), tool_key.clone()); env.insert("MAKIMA_TASK_ID".to_string(), task_id.to_string()); tracing::info!( task_id = %task_id, - api_url = "http://localhost:8080", + api_url = %self.api_url, tool_key_preview = &tool_key[..8.min(tool_key.len())], "Set orchestrator environment variables" ); @@ -3313,7 +3316,7 @@ impl TaskManagerInner { tracing::warn!(task_id = %task_id, "Failed to register contract tool key"); } - env.insert("MAKIMA_API_URL".to_string(), "http://localhost:8080".to_string()); + env.insert("MAKIMA_API_URL".to_string(), self.api_url.clone()); env.insert("MAKIMA_API_KEY".to_string(), tool_key); env.insert("MAKIMA_TASK_ID".to_string(), task_id.to_string()); } @@ -4126,6 +4129,7 @@ impl Clone for TaskManagerInner { active_pids: self.active_pids.clone(), git_user_email: self.git_user_email.clone(), git_user_name: self.git_user_name.clone(), + api_url: self.api_url.clone(), } } } diff --git a/makima/src/daemon/worktree/manager.rs b/makima/src/daemon/worktree/manager.rs index ff0e9e7..d370828 100644 --- a/makima/src/daemon/worktree/manager.rs +++ b/makima/src/daemon/worktree/manager.rs @@ -128,8 +128,30 @@ impl WorktreeManager { /// Detect the default branch of a repository. /// Tries to find HEAD's target, falling back to common branch names. + /// Works for both regular and bare repositories. pub async fn detect_default_branch(&self, repo_path: &Path) -> Result<String, WorktreeError> { - // Try to get the branch that HEAD points to + tracing::debug!("Detecting default branch for repo: {}", repo_path.display()); + + // First, try to read HEAD directly (works for bare repos) + // In bare repos, HEAD is a symbolic ref to the default branch + let output = Command::new("git") + .args(["symbolic-ref", "HEAD", "--short"]) + .current_dir(repo_path) + .output() + .await?; + + if output.status.success() { + let branch = String::from_utf8_lossy(&output.stdout).trim().to_string(); + if !branch.is_empty() { + tracing::debug!("Detected default branch from HEAD: {}", branch); + return Ok(branch); + } + } else { + let stderr = String::from_utf8_lossy(&output.stderr); + tracing::debug!("symbolic-ref HEAD failed: {}", stderr.trim()); + } + + // Try to get the branch that origin/HEAD points to (for regular clones) let output = Command::new("git") .args(["symbolic-ref", "refs/remotes/origin/HEAD", "--short"]) .current_dir(repo_path) @@ -141,11 +163,12 @@ impl WorktreeManager { // Remove "origin/" prefix if present let branch = branch.strip_prefix("origin/").unwrap_or(&branch).to_string(); if !branch.is_empty() { + tracing::debug!("Detected default branch from origin/HEAD: {}", branch); return Ok(branch); } } - // Try common branch names + // Try common branch names in refs/heads (works for bare and regular repos) for branch in ["main", "master", "develop", "trunk"] { let output = Command::new("git") .args(["rev-parse", "--verify", &format!("refs/heads/{}", branch)]) @@ -154,11 +177,12 @@ impl WorktreeManager { .await?; if output.status.success() { + tracing::debug!("Detected default branch from refs/heads: {}", branch); return Ok(branch.to_string()); } } - // Fall back to getting the current branch + // Fall back to getting the current branch (for regular repos) let output = Command::new("git") .args(["rev-parse", "--abbrev-ref", "HEAD"]) .current_dir(repo_path) @@ -168,12 +192,41 @@ impl WorktreeManager { if output.status.success() { let branch = String::from_utf8_lossy(&output.stdout).trim().to_string(); if !branch.is_empty() && branch != "HEAD" { + tracing::debug!("Detected default branch from rev-parse: {}", branch); + return Ok(branch); + } + } + + // Final fallback: list all branches and pick the first one + let output = Command::new("git") + .args(["for-each-ref", "--format=%(refname:short)", "refs/heads/", "--count=1"]) + .current_dir(repo_path) + .output() + .await?; + + if output.status.success() { + let branch = String::from_utf8_lossy(&output.stdout).trim().to_string(); + if !branch.is_empty() { + tracing::warn!("Using first available branch as fallback: {}", branch); return Ok(branch); } } + // Log what branches exist for debugging + let output = Command::new("git") + .args(["for-each-ref", "--format=%(refname)", "refs/"]) + .current_dir(repo_path) + .output() + .await?; + + let available_refs = String::from_utf8_lossy(&output.stdout); + tracing::error!( + "Could not detect default branch. Available refs:\n{}", + available_refs + ); + Err(WorktreeError::GitCommand( - "Could not detect default branch".to_string(), + format!("Could not detect default branch. Check if the repository at {} has any branches.", repo_path.display()), )) } diff --git a/makima/src/server/handlers/repository_history.rs b/makima/src/server/handlers/repository_history.rs index c788d84..9c309c0 100644 --- a/makima/src/server/handlers/repository_history.rs +++ b/makima/src/server/handlers/repository_history.rs @@ -97,6 +97,14 @@ pub async fn get_repository_suggestions( let limit = params.limit.unwrap_or(10).min(50); // Cap at 50 for safety + tracing::debug!( + owner_id = %auth.owner_id, + source_type = ?params.source_type, + query = ?params.query, + limit = limit, + "Fetching repository suggestions" + ); + match repository::get_repository_suggestions( pool, auth.owner_id, @@ -107,6 +115,17 @@ pub async fn get_repository_suggestions( .await { Ok(entries) => { + // Debug log to help diagnose filtering issues + for entry in &entries { + tracing::debug!( + id = %entry.id, + name = %entry.name, + source_type = %entry.source_type, + has_url = entry.repository_url.is_some(), + has_path = entry.local_path.is_some(), + "Repository suggestion entry" + ); + } let total = entries.len() as i64; Json(RepositoryHistoryListResponse { entries, total }).into_response() } diff --git a/makima/src/server/mod.rs b/makima/src/server/mod.rs index e244a08..a4cb3d1 100644 --- a/makima/src/server/mod.rs +++ b/makima/src/server/mod.rs @@ -18,7 +18,7 @@ use tower_http::trace::TraceLayer; use utoipa::OpenApi; use utoipa_swagger_ui::SwaggerUi; -use crate::server::handlers::{api_keys, chat, contract_chat, contract_daemon, contracts, file_ws, files, listen, mesh, mesh_chat, mesh_daemon, mesh_merge, mesh_supervisor, mesh_ws, repository_history, templates, transcript_analysis, users, versions}; +use crate::server::handlers::{api_keys, chat, contract_chat, contract_daemon, contracts, file_ws, files, history, listen, mesh, mesh_chat, mesh_daemon, mesh_merge, mesh_supervisor, mesh_ws, repository_history, templates, transcript_analysis, users, versions}; use crate::server::openapi::ApiDoc; use crate::server::state::SharedState; @@ -107,6 +107,7 @@ pub fn make_router(state: SharedState) -> Router { // Checkpoint endpoints .route("/mesh/tasks/{id}/checkpoint", post(mesh_supervisor::create_checkpoint)) .route("/mesh/tasks/{id}/checkpoints", get(mesh_supervisor::list_checkpoints)) + .route("/mesh/tasks/{id}/conversation", get(history::get_task_conversation)) // Resume and rewind endpoints .route("/mesh/tasks/{id}/rewind", post(mesh::rewind_task)) .route("/mesh/tasks/{id}/fork", post(mesh::fork_task)) @@ -166,6 +167,9 @@ pub fn make_router(state: SharedState) -> Router { // Contract supervisor resume endpoints .route("/contracts/{id}/supervisor/resume", post(mesh_supervisor::resume_supervisor)) .route("/contracts/{id}/supervisor/conversation/rewind", post(mesh_supervisor::rewind_conversation)) + // History endpoints + .route("/contracts/{id}/history", get(history::get_contract_history)) + .route("/contracts/{id}/supervisor/conversation", get(history::get_supervisor_conversation)) // Contract daemon endpoints (for tasks to interact with contracts) .route("/contracts/{id}/daemon/status", get(contract_daemon::get_contract_status)) .route("/contracts/{id}/daemon/checklist", get(contract_daemon::get_contract_checklist)) @@ -198,6 +202,8 @@ pub fn make_router(state: SharedState) -> Router { "/contracts/{id}/tasks/{task_id}", post(contracts::add_task_to_contract).delete(contracts::remove_task_from_contract), ) + // Timeline endpoint (unified history for user) + .route("/timeline", get(history::get_timeline)) // Template endpoints .route("/templates", get(templates::list_templates)) .route("/templates/{id}", get(templates::get_template)) |
