Change module path from 'cburn' to 'github.com/theirongolddev/cburn' to enable standard Go remote installation: go install github.com/theirongolddev/cburn@latest This is a BREAKING CHANGE for any external code importing this module (though as a CLI tool, this is unlikely to affect anyone). All internal imports updated to use the new module path. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
149 lines
3.5 KiB
Go
149 lines
3.5 KiB
Go
package cmd
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/theirongolddev/cburn/internal/config"
|
|
"github.com/theirongolddev/cburn/internal/source"
|
|
"github.com/theirongolddev/cburn/internal/tui/theme"
|
|
|
|
"github.com/charmbracelet/huh"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
var setupCmd = &cobra.Command{
|
|
Use: "setup",
|
|
Short: "First-time setup wizard",
|
|
RunE: runSetup,
|
|
}
|
|
|
|
func init() {
|
|
rootCmd.AddCommand(setupCmd)
|
|
}
|
|
|
|
func runSetup(_ *cobra.Command, _ []string) error {
|
|
cfg, _ := config.Load()
|
|
files, _ := source.ScanDir(flagDataDir)
|
|
projectCount := source.CountProjects(files)
|
|
|
|
// Pre-populate from existing config
|
|
var sessionKey, adminKey string
|
|
days := cfg.General.DefaultDays
|
|
if days == 0 {
|
|
days = 30
|
|
}
|
|
themeName := cfg.Appearance.Theme
|
|
if themeName == "" {
|
|
themeName = "flexoki-dark"
|
|
}
|
|
|
|
// Build welcome description
|
|
welcomeDesc := "Let's configure your dashboard."
|
|
if len(files) > 0 {
|
|
welcomeDesc = fmt.Sprintf("Found %d sessions across %d projects in %s.",
|
|
len(files), projectCount, flagDataDir)
|
|
}
|
|
|
|
// Build placeholder text showing masked existing values
|
|
sessionPlaceholder := "sk-ant-sid... (Enter to skip)"
|
|
if key := config.GetSessionKey(cfg); key != "" {
|
|
sessionPlaceholder = maskAPIKey(key) + " (Enter to keep)"
|
|
}
|
|
adminPlaceholder := "sk-ant-admin-... (Enter to skip)"
|
|
if key := config.GetAdminAPIKey(cfg); key != "" {
|
|
adminPlaceholder = maskAPIKey(key) + " (Enter to keep)"
|
|
}
|
|
|
|
form := huh.NewForm(
|
|
huh.NewGroup(
|
|
huh.NewNote().
|
|
Title("Welcome to cburn").
|
|
Description(welcomeDesc).
|
|
Next(true).
|
|
NextLabel("Start"),
|
|
),
|
|
|
|
huh.NewGroup(
|
|
huh.NewInput().
|
|
Title("Claude.ai session key").
|
|
Description("For rate-limit and subscription data.\nclaude.ai > DevTools > Application > Cookies > sessionKey").
|
|
Placeholder(sessionPlaceholder).
|
|
EchoMode(huh.EchoModePassword).
|
|
Value(&sessionKey),
|
|
|
|
huh.NewInput().
|
|
Title("Anthropic Admin API key").
|
|
Description("For real cost data from the billing API.").
|
|
Placeholder(adminPlaceholder).
|
|
EchoMode(huh.EchoModePassword).
|
|
Value(&adminKey),
|
|
),
|
|
|
|
huh.NewGroup(
|
|
huh.NewSelect[int]().
|
|
Title("Default time range").
|
|
Options(
|
|
huh.NewOption("7 days", 7),
|
|
huh.NewOption("30 days", 30),
|
|
huh.NewOption("90 days", 90),
|
|
).
|
|
Value(&days),
|
|
|
|
huh.NewSelect[string]().
|
|
Title("Color theme").
|
|
Options(themeOpts()...).
|
|
Value(&themeName),
|
|
),
|
|
).WithTheme(huh.ThemeDracula())
|
|
|
|
if err := form.Run(); err != nil {
|
|
if errors.Is(err, huh.ErrUserAborted) {
|
|
fmt.Println("\n Setup cancelled.")
|
|
return nil
|
|
}
|
|
return fmt.Errorf("setup form: %w", err)
|
|
}
|
|
|
|
// Only overwrite keys if the user typed new ones
|
|
sessionKey = strings.TrimSpace(sessionKey)
|
|
if sessionKey != "" {
|
|
cfg.ClaudeAI.SessionKey = sessionKey
|
|
}
|
|
adminKey = strings.TrimSpace(adminKey)
|
|
if adminKey != "" {
|
|
cfg.AdminAPI.APIKey = adminKey
|
|
}
|
|
cfg.General.DefaultDays = days
|
|
cfg.Appearance.Theme = themeName
|
|
|
|
if err := config.Save(cfg); err != nil {
|
|
return fmt.Errorf("saving config: %w", err)
|
|
}
|
|
|
|
fmt.Printf("\n Saved to %s\n", config.Path())
|
|
fmt.Println(" Run `cburn setup` anytime to reconfigure.")
|
|
fmt.Println()
|
|
|
|
return nil
|
|
}
|
|
|
|
func themeOpts() []huh.Option[string] {
|
|
opts := make([]huh.Option[string], len(theme.All))
|
|
for i, t := range theme.All {
|
|
opts[i] = huh.NewOption(t.Name, t.Name)
|
|
}
|
|
return opts
|
|
}
|
|
|
|
func maskAPIKey(key string) string {
|
|
if len(key) > 16 {
|
|
return key[:8] + "..." + key[len(key)-4:]
|
|
}
|
|
if len(key) > 4 {
|
|
return key[:4] + "..."
|
|
}
|
|
return "****"
|
|
}
|