diff --git a/cmd/search.go b/cmd/search.go index 1636f5d..d9afc10 100644 --- a/cmd/search.go +++ b/cmd/search.go @@ -205,6 +205,7 @@ func outputJSON(hosts []config.SSHHost) { fmt.Printf(" \"port\": \"%s\",\n", escapeJSON(host.Port)) fmt.Printf(" \"identity\": \"%s\",\n", escapeJSON(host.Identity)) fmt.Printf(" \"proxy_jump\": \"%s\",\n", escapeJSON(host.ProxyJump)) + fmt.Printf(" \"proxy_command\": \"%s\",\n", escapeJSON(host.ProxyCommand)) fmt.Printf(" \"options\": \"%s\",\n", escapeJSON(host.Options)) fmt.Printf(" \"tags\": [") for j, tag := range host.Tags { diff --git a/internal/config/ssh.go b/internal/config/ssh.go index 9f7ae91..d308d56 100644 --- a/internal/config/ssh.go +++ b/internal/config/ssh.go @@ -19,6 +19,7 @@ type SSHHost struct { Port string Identity string ProxyJump string + ProxyCommand string Options string RemoteCommand string // Command to execute after SSH connection RequestTTY string // Request TTY (yes, no, force, auto) @@ -328,6 +329,10 @@ func parseSSHConfigFileWithProcessedFiles(configPath string, processedFiles map[ if currentHost != nil { currentHost.ProxyJump = value } + case "proxycommand": + if currentHost != nil { + currentHost.ProxyCommand = value + } case "remotecommand": if currentHost != nil { currentHost.RemoteCommand = value @@ -613,6 +618,13 @@ func AddSSHHostToFile(host SSHHost, configPath string) error { } } + if host.ProxyCommand != "" { + _, err = file.WriteString(fmt.Sprintf(" ProxyCommand=%s\n", host.ProxyCommand)) + if err != nil { + return err + } + } + if host.RemoteCommand != "" { _, err = file.WriteString(fmt.Sprintf(" RemoteCommand %s\n", host.RemoteCommand)) if err != nil { @@ -1044,6 +1056,9 @@ func UpdateSSHHostInFile(oldName string, newHost SSHHost, configPath string) err if newHost.ProxyJump != "" { newLines = append(newLines, " ProxyJump "+newHost.ProxyJump) } + if newHost.ProxyCommand != "" { + newLines = append(newLines, " ProxyCommand="+newHost.ProxyCommand) + } if newHost.RemoteCommand != "" { newLines = append(newLines, " RemoteCommand "+newHost.RemoteCommand) } @@ -1098,6 +1113,9 @@ func UpdateSSHHostInFile(oldName string, newHost SSHHost, configPath string) err if newHost.ProxyJump != "" { newLines = append(newLines, " ProxyJump "+newHost.ProxyJump) } + if newHost.ProxyCommand != "" { + newLines = append(newLines, " ProxyCommand="+newHost.ProxyCommand) + } if newHost.RemoteCommand != "" { newLines = append(newLines, " RemoteCommand "+newHost.RemoteCommand) } @@ -1188,6 +1206,9 @@ func UpdateSSHHostInFile(oldName string, newHost SSHHost, configPath string) err if newHost.ProxyJump != "" { newLines = append(newLines, " ProxyJump "+newHost.ProxyJump) } + if newHost.ProxyCommand != "" { + newLines = append(newLines, " ProxyCommand="+newHost.ProxyCommand) + } if newHost.RemoteCommand != "" { newLines = append(newLines, " RemoteCommand "+newHost.RemoteCommand) } @@ -1242,6 +1263,9 @@ func UpdateSSHHostInFile(oldName string, newHost SSHHost, configPath string) err if newHost.ProxyJump != "" { newLines = append(newLines, " ProxyJump "+newHost.ProxyJump) } + if newHost.ProxyCommand != "" { + newLines = append(newLines, " ProxyCommand="+newHost.ProxyCommand) + } if newHost.RemoteCommand != "" { newLines = append(newLines, " RemoteCommand "+newHost.RemoteCommand) } @@ -1742,6 +1766,9 @@ func UpdateMultiHostBlock(originalHosts, newHosts []string, commonProperties SSH if commonProperties.ProxyJump != "" { newLines = append(newLines, " ProxyJump "+commonProperties.ProxyJump) } + if commonProperties.ProxyCommand != "" { + newLines = append(newLines, " ProxyCommand="+commonProperties.ProxyCommand) + } if commonProperties.RemoteCommand != "" { newLines = append(newLines, " RemoteCommand "+commonProperties.RemoteCommand) } @@ -1828,6 +1855,9 @@ func UpdateMultiHostBlock(originalHosts, newHosts []string, commonProperties SSH if commonProperties.ProxyJump != "" { newLines = append(newLines, " ProxyJump "+commonProperties.ProxyJump) } + if commonProperties.ProxyCommand != "" { + newLines = append(newLines, " ProxyCommand="+commonProperties.ProxyCommand) + } if commonProperties.RemoteCommand != "" { newLines = append(newLines, " RemoteCommand "+commonProperties.RemoteCommand) } diff --git a/internal/ui/add_form.go b/internal/ui/add_form.go index 75a7cb4..eb3717b 100644 --- a/internal/ui/add_form.go +++ b/internal/ui/add_form.go @@ -91,6 +91,12 @@ func NewAddForm(hostname string, styles Styles, width, height int, configFile st inputs[proxyJumpInput].CharLimit = 200 inputs[proxyJumpInput].Width = 50 + // ProxyCommand input + inputs[proxyCommandInput] = textinput.New() + inputs[proxyCommandInput].Placeholder = "/usr/local/bin/wssh proxy %h" + inputs[proxyCommandInput].CharLimit = 200 + inputs[proxyCommandInput].Width = 50 + // SSH Options input inputs[optionsInput] = textinput.New() inputs[optionsInput].Placeholder = "-o Compression=yes -o ServerAliveInterval=60" @@ -138,6 +144,7 @@ const ( portInput identityInput proxyJumpInput + proxyCommandInput tagsInput // Advanced tab inputs optionsInput @@ -229,11 +236,11 @@ func (m *addFormModel) getFirstInputForTab(tab int) int { func (m *addFormModel) getInputsForCurrentTab() []int { switch m.currentTab { case tabGeneral: - return []int{nameInput, hostnameInput, userInput, portInput, identityInput, proxyJumpInput, tagsInput} + return []int{nameInput, hostnameInput, userInput, portInput, identityInput, proxyJumpInput, proxyCommandInput, tagsInput} case tabAdvanced: return []int{optionsInput, remoteCommandInput, requestTTYInput} default: - return []int{nameInput, hostnameInput, userInput, portInput, identityInput, proxyJumpInput, tagsInput} + return []int{nameInput, hostnameInput, userInput, portInput, identityInput, proxyJumpInput, proxyCommandInput, tagsInput} } } @@ -401,6 +408,7 @@ func (m *addFormModel) renderGeneralTab() string { {portInput, "Port"}, {identityInput, "Identity File"}, {proxyJumpInput, "ProxyJump"}, + {proxyCommandInput, "ProxyCommand"} {tagsInput, "Tags (comma-separated)"}, } @@ -489,6 +497,7 @@ func (m *addFormModel) submitForm() tea.Cmd { port := strings.TrimSpace(m.inputs[portInput].Value()) identity := strings.TrimSpace(m.inputs[identityInput].Value()) proxyJump := strings.TrimSpace(m.inputs[proxyJumpInput].Value()) + proxyCommand := strings.TrimSpace(m.inputs[proxyCommandInput].Value()) options := strings.TrimSpace(m.inputs[optionsInput].Value()) remoteCommand := strings.TrimSpace(m.inputs[remoteCommandInput].Value()) requestTTY := strings.TrimSpace(m.inputs[requestTTYInput].Value()) @@ -526,6 +535,7 @@ func (m *addFormModel) submitForm() tea.Cmd { Port: port, Identity: identity, ProxyJump: proxyJump, + ProxyCommand: proxyCommand, Options: config.ParseSSHOptionsFromCommand(options), RemoteCommand: remoteCommand, RequestTTY: requestTTY, diff --git a/internal/ui/edit_form.go b/internal/ui/edit_form.go index fdac316..eacba95 100644 --- a/internal/ui/edit_form.go +++ b/internal/ui/edit_form.go @@ -128,37 +128,44 @@ func NewEditForm(hostName string, styles Styles, width, height int, configFile s inputs[4].Width = 30 inputs[4].SetValue(host.ProxyJump) - // Options input + // ProxyCommand input inputs[5] = textinput.New() - inputs[5].Placeholder = "-o StrictHostKeyChecking=no" + inputs[5].Placeholder = "/bin/wssh proxy" inputs[5].CharLimit = 200 inputs[5].Width = 50 + inputs[5].SetValue(host.ProxyCommand) + + // Options input + inputs[6] = textinput.New() + inputs[6].Placeholder = "-o StrictHostKeyChecking=no" + inputs[6].CharLimit = 200 + inputs[6].Width = 50 if host.Options != "" { - inputs[5].SetValue(config.FormatSSHOptionsForCommand(host.Options)) + inputs[6].SetValue(config.FormatSSHOptionsForCommand(host.Options)) } // Tags input - inputs[6] = textinput.New() - inputs[6].Placeholder = "production, web, database" - inputs[6].CharLimit = 200 - inputs[6].Width = 50 + inputs[7] = textinput.New() + inputs[7].Placeholder = "production, web, database" + inputs[7].CharLimit = 200 + inputs[7].Width = 50 if len(host.Tags) > 0 { - inputs[6].SetValue(strings.Join(host.Tags, ", ")) + inputs[7].SetValue(strings.Join(host.Tags, ", ")) } // Remote Command input - inputs[7] = textinput.New() - inputs[7].Placeholder = "ls -la, htop, bash" - inputs[7].CharLimit = 300 - inputs[7].Width = 70 - inputs[7].SetValue(host.RemoteCommand) + inputs[8] = textinput.New() + inputs[8].Placeholder = "ls -la, htop, bash" + inputs[8].CharLimit = 300 + inputs[8].Width = 70 + inputs[8].SetValue(host.RemoteCommand) // RequestTTY input - inputs[8] = textinput.New() - inputs[8].Placeholder = "yes, no, force, auto" - inputs[8].CharLimit = 10 - inputs[8].Width = 30 - inputs[8].SetValue(host.RequestTTY) + inputs[9] = textinput.New() + inputs[9].Placeholder = "yes, no, force, auto" + inputs[9].CharLimit = 10 + inputs[9].Width = 30 + inputs[9].SetValue(host.RequestTTY) return &editFormModel{ hostInputs: hostInputs, @@ -253,7 +260,7 @@ func (m *editFormModel) updateFocus() tea.Cmd { func (m *editFormModel) getPropertiesForCurrentTab() []int { switch m.currentTab { case 0: // General - return []int{0, 1, 2, 3, 4, 6} // hostname, user, port, identity, proxyjump, tags + return []int{0, 1, 2, 3, 4, 5, 6} // hostname, user, port, identity, proxyjump, proxycommand, tags case 1: // Advanced return []int{5, 7, 8} // options, remotecommand, requesttty default: @@ -683,9 +690,10 @@ func (m *editFormModel) submitEditForm() tea.Cmd { port := strings.TrimSpace(m.inputs[2].Value()) // portInput identity := strings.TrimSpace(m.inputs[3].Value()) // identityInput proxyJump := strings.TrimSpace(m.inputs[4].Value()) // proxyJumpInput - options := strings.TrimSpace(m.inputs[5].Value()) // optionsInput - remoteCommand := strings.TrimSpace(m.inputs[7].Value()) // remoteCommandInput - requestTTY := strings.TrimSpace(m.inputs[8].Value()) // requestTTYInput + proxyCommand := strings.TrimSpace(m.inputs[5].Value()) // proxyCommandInput + options := strings.TrimSpace(m.inputs[6].Value()) // optionsInput + remoteCommand := strings.TrimSpace(m.inputs[8].Value()) // remoteCommandInput + requestTTY := strings.TrimSpace(m.inputs[9].Value()) // requestTTYInput // Set defaults if port == "" { @@ -723,6 +731,7 @@ func (m *editFormModel) submitEditForm() tea.Cmd { Port: port, Identity: identity, ProxyJump: proxyJump, + ProxyCommand: proxyCommand, Options: options, RemoteCommand: remoteCommand, RequestTTY: requestTTY, diff --git a/internal/ui/info_form.go b/internal/ui/info_form.go index 8b50364..50f386e 100644 --- a/internal/ui/info_form.go +++ b/internal/ui/info_form.go @@ -97,6 +97,7 @@ func (m *infoFormModel) View() string { {"Port", formatOptionalValue(m.host.Port)}, {"Identity File", formatOptionalValue(m.host.Identity)}, {"ProxyJump", formatOptionalValue(m.host.ProxyJump)}, + {"ProxyCommand", formatOptionalValue(m.host.ProxyCommand)}, {"SSH Options", formatSSHOptions(m.host.Options)}, {"Tags", formatTags(m.host.Tags)}, }