1package config
2
3import (
4 "context"
5 "fmt"
6 "os"
7 "strings"
8 "time"
9
10 "github.com/charmbracelet/crush/internal/llm/tools/shell"
11 "github.com/charmbracelet/crush/internal/logging"
12)
13
14// ExecuteCommand executes a shell command and returns the output
15// This is a shared utility that can be used by both provider config and tools
16func ExecuteCommand(ctx context.Context, command string, workingDir string) (string, error) {
17 if workingDir == "" {
18 workingDir = WorkingDirectory()
19 }
20
21 persistentShell := shell.GetPersistentShell(workingDir)
22
23 stdout, stderr, err := persistentShell.Exec(ctx, command)
24 if err != nil {
25 logging.Debug("Command execution failed", "command", command, "error", err, "stderr", stderr)
26 return "", fmt.Errorf("command execution failed: %w", err)
27 }
28
29 return strings.TrimSpace(stdout), nil
30}
31
32// ResolveAPIKey resolves an API key that can be either:
33// - A direct string value
34// - An environment variable (prefixed with $)
35// - A shell command (wrapped in $(...))
36func ResolveAPIKey(apiKey string) (string, error) {
37 if !strings.HasPrefix(apiKey, "$") {
38 return apiKey, nil
39 }
40
41 if strings.HasPrefix(apiKey, "$(") && strings.HasSuffix(apiKey, ")") {
42 command := strings.TrimSuffix(strings.TrimPrefix(apiKey, "$("), ")")
43 logging.Debug("Resolving API key from command", "command", command)
44 return resolveCommandAPIKey(command)
45 }
46
47 envVar := strings.TrimPrefix(apiKey, "$")
48 if value := os.Getenv(envVar); value != "" {
49 logging.Debug("Resolved environment variable", "envVar", envVar, "value", value)
50 return value, nil
51 }
52
53 logging.Debug("Environment variable not found", "envVar", envVar)
54
55 return "", fmt.Errorf("environment variable %s not found", envVar)
56}
57
58// resolveCommandAPIKey executes a command to get an API key, with caching support
59func resolveCommandAPIKey(command string) (string, error) {
60 ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
61 defer cancel()
62
63 logging.Debug("Executing command for API key", "command", command)
64
65 workingDir := WorkingDirectory()
66
67 result, err := ExecuteCommand(ctx, command, workingDir)
68 if err != nil {
69 return "", fmt.Errorf("failed to execute API key command: %w", err)
70 }
71 logging.Debug("Command executed successfully", "command", command, "result", result)
72 return result, nil
73}
74