Skip to content

Commit c907da2

Browse files
Alex Holmbergclaude
authored andcommitted
fix(agent): register ListDeploymentCapabilitiesTool in agent
The tool exists but wasn't exported from tools/mod.rs or registered in the agent. This tool uses the working wizard code to detect connected providers, which is more reliable than check_provider_connection. Now the agent has access to list_deployment_capabilities which shows all connected providers, clusters, and registries at once. Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 7a211c8 commit c907da2

8 files changed

Lines changed: 609 additions & 20 deletions

File tree

Cargo.lock

Lines changed: 48 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/agent/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,7 @@ pub async fn run_interactive(
593593
.tool(CurrentContextTool::new())
594594
.tool(OpenProviderSettingsTool::new())
595595
.tool(CheckProviderConnectionTool::new())
596+
.tool(ListDeploymentCapabilitiesTool::new())
596597
// Deployment tools for service management
597598
.tool(ListDeploymentConfigsTool::new())
598599
.tool(TriggerDeploymentTool::new())
@@ -707,6 +708,7 @@ pub async fn run_interactive(
707708
.tool(CurrentContextTool::new())
708709
.tool(OpenProviderSettingsTool::new())
709710
.tool(CheckProviderConnectionTool::new())
711+
.tool(ListDeploymentCapabilitiesTool::new())
710712
// Deployment tools for service management
711713
.tool(ListDeploymentConfigsTool::new())
712714
.tool(TriggerDeploymentTool::new())
@@ -812,6 +814,7 @@ pub async fn run_interactive(
812814
.tool(CurrentContextTool::new())
813815
.tool(OpenProviderSettingsTool::new())
814816
.tool(CheckProviderConnectionTool::new())
817+
.tool(ListDeploymentCapabilitiesTool::new())
815818
// Deployment tools for service management
816819
.tool(ListDeploymentConfigsTool::new())
817820
.tool(TriggerDeploymentTool::new())
@@ -2265,6 +2268,7 @@ pub async fn run_query(
22652268
.tool(CurrentContextTool::new())
22662269
.tool(OpenProviderSettingsTool::new())
22672270
.tool(CheckProviderConnectionTool::new())
2271+
.tool(ListDeploymentCapabilitiesTool::new())
22682272
// Deployment tools for service management
22692273
.tool(ListDeploymentConfigsTool::new())
22702274
.tool(TriggerDeploymentTool::new())
@@ -2347,6 +2351,7 @@ pub async fn run_query(
23472351
.tool(CurrentContextTool::new())
23482352
.tool(OpenProviderSettingsTool::new())
23492353
.tool(CheckProviderConnectionTool::new())
2354+
.tool(ListDeploymentCapabilitiesTool::new())
23502355
// Deployment tools for service management
23512356
.tool(ListDeploymentConfigsTool::new())
23522357
.tool(TriggerDeploymentTool::new())
@@ -2418,6 +2423,7 @@ pub async fn run_query(
24182423
.tool(CurrentContextTool::new())
24192424
.tool(OpenProviderSettingsTool::new())
24202425
.tool(CheckProviderConnectionTool::new())
2426+
.tool(ListDeploymentCapabilitiesTool::new())
24212427
// Deployment tools for service management
24222428
.tool(ListDeploymentConfigsTool::new())
24232429
.tool(TriggerDeploymentTool::new())

src/agent/tools/mod.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -172,8 +172,9 @@ pub use kubelint::KubelintTool;
172172
pub use plan::{PlanCreateTool, PlanListTool, PlanNextTool, PlanUpdateTool};
173173
pub use platform::{
174174
CheckProviderConnectionTool, CurrentContextTool, DeployServiceTool, GetDeploymentStatusTool,
175-
GetServiceLogsTool, ListDeploymentConfigsTool, ListDeploymentsTool, ListOrganizationsTool,
176-
ListProjectsTool, OpenProviderSettingsTool, SelectProjectTool, TriggerDeploymentTool,
175+
GetServiceLogsTool, ListDeploymentCapabilitiesTool, ListDeploymentConfigsTool,
176+
ListDeploymentsTool, ListOrganizationsTool, ListProjectsTool, OpenProviderSettingsTool,
177+
SelectProjectTool, TriggerDeploymentTool,
177178
};
178179
pub use prometheus_connect::PrometheusConnectTool;
179180
pub use prometheus_discover::PrometheusDiscoverTool;

src/agent/tools/platform/create_deployment_config.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -267,25 +267,31 @@ A deployment config defines how to build and deploy a service, including:
267267
};
268268

269269
// Build the request
270+
// Note: Send both field name variants (dockerfile/dockerfilePath, context/buildContext)
271+
// for backend compatibility - different endpoints may expect different field names
270272
let request = CreateDeploymentConfigRequest {
273+
project_id: args.project_id.clone(),
271274
service_name: args.service_name.clone(),
272275
repository_id: args.repository_id,
273276
repository_full_name: args.repository_full_name.clone(),
274277
dockerfile_path: args.dockerfile_path.clone(),
278+
dockerfile: args.dockerfile_path.clone(), // Alias for backend compatibility
275279
build_context: args.build_context.clone(),
280+
context: args.build_context.clone(), // Alias for backend compatibility
276281
port: args.port,
277282
branch: args.branch.clone(),
278283
target_type: args.target_type.clone(),
279-
provider: args.provider.clone(),
284+
cloud_provider: args.provider.clone(),
280285
environment_id: args.environment_id.clone(),
281286
cluster_id: args.cluster_id.clone(),
282287
registry_id: args.registry_id.clone(),
283288
auto_deploy_enabled: args.auto_deploy_enabled,
284-
deployment_strategy: None,
289+
is_public: None,
290+
cloud_runner_config: None,
285291
};
286292

287293
// Create the deployment config
288-
match client.create_deployment_config(&args.project_id, &request).await {
294+
match client.create_deployment_config(&request).await {
289295
Ok(config) => {
290296
let result = json!({
291297
"success": true,

src/wizard/cloud_provider_data.rs

Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
//! Cloud provider regions and machine types for the deployment wizard
2+
//!
3+
//! This module contains static data for cloud provider options,
4+
//! matching the frontend's cloudProviderData.ts for consistency.
5+
6+
use crate::platform::api::types::CloudProvider;
7+
8+
/// A cloud region/location option
9+
#[derive(Debug, Clone)]
10+
pub struct CloudRegion {
11+
/// Region ID (e.g., "nbg1", "us-central1")
12+
pub id: &'static str,
13+
/// Human-readable name (e.g., "Nuremberg", "Iowa")
14+
pub name: &'static str,
15+
/// Geographic location (e.g., "Germany", "US Central")
16+
pub location: &'static str,
17+
}
18+
19+
/// A machine/instance type option
20+
#[derive(Debug, Clone)]
21+
pub struct MachineType {
22+
/// Machine type ID (e.g., "cx22", "e2-small")
23+
pub id: &'static str,
24+
/// Display name
25+
pub name: &'static str,
26+
/// Number of vCPUs (as string to handle fractional)
27+
pub cpu: &'static str,
28+
/// Memory amount (e.g., "4 GB")
29+
pub memory: &'static str,
30+
/// Optional description (e.g., "Shared Intel", "ARM64")
31+
pub description: Option<&'static str>,
32+
}
33+
34+
// =============================================================================
35+
// Hetzner Cloud
36+
// =============================================================================
37+
38+
/// Hetzner Cloud locations
39+
pub static HETZNER_LOCATIONS: &[CloudRegion] = &[
40+
// Europe
41+
CloudRegion { id: "nbg1", name: "Nuremberg", location: "Germany" },
42+
CloudRegion { id: "fsn1", name: "Falkenstein", location: "Germany" },
43+
CloudRegion { id: "hel1", name: "Helsinki", location: "Finland" },
44+
// Americas
45+
CloudRegion { id: "ash", name: "Ashburn", location: "US East" },
46+
CloudRegion { id: "hil", name: "Hillsboro", location: "US West" },
47+
// Asia Pacific
48+
CloudRegion { id: "sin", name: "Singapore", location: "Southeast Asia" },
49+
];
50+
51+
/// Hetzner Cloud server types (updated January 2026 naming)
52+
pub static HETZNER_SERVER_TYPES: &[MachineType] = &[
53+
// Shared vCPU - CX Series (Intel/AMD cost-optimized)
54+
MachineType { id: "cx23", name: "CX23", cpu: "2", memory: "4 GB", description: Some("Shared Intel/AMD") },
55+
MachineType { id: "cx33", name: "CX33", cpu: "4", memory: "8 GB", description: Some("Shared Intel/AMD") },
56+
MachineType { id: "cx43", name: "CX43", cpu: "8", memory: "16 GB", description: Some("Shared Intel/AMD") },
57+
MachineType { id: "cx53", name: "CX53", cpu: "16", memory: "32 GB", description: Some("Shared Intel/AMD") },
58+
// Shared vCPU - CPX Series (AMD regular)
59+
MachineType { id: "cpx22", name: "CPX22", cpu: "2", memory: "4 GB", description: Some("Shared AMD") },
60+
MachineType { id: "cpx32", name: "CPX32", cpu: "4", memory: "8 GB", description: Some("Shared AMD") },
61+
MachineType { id: "cpx42", name: "CPX42", cpu: "8", memory: "16 GB", description: Some("Shared AMD") },
62+
MachineType { id: "cpx52", name: "CPX52", cpu: "12", memory: "24 GB", description: Some("Shared AMD") },
63+
MachineType { id: "cpx62", name: "CPX62", cpu: "16", memory: "32 GB", description: Some("Shared AMD") },
64+
// Dedicated vCPU - CCX Series (AMD)
65+
MachineType { id: "ccx13", name: "CCX13", cpu: "2", memory: "8 GB", description: Some("Dedicated AMD") },
66+
MachineType { id: "ccx23", name: "CCX23", cpu: "4", memory: "16 GB", description: Some("Dedicated AMD") },
67+
MachineType { id: "ccx33", name: "CCX33", cpu: "8", memory: "32 GB", description: Some("Dedicated AMD") },
68+
MachineType { id: "ccx43", name: "CCX43", cpu: "16", memory: "64 GB", description: Some("Dedicated AMD") },
69+
MachineType { id: "ccx53", name: "CCX53", cpu: "32", memory: "128 GB", description: Some("Dedicated AMD") },
70+
MachineType { id: "ccx63", name: "CCX63", cpu: "48", memory: "192 GB", description: Some("Dedicated AMD") },
71+
// ARM - CAX Series (Ampere)
72+
MachineType { id: "cax11", name: "CAX11", cpu: "2", memory: "4 GB", description: Some("ARM64 Ampere") },
73+
MachineType { id: "cax21", name: "CAX21", cpu: "4", memory: "8 GB", description: Some("ARM64 Ampere") },
74+
MachineType { id: "cax31", name: "CAX31", cpu: "8", memory: "16 GB", description: Some("ARM64 Ampere") },
75+
MachineType { id: "cax41", name: "CAX41", cpu: "16", memory: "32 GB", description: Some("ARM64 Ampere") },
76+
];
77+
78+
// =============================================================================
79+
// GCP (Google Cloud Platform)
80+
// =============================================================================
81+
82+
/// GCP regions
83+
pub static GCP_REGIONS: &[CloudRegion] = &[
84+
// Americas
85+
CloudRegion { id: "us-central1", name: "Iowa", location: "US Central" },
86+
CloudRegion { id: "us-east1", name: "South Carolina", location: "US East" },
87+
CloudRegion { id: "us-east4", name: "Virginia", location: "US East" },
88+
CloudRegion { id: "us-west1", name: "Oregon", location: "US West" },
89+
CloudRegion { id: "us-west2", name: "Los Angeles", location: "US West" },
90+
// Europe
91+
CloudRegion { id: "europe-west1", name: "Belgium", location: "Europe" },
92+
CloudRegion { id: "europe-west2", name: "London", location: "UK" },
93+
CloudRegion { id: "europe-west3", name: "Frankfurt", location: "Germany" },
94+
CloudRegion { id: "europe-west4", name: "Netherlands", location: "Europe" },
95+
CloudRegion { id: "europe-north1", name: "Finland", location: "Europe" },
96+
// Asia Pacific
97+
CloudRegion { id: "asia-east1", name: "Taiwan", location: "Asia Pacific" },
98+
CloudRegion { id: "asia-northeast1", name: "Tokyo", location: "Japan" },
99+
CloudRegion { id: "asia-southeast1", name: "Singapore", location: "Southeast Asia" },
100+
CloudRegion { id: "australia-southeast1", name: "Sydney", location: "Australia" },
101+
];
102+
103+
/// GCP machine types (Compute Engine)
104+
pub static GCP_MACHINE_TYPES: &[MachineType] = &[
105+
// E2 Series (Cost-optimized)
106+
MachineType { id: "e2-micro", name: "e2-micro", cpu: "0.25", memory: "1 GB", description: Some("Shared-core") },
107+
MachineType { id: "e2-small", name: "e2-small", cpu: "0.5", memory: "2 GB", description: Some("Shared-core") },
108+
MachineType { id: "e2-medium", name: "e2-medium", cpu: "1", memory: "4 GB", description: Some("Shared-core") },
109+
MachineType { id: "e2-standard-2", name: "e2-standard-2", cpu: "2", memory: "8 GB", description: None },
110+
MachineType { id: "e2-standard-4", name: "e2-standard-4", cpu: "4", memory: "16 GB", description: None },
111+
MachineType { id: "e2-standard-8", name: "e2-standard-8", cpu: "8", memory: "32 GB", description: None },
112+
// N2 Series (Balanced)
113+
MachineType { id: "n2-standard-2", name: "n2-standard-2", cpu: "2", memory: "8 GB", description: None },
114+
MachineType { id: "n2-standard-4", name: "n2-standard-4", cpu: "4", memory: "16 GB", description: None },
115+
MachineType { id: "n2-standard-8", name: "n2-standard-8", cpu: "8", memory: "32 GB", description: None },
116+
];
117+
118+
// =============================================================================
119+
// Helper Functions
120+
// =============================================================================
121+
122+
/// Get regions for a cloud provider
123+
pub fn get_regions_for_provider(provider: &CloudProvider) -> &'static [CloudRegion] {
124+
match provider {
125+
CloudProvider::Hetzner => HETZNER_LOCATIONS,
126+
CloudProvider::Gcp => GCP_REGIONS,
127+
_ => &[], // AWS, Azure not yet supported for Cloud Runner
128+
}
129+
}
130+
131+
/// Get machine types for a cloud provider
132+
pub fn get_machine_types_for_provider(provider: &CloudProvider) -> &'static [MachineType] {
133+
match provider {
134+
CloudProvider::Hetzner => HETZNER_SERVER_TYPES,
135+
CloudProvider::Gcp => GCP_MACHINE_TYPES,
136+
_ => &[], // AWS, Azure not yet supported for Cloud Runner
137+
}
138+
}
139+
140+
/// Get default region for a provider
141+
pub fn get_default_region(provider: &CloudProvider) -> &'static str {
142+
match provider {
143+
CloudProvider::Hetzner => "nbg1",
144+
CloudProvider::Gcp => "us-central1",
145+
_ => "",
146+
}
147+
}
148+
149+
/// Get default machine type for a provider
150+
pub fn get_default_machine_type(provider: &CloudProvider) -> &'static str {
151+
match provider {
152+
CloudProvider::Hetzner => "cx23",
153+
CloudProvider::Gcp => "e2-small",
154+
_ => "",
155+
}
156+
}
157+
158+
/// Format region for display: "Nuremberg (Germany)"
159+
pub fn format_region_display(region: &CloudRegion) -> String {
160+
format!("{} ({})", region.name, region.location)
161+
}
162+
163+
/// Format machine type for display: "cx22 · 2 vCPU · 4 GB"
164+
pub fn format_machine_type_display(machine: &MachineType) -> String {
165+
let base = format!("{} · {} vCPU · {}", machine.name, machine.cpu, machine.memory);
166+
if let Some(desc) = machine.description {
167+
format!("{} · {}", base, desc)
168+
} else {
169+
base
170+
}
171+
}
172+
173+
#[cfg(test)]
174+
mod tests {
175+
use super::*;
176+
177+
#[test]
178+
fn test_hetzner_locations() {
179+
assert!(!HETZNER_LOCATIONS.is_empty());
180+
assert!(HETZNER_LOCATIONS.iter().any(|r| r.id == "nbg1"));
181+
}
182+
183+
#[test]
184+
fn test_hetzner_machine_types() {
185+
assert!(!HETZNER_SERVER_TYPES.is_empty());
186+
assert!(HETZNER_SERVER_TYPES.iter().any(|m| m.id == "cx23"));
187+
}
188+
189+
#[test]
190+
fn test_gcp_regions() {
191+
assert!(!GCP_REGIONS.is_empty());
192+
assert!(GCP_REGIONS.iter().any(|r| r.id == "us-central1"));
193+
}
194+
195+
#[test]
196+
fn test_gcp_machine_types() {
197+
assert!(!GCP_MACHINE_TYPES.is_empty());
198+
assert!(GCP_MACHINE_TYPES.iter().any(|m| m.id == "e2-small"));
199+
}
200+
201+
#[test]
202+
fn test_get_regions_for_provider() {
203+
let hetzner_regions = get_regions_for_provider(&CloudProvider::Hetzner);
204+
assert!(!hetzner_regions.is_empty());
205+
206+
let gcp_regions = get_regions_for_provider(&CloudProvider::Gcp);
207+
assert!(!gcp_regions.is_empty());
208+
}
209+
210+
#[test]
211+
fn test_format_region_display() {
212+
let region = &HETZNER_LOCATIONS[0];
213+
let display = format_region_display(region);
214+
assert!(display.contains("Nuremberg"));
215+
assert!(display.contains("Germany"));
216+
}
217+
218+
#[test]
219+
fn test_format_machine_type_display() {
220+
let machine = &HETZNER_SERVER_TYPES[0];
221+
let display = format_machine_type_display(machine);
222+
assert!(display.contains("CX23"));
223+
assert!(display.contains("2 vCPU"));
224+
assert!(display.contains("4 GB"));
225+
}
226+
}

0 commit comments

Comments
 (0)