- Interactive deploy command with 8-step walkthrough: framework → provider → token → SSH → server → inference → tailscale → discord - .env file generation from walkthrough config - DeploymentConfig struct with framework-aware defaults - Inference API client with validation for Venice, OpenRouter, OpenAI, Anthropic - Hetzner Cloud provider: token validation, SSH key listing - DotEnv parser/writer with schema validation - Destroy command with confirmation prompt - Validation subcommand for checking existing .env files - All tests passing, go vet clean
166 lines
3.8 KiB
Go
166 lines
3.8 KiB
Go
// Package config defines the deployment configuration for OpenBoatmobile.
|
|
package config
|
|
|
|
import (
|
|
"strings"
|
|
)
|
|
|
|
// DeploymentConfig holds all configuration gathered during the walkthrough.
|
|
type DeploymentConfig struct {
|
|
// Framework selection
|
|
Framework string // "hermes" or "openclaw"
|
|
|
|
// Cloud provider
|
|
CloudProvider string // "hetzner" or "digitalocean"
|
|
|
|
// Provider tokens (sensitive)
|
|
HetznerToken string
|
|
DOToken string
|
|
|
|
// SSH configuration
|
|
SSHKeyNames []string
|
|
SSHKeyFingerprints []string
|
|
|
|
// Server configuration
|
|
ServerName string
|
|
Location string // hetzner: ash, fsn1, nbg1, hel1
|
|
Region string // DO: nyc3, sfo2, ams3, etc.
|
|
ServerType string // hetzner: cpx21, cx23, etc.
|
|
DropletSize string // DO: s-2vcpu-4gb, etc.
|
|
AgentName string
|
|
AgentTimezone string
|
|
|
|
// Inference
|
|
InferenceProvider string // "venice", "openrouter", "openai", "anthropic", "custom"
|
|
InferenceAPIKey string
|
|
InferenceBaseURL string
|
|
|
|
// Fallback inference
|
|
FallbackProviders []InferenceProviderConfig
|
|
|
|
// Model
|
|
PrimaryModel string
|
|
PrimaryModelName string
|
|
FallbackModels []string
|
|
VeniceBaseURL string
|
|
|
|
// Docker (Hermes only)
|
|
DockerEnabled bool
|
|
|
|
// OpenClaw-specific
|
|
OpenClawVersion string
|
|
NodeVersion string
|
|
EnableSwap bool
|
|
SwapSizeGB int
|
|
EnableFail2ban bool
|
|
EnableUnattendedUpgrades bool
|
|
|
|
// Tailscale
|
|
EnableTailscale bool
|
|
TailscaleAuthKey string
|
|
TailnetDomain string
|
|
|
|
// Discord
|
|
EnableDiscord bool
|
|
DiscordBotToken string
|
|
DiscordServerID string
|
|
DiscordUserIDs []string
|
|
// Hermes-specific Discord
|
|
DiscordHomeChannel string
|
|
DiscordAllowedUsers string
|
|
DiscordAutoThread bool
|
|
|
|
// Gateway (Hermes)
|
|
GatewayToken string
|
|
GatewayAllowedUsers string
|
|
GatewayAllowAllUsers bool
|
|
|
|
// Optional integrations
|
|
BraveSearchAPIKey string
|
|
}
|
|
|
|
// InferenceProviderConfig holds a single inference provider's config.
|
|
type InferenceProviderConfig struct {
|
|
Provider string
|
|
APIKey string
|
|
BaseURL string
|
|
}
|
|
|
|
// AdminUser returns the admin username based on framework selection.
|
|
func (c *DeploymentConfig) AdminUser() string {
|
|
return c.Framework // "hermes" or "openclaw"
|
|
}
|
|
|
|
// MonthlyCostEstimate returns an estimated monthly cost string.
|
|
func (c *DeploymentConfig) MonthlyCostEstimate() string {
|
|
switch c.CloudProvider {
|
|
case "hetzner":
|
|
return hetznerPrice(c.ServerType)
|
|
case "digitalocean":
|
|
return doPrice(c.DropletSize)
|
|
default:
|
|
return "unknown"
|
|
}
|
|
}
|
|
|
|
func hetznerPrice(serverType string) string {
|
|
prices := map[string]string{
|
|
"cx22": "€3.79/mo",
|
|
"cx23": "€5.83/mo",
|
|
"cpx21": "€4.49/mo",
|
|
"cpx31": "€8.98/mo",
|
|
"cpx41": "€17.96/mo",
|
|
}
|
|
if p, ok := prices[serverType]; ok {
|
|
return p
|
|
}
|
|
return "see Hetzner pricing"
|
|
}
|
|
|
|
func doPrice(size string) string {
|
|
prices := map[string]string{
|
|
"s-1vcpu-1gb": "$6/mo",
|
|
"s-1vcpu-2gb": "$12/mo",
|
|
"s-2vcpu-4gb": "$24/mo",
|
|
"s-4vcpu-8gb": "$48/mo",
|
|
"g-2vcpu-8gb": "$63/mo",
|
|
}
|
|
if p, ok := prices[size]; ok {
|
|
return p
|
|
}
|
|
return "see DO pricing"
|
|
}
|
|
|
|
// LocationOrRegion returns the location (Hetzner) or region (DO) string.
|
|
func LocationOrRegion(c *DeploymentConfig) string {
|
|
if c.CloudProvider == "hetzner" {
|
|
return c.Location
|
|
}
|
|
return c.Region
|
|
}
|
|
|
|
// ServerTypeOrDroplet returns the server type or droplet size string.
|
|
func ServerTypeOrDroplet(c *DeploymentConfig) string {
|
|
if c.CloudProvider == "hetzner" {
|
|
return c.ServerType
|
|
}
|
|
return c.DropletSize
|
|
}
|
|
|
|
// SSHKeySummary returns a human-readable summary of the SSH key config.
|
|
func SSHKeySummary(c *DeploymentConfig) string {
|
|
if len(c.SSHKeyNames) > 0 {
|
|
return strings.Join(c.SSHKeyNames, ", ")
|
|
}
|
|
if len(c.SSHKeyFingerprints) > 0 {
|
|
return "****" + c.SSHKeyFingerprints[0][max(0, len(c.SSHKeyFingerprints[0])-4):]
|
|
}
|
|
return "(none)"
|
|
}
|
|
|
|
func max(a, b int) int {
|
|
if a > b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|