From fce253b1906cfc6edb34c6472ce172c0eb6a1ade Mon Sep 17 00:00:00 2001 From: "zhenxing.shen" Date: Mon, 29 Jun 2026 14:16:41 +0800 Subject: [PATCH 1/2] fix(shellexec): only correct XDG vars when Wave runs as a Snap, and skip empty XDG values - Use SNAP_NAME == 'waveterm' instead of checking for any inherited SNAP variable, so child Snap processes do not trigger the XDG correction logic. - Remove empty values from varsToReplace before passing to UpdateCmdEnv so XDG_*_HOME variables are not explicitly set to empty strings. Previously an empty value would either unset the variable or set it to '' (depending on prior presence), breaking tools that rely on XDG spec defaults. Fixes #3336 --- pkg/shellexec/shellexec.go | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/pkg/shellexec/shellexec.go b/pkg/shellexec/shellexec.go index 35af5446a3..2e1f118dab 100644 --- a/pkg/shellexec/shellexec.go +++ b/pkg/shellexec/shellexec.go @@ -648,8 +648,12 @@ func StartLocalShellProc(logCtx context.Context, termSize waveobj.TermSize, cmdS overrides them to point to snap directories. We will get the correct values, if set, from the PAM environment. If the XDG variables are set in profile or in an RC file, it will be overridden when the shell initializes. + + Only apply this correction when Wave itself is running as a Snap (SNAP_NAME == "waveterm"), + not when $SNAP is merely inherited from a parent Snap process (e.g. a CLI tool + installed via Snap). */ - if os.Getenv("SNAP") != "" { + if os.Getenv("SNAP_NAME") == "waveterm" { log.Printf("Detected Snap installation, correcting XDG environment variables") varsToReplace := map[string]string{"XDG_CONFIG_HOME": "", "XDG_DATA_HOME": "", "XDG_CACHE_HOME": "", "XDG_RUNTIME_DIR": "", "XDG_CONFIG_DIRS": "", "XDG_DATA_DIRS": ""} pamEnvs := tryGetPamEnvVars() @@ -661,6 +665,15 @@ func StartLocalShellProc(logCtx context.Context, termSize waveobj.TermSize, cmdS } } } + // Remove keys that still have empty values so they are not explicitly set to + // empty strings in the child environment (which breaks tools that fall back to + // XDG spec defaults). When a key is absent from varsToReplace the child will + // either inherit the parent value or rely on shell rc files / spec defaults. + for k, v := range varsToReplace { + if v == "" { + delete(varsToReplace, k) + } + } log.Printf("Setting XDG environment variables to: %v", varsToReplace) shellutil.UpdateCmdEnv(ecmd, varsToReplace) } From 79d1959a99824950f59eb45123f0a5537368cb60 Mon Sep 17 00:00:00 2001 From: "zhenxing.shen" Date: Sun, 5 Jul 2026 09:29:42 +0800 Subject: [PATCH 2/2] docs: add docstrings for shellexec XDG/Snap fix functions --- pkg/shellexec/shellexec.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/shellexec/shellexec.go b/pkg/shellexec/shellexec.go index 2e1f118dab..31321f335e 100644 --- a/pkg/shellexec/shellexec.go +++ b/pkg/shellexec/shellexec.go @@ -580,6 +580,10 @@ func StartRemoteShellJob(ctx context.Context, logCtx context.Context, termSize w return jobId, nil } +// StartLocalShellProc starts a local shell process with the given command string, +// terminal size, command options, and connection name. It configures the shell +// environment (including correcting XDG directory variables when running as a Snap), +// sets up a PTY, and returns a ShellProc instance managing the running process. func StartLocalShellProc(logCtx context.Context, termSize waveobj.TermSize, cmdStr string, cmdOpts CommandOptsType, connName string) (*ShellProc, error) { if cmdOpts.SwapToken == nil { return nil, fmt.Errorf("SwapToken is required in CommandOptsType")