Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions cmd/server/main-server.go
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,10 @@ func main() {
return
}

err = shellutil.FixupWaveZshHistory()
if err != nil {
log.Printf("error fixing up wave zsh history: %v\n", err)
}
createMainWshClient()
sigutil.InstallShutdownSignalHandlers(doShutdown)
sigutil.InstallSIGUSR1Handler()
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ require (
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/outrigdev/goid v0.3.0 // indirect
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,8 @@ github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuE
github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/outrigdev/goid v0.3.0 h1:t/otQD3EXc45cLtQVPUnNgEyRaTQA4cPeu3qVcrsIws=
github.com/outrigdev/goid v0.3.0/go.mod h1:hEH7f27ypN/GHWt/7gvkRoFYR0LZizfUBIAbak4neVE=
github.com/photostorm/pty v1.1.19-0.20230903182454-31354506054b h1:cLGKfKb1uk0hxI0Q8L83UAJPpeJ+gSpn3cCU/tjd3eg=
github.com/photostorm/pty v1.1.19-0.20230903182454-31354506054b/go.mod h1:KO+FcPtyLAiRC0hJwreJVvfwc7vnNz77UxBTIGHdPVk=
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo=
Expand Down
7 changes: 5 additions & 2 deletions pkg/telemetry/telemetry.go
Original file line number Diff line number Diff line change
Expand Up @@ -254,10 +254,13 @@ func RecordTEvent(ctx context.Context, tevent *telemetrydata.TEvent) error {
}

func CleanOldTEvents(ctx context.Context) error {
daysToKeep := 7
if !IsTelemetryEnabled() {
daysToKeep = 1
}
olderThan := time.Now().AddDate(0, 0, -daysToKeep).UnixMilli()
return wstore.WithTx(ctx, func(tx *wstore.TxWrap) error {
// delete events older than 28 days
query := `DELETE FROM db_tevent WHERE ts < ?`
olderThan := time.Now().AddDate(0, 0, -28).UnixMilli()
tx.Exec(query, olderThan)
return nil
})
Expand Down
24 changes: 24 additions & 0 deletions pkg/util/envutil/envutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,27 @@ func RmEnv(envStr string, key string) string {
delete(envMap, key)
return MapToEnv(envMap)
}

func SliceToEnv(env []string) string {
var sb strings.Builder
for _, envVar := range env {
if len(envVar) == 0 {
continue
}
sb.WriteString(envVar)
sb.WriteByte('\x00')
}
return sb.String()
}

func EnvToSlice(envStr string) []string {
envLines := strings.Split(envStr, "\x00")
result := make([]string, 0, len(envLines))
for _, line := range envLines {
if len(line) == 0 {
continue
}
result = append(result, line)
}
return result
}
5 changes: 5 additions & 0 deletions pkg/util/shellutil/shellintegration/zsh_zshrc.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ if [[ -n ${_comps+x} ]]; then
source <(wsh completion zsh)
fi

# fix history (macos)
if [[ "$HISTFILE" == "$WAVETERM_ZDOTDIR/.zsh_history" ]]; then
HISTFILE="$HOME/.zsh_history"
fi

typeset -g _WAVETERM_SI_FIRSTPRECMD=1

# shell integration
Expand Down
118 changes: 118 additions & 0 deletions pkg/util/shellutil/shellutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"sync"
"time"

"github.com/wavetermdev/waveterm/pkg/util/envutil"
"github.com/wavetermdev/waveterm/pkg/util/utilfn"
"github.com/wavetermdev/waveterm/pkg/wavebase"
"github.com/wavetermdev/waveterm/pkg/waveobj"
Expand Down Expand Up @@ -47,6 +48,8 @@ var (

//go:embed shellintegration/pwsh_wavepwsh.sh
PwshStartup_wavepwsh string

ZshExtendedHistoryPattern = regexp.MustCompile(`^: [0-9]+:`)
)

const DefaultTermType = "xterm-256color"
Expand Down Expand Up @@ -74,6 +77,7 @@ const (
PwshIntegrationDir = "shell/pwsh"
FishIntegrationDir = "shell/fish"
WaveHomeBinDir = "bin"
ZshHistoryFileName = ".zsh_history"
)

func DetectLocalShellPath() string {
Expand Down Expand Up @@ -208,6 +212,46 @@ func GetLocalZshZDotDir() string {
return filepath.Join(wavebase.GetWaveDataDir(), ZshIntegrationDir)
}

func HasWaveZshHistory() (bool, int64) {
zshDir := GetLocalZshZDotDir()
historyFile := filepath.Join(zshDir, ZshHistoryFileName)
fileInfo, err := os.Stat(historyFile)
if err != nil {
return false, 0
}
return true, fileInfo.Size()
}

func IsExtendedZshHistoryFile(fileName string) (bool, error) {
file, err := os.Open(fileName)
if err != nil {
if os.IsNotExist(err) {
return false, nil
}
return false, err
}
defer file.Close()

buf := make([]byte, 1024)
n, err := file.Read(buf)
if err != nil {
return false, err
}
Comment on lines +235 to +239
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Handle io.EOF when reading small files.

file.Read() can return io.EOF along with n > 0 for files smaller than the buffer. This would cause the function to incorrectly return an error for small history files.

Apply this diff:

 	buf := make([]byte, 1024)
 	n, err := file.Read(buf)
-	if err != nil {
+	if err != nil && err != io.EOF {
 		return false, err
 	}
+	if n == 0 {
+		return false, nil
+	}

Also add "io" to the imports.

🤖 Prompt for AI Agents
In pkg/util/shellutil/shellutil.go around lines 234 to 238, the code reads into
a fixed buffer and currently returns any non-nil error directly; modify the
logic to import "io" and treat io.EOF as non-fatal when n>0 by only returning an
error if err != nil && err != io.EOF, and ensure subsequent processing uses only
the first n bytes of the buffer (e.g., buf[:n]) so small files that fill less
than the buffer are handled correctly.


content := string(buf[:n])
lines := strings.Split(content, "\n")

for _, line := range lines {
line = strings.TrimSpace(line)
if line == "" {
continue
}
return ZshExtendedHistoryPattern.MatchString(line), nil
}

return false, nil
}

func GetLocalWshBinaryPath(version string, goos string, goarch string) (string, error) {
ext := ""
if goarch == "amd64" {
Expand Down Expand Up @@ -422,6 +466,80 @@ func getShellVersion(shellPath string, shellType string) (string, error) {
return matches[1], nil
}

func FixupWaveZshHistory() error {
if runtime.GOOS != "darwin" {
return nil
}

hasHistory, size := HasWaveZshHistory()
if !hasHistory {
return nil
}

zshDir := GetLocalZshZDotDir()
waveHistFile := filepath.Join(zshDir, ZshHistoryFileName)

if size == 0 {
err := os.Remove(waveHistFile)
if err != nil {
log.Printf("error removing wave zsh history file %s: %v\n", waveHistFile, err)
}
return nil
}

log.Printf("merging wave zsh history %s into ~/.zsh_history\n", waveHistFile)

homeDir, err := os.UserHomeDir()
if err != nil {
return fmt.Errorf("error getting home directory: %w", err)
}
realHistFile := filepath.Join(homeDir, ".zsh_history")

isExtended, err := IsExtendedZshHistoryFile(realHistFile)
if err != nil {
return fmt.Errorf("error checking if history is extended: %w", err)
}

hasExtendedStr := "false"
if isExtended {
hasExtendedStr = "true"
}

quotedWaveHistFile := utilfn.ShellQuote(waveHistFile, true, -1)

script := fmt.Sprintf(`
HISTFILE=~/.zsh_history
HISTSIZE=999999
SAVEHIST=999999
has_extended_history=%s
[[ $has_extended_history == true ]] && setopt EXTENDED_HISTORY
fc -RI
fc -RI %s
fc -W
`, hasExtendedStr, quotedWaveHistFile)

ctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Second)
defer cancelFn()

cmd := exec.CommandContext(ctx, "zsh", "-f", "-i", "-c", script)
cmd.Stdin = nil
envStr := envutil.SliceToEnv(os.Environ())
envStr = envutil.RmEnv(envStr, "ZDOTDIR")
cmd.Env = envutil.EnvToSlice(envStr)
output, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("error executing zsh history fixup script: %w, output: %s", err, string(output))
}

err = os.Remove(waveHistFile)
if err != nil {
log.Printf("error removing wave zsh history file %s: %v\n", waveHistFile, err)
}
log.Printf("successfully merged wave zsh history %s into ~/.zsh_history\n", waveHistFile)

return nil
}

func FormatOSC(oscNum int, parts ...string) string {
if len(parts) == 0 {
return fmt.Sprintf("\x1b]%d\x07", oscNum)
Expand Down
10 changes: 5 additions & 5 deletions pkg/wcloud/wcloud.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,11 +214,11 @@ func sendTEvents(clientId string) (int, error) {
}

func SendAllTelemetry(clientId string) error {
defer func() {
ctx, cancelFn := context.WithTimeout(context.Background(), 2*time.Second)
defer cancelFn()
telemetry.CleanOldTEvents(ctx)
}()
ctx, cancelFn := context.WithTimeout(context.Background(), 2*time.Second)
defer cancelFn()
if err := telemetry.CleanOldTEvents(ctx); err != nil {
log.Printf("error cleaning old telemetry events: %v\n", err)
}
if !telemetry.IsTelemetryEnabled() {
log.Printf("telemetry disabled, not sending\n")
return nil
Expand Down
Loading