diff --git a/internal/cmd/config.go b/internal/cmd/config.go index a8c01d6..10e0f34 100644 --- a/internal/cmd/config.go +++ b/internal/cmd/config.go @@ -82,6 +82,8 @@ func newConfigShowCmd() *cobra.Command { io.Printf("%s %s\n", ui.Label("gitHubToken:", w), ui.Dim("(not set)")) } + io.Printf("%s %v\n", ui.Label("useSSHIdentityFile:", w), rc.UseSSHIdentityFile) + return nil }, } @@ -95,7 +97,7 @@ func newConfigSetCmd() *cobra.Command { return &cobra.Command{ Use: "set ", Short: "Set a config value", - Long: "Set a config value. Use 'chunk auth set ' to store credentials with validation.\n\nUser keys: model\nProject keys: orgID, validation.sidecarImage", + Long: "Set a config value. Use 'chunk auth set ' to store credentials with validation.\n\nUser keys: model, useSSHIdentityFile\nProject keys: orgID, validation.sidecarImage", Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { io := iostream.FromCmd(cmd) @@ -131,7 +133,7 @@ func newConfigSetCmd() *cobra.Command { if !config.ValidConfigKeys[key] { return &userError{ msg: fmt.Sprintf("Unknown config key: %q.", key), - detail: "Supported keys: model, orgID, validation.sidecarImage.", + detail: "Supported keys: model, useSSHIdentityFile, orgID, validation.sidecarImage.", errMsg: fmt.Sprintf("unknown config key %q", key), } } @@ -141,8 +143,22 @@ func newConfigSetCmd() *cobra.Command { return &userError{msg: msgCouldNotLoadConfig, suggestion: configFilePermHint, err: err} } - if key == "model" { + switch key { + case "model": cfg.Model = value + case "useSSHIdentityFile": + switch value { + case "true", "1": + cfg.UseSSHIdentityFile = true + case "false", "0": + cfg.UseSSHIdentityFile = false + default: + return &userError{ + msg: fmt.Sprintf("Invalid value %q for useSSHIdentityFile.", value), + detail: "Accepted values: true, false.", + errMsg: fmt.Sprintf("invalid boolean value %q", value), + } + } } if err := config.Save(cfg); err != nil { diff --git a/internal/cmd/validate.go b/internal/cmd/validate.go index 72e3b7c..651b0eb 100644 --- a/internal/cmd/validate.go +++ b/internal/cmd/validate.go @@ -310,7 +310,19 @@ func openSSHSession(ctx context.Context, sidecarID, identityFile, workdir string if err != nil { return nil, "", err } - authSock := os.Getenv(config.EnvSSHAuthSock) + if identityFile == "" { + if userCfg, err := config.Load(); err == nil && userCfg.UseSSHIdentityFile { + var keyErr error + identityFile, keyErr = sidecar.DefaultKeyPath() + if keyErr != nil { + streams.ErrPrintf("warning: could not resolve SSH identity file: %v\n", keyErr) + } + } + } + var authSock string + if identityFile == "" { + authSock = os.Getenv(config.EnvSSHAuthSock) + } session, err := sidecar.OpenSession(ctx, client, sidecarID, identityFile, authSock) if err != nil { return nil, "", &userError{msg: "Could not open SSH session to sidecar.", err: err} diff --git a/internal/config/config.go b/internal/config/config.go index 3d4f530..2964e04 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -99,10 +99,11 @@ func LoadEnv(ctx context.Context) (EnvVars, error) { // UserConfig is the on-disk JSON config. type UserConfig struct { - AnthropicAPIKey string `json:"anthropicAPIKey,omitempty"` - CircleCIToken string `json:"circleCIToken,omitempty"` - GitHubToken string `json:"gitHubToken,omitempty"` - Model string `json:"model,omitempty"` + AnthropicAPIKey string `json:"anthropicAPIKey,omitempty"` + CircleCIToken string `json:"circleCIToken,omitempty"` + GitHubToken string `json:"gitHubToken,omitempty"` + Model string `json:"model,omitempty"` + UseSSHIdentityFile bool `json:"useSSHIdentityFile,omitempty"` // LegacyAPIKey reads the pre-rename "apiKey" field so existing users don't // silently lose their stored Anthropic key on upgrade. Migrated into @@ -125,6 +126,7 @@ type ResolvedConfig struct { ModelSource string AnalyzeModel string PromptModel string + UseSSHIdentityFile bool } // Load reads the config file. Returns empty config if not found. @@ -257,6 +259,7 @@ func Resolve(flagAPIKey, flagModel string) (ResolvedConfig, error) { rc.CircleCIBaseURL = env.CircleCIBaseURL rc.AnthropicBaseURL = env.AnthropicBaseURL rc.GitHubAPIURL = env.GitHubAPIURL + rc.UseSSHIdentityFile = cfg.UseSSHIdentityFile return rc, err } @@ -273,7 +276,8 @@ func MaskKey(key string) string { // Credentials (anthropicAPIKey, circleCIToken) are intentionally excluded — // users should use "auth set" which validates before storing. var ValidConfigKeys = map[string]bool{ - "model": true, + "model": true, + "useSSHIdentityFile": true, } // ValidProjectConfigKeys are the keys accepted by "config set" that write to