-
Notifications
You must be signed in to change notification settings - Fork 0
fix: improve Linux fzf IPC compatibility #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
26af765
c1dd52a
40c44c9
eb11571
5fa2f6c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -7,6 +7,7 @@ import ( | |||||||||||||||||||||||||||||||||||||||||||||||||
| "strconv" | ||||||||||||||||||||||||||||||||||||||||||||||||||
| "strings" | ||||||||||||||||||||||||||||||||||||||||||||||||||
| "time" | ||||||||||||||||||||||||||||||||||||||||||||||||||
| "net" | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| func maybeRunDynamicReloadAction(args []string) (bool, int) { | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -238,3 +239,28 @@ func isManagerCommandReady(manager string) bool { | |||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| return true | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| const maxReloadAttempts = 3 | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| func handleIPCReload(conn net.Conn, config any) error { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| var lastErr error | ||||||||||||||||||||||||||||||||||||||||||||||||||
| for attempt := 1; attempt <= maxReloadAttempts; attempt++ { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| if err := performReloadHandshake(conn, config); err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| lastErr = err | ||||||||||||||||||||||||||||||||||||||||||||||||||
| if attempt < maxReloadAttempts { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| sleepDuration := time.Duration(attempt*attempt*50) * time.Millisecond | ||||||||||||||||||||||||||||||||||||||||||||||||||
| time.Sleep(sleepDuration) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| continue | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
| return lastErr | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+247
to
+257
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Retry loop returns a stale error even after a successful retry. After any earlier failure, 🔧 Proposed fix func handleIPCReload(conn net.Conn, config any) error {
var lastErr error
for attempt := 1; attempt <= maxReloadAttempts; attempt++ {
if err := performReloadHandshake(conn, config); err != nil {
lastErr = err
if attempt < maxReloadAttempts {
sleepDuration := time.Duration(attempt*attempt*50) * time.Millisecond
time.Sleep(sleepDuration)
continue
}
- }
- return lastErr
+ return lastErr
+ }
+ return nil
}
return lastErr
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||
| return lastErr | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| func performReloadHandshake(conn net.Conn, config any) error { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| if conn == nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| return fmt.Errorf("connection is nil") | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
| return nil | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -2,21 +2,22 @@ package main | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "fmt" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "net" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "os" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "os/exec" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "strings" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "time" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func maybeRunIPCReloadAction(args []string) (bool, int) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| hasAction, query := parseIPCRequest(args, "--ipc-reload") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if !hasAction { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return false, 0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err := runIPCReload(query); err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fmt.Fprintf(os.Stderr, "fzf IPC reload failed: %v\n", err) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return true, 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return true, 0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -41,124 +42,97 @@ func maybeRunIPCQueryNotifyAction(args []string) (bool, int) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return true, 0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func parseIPCRequest(args []string, actionFlag string) (bool, string) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| hasAction := false | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| query := "" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for i := 0; i < len(args); i++ { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| switch args[i] { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case actionFlag: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| hasAction = true | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case "--": | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if i+1 < len(args) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| query = args[i+1] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return hasAction, query | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return hasAction, query | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func runIPCReload(query string) error { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fallbackFile := strings.TrimSpace(os.Getenv("FPF_IPC_FALLBACK_FILE")) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if fallbackFile == "" { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return fmt.Errorf("missing FPF_IPC_FALLBACK_FILE") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if _, err := os.Stat(fallbackFile); err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return err | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fzfHost := os.Getenv("FPF_FZF_LISTEN_HOST") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if fzfHost == "" { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fzfHost = "localhost:9812" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| managerOverride := normalizeManagerName(strings.TrimSpace(os.Getenv("FPF_IPC_MANAGER_OVERRIDE"))) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if managerOverride != "" && !isManagerSupported(managerOverride) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return fmt.Errorf("unsupported manager override") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Try curl first (fzf >= 0.42.0 with change:reload:) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err := runCurlReload(fzfHost, query); err == nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return nil | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| managerListCSV := strings.TrimSpace(os.Getenv("FPF_IPC_MANAGER_LIST")) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| bypassQueryCache := strings.TrimSpace(os.Getenv("FPF_BYPASS_QUERY_CACHE")) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if bypassQueryCache == "" { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| bypassQueryCache = "0" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Fallback to nc (netcat) for older fzf versions | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return runNetcatReload(fzfHost, query) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func runCurlReload(host, query string) error { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // fzf 0.42+ supports change:reload: which sends HTTP/1.1 POST | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| url := "http://" + host + "/fzf_reload" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| payload := "reload " + query | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| reloadCmd := buildDynamicReloadCommandForQuery(managerOverride, fallbackFile, managerListCSV, query, bypassQueryCache) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| actionPayload := "change-prompt(Search> )+reload(" + reloadCmd + ")" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cmd := exec.Command("curl", "-s", "-X", "POST", "-d", payload, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "--max-time", "1", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "-H", "Content-Type: application/octet-stream", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| url) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return sendFzfListenAction(actionPayload) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Run without waiting — fire and forget, fzf handles it | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cmd.Start() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cmd.Process.Release() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return nil | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+60
to
+73
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Curl path can panic and falsely report success, preventing fallback. Line 71 ignores 🔧 Proposed fix func runCurlReload(host, query string) error {
// fzf 0.42+ supports change:reload: which sends HTTP/1.1 POST
url := "http://" + host + "/fzf_reload"
payload := "reload " + query
cmd := exec.Command("curl", "-s", "-X", "POST", "-d", payload,
"--max-time", "1",
+ "--fail",
"-H", "Content-Type: application/octet-stream",
url)
- // Run without waiting — fire and forget, fzf handles it
- cmd.Start()
- cmd.Process.Release()
- return nil
+ if err := cmd.Run(); err != nil {
+ return fmt.Errorf("curl reload failed: %w", err)
+ }
+ return nil
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func buildDynamicReloadCommandForQuery(managerOverride, fallbackFile, managerListCSV, queryValue, bypassQueryCache string) string { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| parts := []string{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "FPF_SKIP_INSTALLED_MARKERS=1", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "FPF_BYPASS_QUERY_CACHE=" + shellQuote(bypassQueryCache), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "FPF_SKIP_QUERY_CACHE_WRITE=1", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "FPF_IPC_MANAGER_OVERRIDE=" + shellQuote(managerOverride), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "FPF_IPC_MANAGER_LIST=" + shellQuote(managerListCSV), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "FPF_IPC_FALLBACK_FILE=" + shellQuote(fallbackFile), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| shellQuote(os.Args[0]), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "--dynamic-reload", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "--", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| shellQuote(queryValue), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func runNetcatReload(host, query string) error { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // For older fzf: use netcat to send fzf IPC protocol message | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Protocol: one line per event, \n terminated | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Event format: "event payload\n" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| hostPort := strings.Split(host, ":") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if len(hostPort) != 2 { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return fmt.Errorf("invalid FPF_FZF_LISTEN_HOST: %s (expected host:port)", host) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return strings.Join(parts, " ") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| payload := fmt.Sprintf("reload %s\n", query) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func sendFzfListenAction(actionPayload string) error { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fzfPort := strings.TrimSpace(os.Getenv("FZF_PORT")) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if fzfPort == "" { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return fmt.Errorf("missing FZF_PORT") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Use timeout(1) wrapper for compatibility across Linux/macOS | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // timeout on Linux, gtimeout on macOS (coreutils) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| timeoutCmd := "timeout" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if _, err := exec.LookPath("timeout"); err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| timeoutCmd = "gtimeout" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| _ = timeoutCmd // reserved for future netcat variant use | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+76
to
94
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🌐 Web query:
💡 Result: fzf The POST body is interpreted as an fzf action list string (same syntax as in Minimal example (TCP): POST / HTTP/1.1
Host: localhost
Content-Length: 16
reload(ps -ef)(Optionally include Sources: fzf [1] (github.com) Citations:
HTTP/1.1 POST format required but raw protocol sent instead.
The timeout/nc setup is currently unused. Update the payload to use proper HTTP/1.1 formatting before sending over netcat. 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| host := "127.0.0.1" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| port := fzfPort | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if strings.Contains(fzfPort, ":") { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| host, port, _ = strings.Cut(fzfPort, ":") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| host = strings.TrimSpace(host) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| port = strings.TrimSpace(port) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if host == "" { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| host = "127.0.0.1" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| conn, err := net.DialTimeout("tcp", host, 1*time.Second) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return fmt.Errorf("connecting to fzf at %s: %w", host, err) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| defer conn.Close() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| targetURL := fmt.Sprintf("http://%s:%s", host, port) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| curlCmd := exec.Command( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "curl", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "--silent", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "--show-error", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "--fail", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "--max-time", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "2", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "-H", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "Content-Type: text/plain", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "--data-binary", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| actionPayload, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| targetURL, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| curlCmd.Env = os.Environ() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| curlCmd.Stdin = os.Stdin | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| curlCmd.Stdout = os.Stdout | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| curlCmd.Stderr = os.Stderr | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err := curlCmd.Run(); err == nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return nil | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| conn.SetDeadline(time.Now().Add(1 * time.Second)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| _, err = conn.Write([]byte(payload)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return fmt.Errorf("sending reload to fzf: %w", err) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| httpRequest := "POST / HTTP/1.1\r\n" + | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fmt.Sprintf("Host: %s:%s\r\n", host, port) + | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "Content-Type: text/plain\r\n" + | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fmt.Sprintf("Content-Length: %d\r\n", len(actionPayload)) + | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "\r\n" + | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| actionPayload | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Read response (fzf sends back the reload result) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| buf := make([]byte, 1024) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| conn.Read(buf) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return nil | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // parseIPCRequest parses CLI args for an IPC action flag and extracts the query. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func parseIPCRequest(args []string, actionFlag string) (bool, string) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| hasAction := false | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| query := "" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ncCmd := exec.Command("nc", "-w", "2", host, port) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ncCmd.Env = os.Environ() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ncCmd.Stdin = strings.NewReader(httpRequest) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ncCmd.Stdout = os.Stdout | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ncCmd.Stderr = os.Stderr | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for i := 0; i < len(args); i++ { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| switch args[i] { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case actionFlag: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| hasAction = true | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case "--": | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if i+1 < len(args) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| query = args[i+1] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return hasAction, query | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ncCmd.Run() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return hasAction, query | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // shellQuote wraps a string in single quotes, escaping any internal single quotes. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func shellQuote(value string) string { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if value == "" { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return "''" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🌐 Web query:
What is the minimum fzf version required for stable--listen+change:reloadbehavior used by external IPC reload requests?💡 Result:
Minimum fzf v0.56.1.
Reason: v0.56.1 fixed a race condition that could leave “stale results after
reload”, which directly affects setups that combinechange:reload(frequent reloads) with external--listenIPCPOSTreload requests. [1](
--listenitself (HTTP server acceptingPOSTaction sequences likereload(...)) has existed since v0.36.0.) [2]Sources:
[1] (github.com)
[2] (github.com)
Citations:
Minimum fzf version gate is incorrect and too permissive.
Line 834 uses
0.42.0, but stable--listen+change:reloadbehavior requires fzf >= 0.56.1. This version fixed a race condition that could leave stale results after reload, which directly impacts external IPC reload requests. The current gate would allow incompatible versions and break dynamic reload at runtime.🔧 Proposed fix
🤖 Prompt for AI Agents