Skip to content
Open
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 README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ Wave also supports durable SSH sessions that survive network interruptions and r

![WaveTerm Screenshot](./assets/wave-screenshot.webp)

## Fork additions

This fork removes telemetry and adds native ChatGPT / Codex subscription authorization, a BYOK setup form, and FreeRDP/VNC viewer controls in Settings. See [build and usage instructions](docs/CHATGPT-AND-REMOTE-DESKTOPS.md) for Fedora KDE, supported features, and validation limits.

## Key Features

- Wave AI - Context-aware terminal assistant that reads your terminal output, analyzes widgets, and performs file operations
Expand Down
268 changes: 0 additions & 268 deletions cmd/server/main-server.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,11 @@ import (
"github.com/wavetermdev/waveterm/pkg/filestore"
"github.com/wavetermdev/waveterm/pkg/jobcontroller"
"github.com/wavetermdev/waveterm/pkg/panichandler"
"github.com/wavetermdev/waveterm/pkg/remote/conncontroller"
"github.com/wavetermdev/waveterm/pkg/remote/fileshare/wshfs"
"github.com/wavetermdev/waveterm/pkg/secretstore"
"github.com/wavetermdev/waveterm/pkg/service"
"github.com/wavetermdev/waveterm/pkg/telemetry"
"github.com/wavetermdev/waveterm/pkg/telemetry/telemetrydata"
"github.com/wavetermdev/waveterm/pkg/util/envutil"
"github.com/wavetermdev/waveterm/pkg/util/shellutil"
"github.com/wavetermdev/waveterm/pkg/util/sigutil"
"github.com/wavetermdev/waveterm/pkg/util/utilfn"
"github.com/wavetermdev/waveterm/pkg/wavebase"
"github.com/wavetermdev/waveterm/pkg/waveobj"
"github.com/wavetermdev/waveterm/pkg/wcloud"
Expand All @@ -44,7 +39,6 @@ import (
"github.com/wavetermdev/waveterm/pkg/wshrpc/wshremote"
"github.com/wavetermdev/waveterm/pkg/wshrpc/wshserver"
"github.com/wavetermdev/waveterm/pkg/wshutil"
"github.com/wavetermdev/waveterm/pkg/wslconn"
"github.com/wavetermdev/waveterm/pkg/wstore"

"net/http"
Expand All @@ -55,15 +49,8 @@ import (
var WaveVersion = "0.0.0"
var BuildTime = "0"

const InitialTelemetryWait = 10 * time.Second
const TelemetryTick = 2 * time.Minute
const TelemetryInterval = 4 * time.Hour
const TelemetryInitialCountsWait = 5 * time.Second
const TelemetryCountsInterval = 1 * time.Hour
const BackupCleanupTick = 2 * time.Minute
const BackupCleanupInterval = 4 * time.Hour
const InitialDiagnosticWait = 5 * time.Minute
const DiagnosticTick = 10 * time.Minute

var shutdownOnce sync.Once

Expand All @@ -81,8 +68,6 @@ func doShutdown(reason string) {
ctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Second)
defer cancelFn()
go blockcontroller.StopAllBlockControllersForShutdown()
shutdownActivityUpdate()
sendTelemetryWrapper()
// TODO deal with flush in progress
clearTempFiles()
filestore.WFS.FlushCache(ctx)
Expand Down Expand Up @@ -118,74 +103,6 @@ func startConfigWatcher() {
}
}

func telemetryLoop() {
defer func() {
panichandler.PanicHandler("telemetryLoop", recover())
}()
var nextSend int64
time.Sleep(InitialTelemetryWait)
for {
if time.Now().Unix() > nextSend {
nextSend = time.Now().Add(TelemetryInterval).Unix()
sendTelemetryWrapper()
}
time.Sleep(TelemetryTick)
}
}

func diagnosticLoop() {
defer func() {
panichandler.PanicHandler("diagnosticLoop", recover())
}()
if os.Getenv("WAVETERM_NOPING") != "" {
log.Printf("WAVETERM_NOPING set, disabling diagnostic ping\n")
return
}
var lastSentDate string
time.Sleep(InitialDiagnosticWait)
for {
currentDate := time.Now().Format("2006-01-02")
if lastSentDate == "" || lastSentDate != currentDate {
if sendDiagnosticPing() {
lastSentDate = currentDate
}
}
time.Sleep(DiagnosticTick)
}
}

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

rpcClient := wshclient.GetBareRpcClient()
isOnline, err := wshclient.NetworkOnlineCommand(rpcClient, &wshrpc.RpcOpts{Route: "electron", Timeout: 2000})
if err != nil || !isOnline {
return false
}
clientId := wstore.GetClientId()
usageTelemetry := telemetry.IsTelemetryEnabled()
wcloud.SendDiagnosticPing(ctx, clientId, usageTelemetry)
return true
}

func setupTelemetryConfigHandler() {
watcher := wconfig.GetWatcher()
if watcher == nil {
return
}
currentConfig := watcher.GetFullConfig()
currentTelemetryEnabled := currentConfig.Settings.TelemetryEnabled

watcher.RegisterUpdateHandler(func(newConfig wconfig.FullConfigType) {
newTelemetryEnabled := newConfig.Settings.TelemetryEnabled
if newTelemetryEnabled != currentTelemetryEnabled {
currentTelemetryEnabled = newTelemetryEnabled
wcore.GoSendNoTelemetryUpdate(newTelemetryEnabled)
}
})
}

func backupCleanupLoop() {
defer func() {
panichandler.PanicHandler("backupCleanupLoop", recover())
Expand All @@ -203,185 +120,6 @@ func backupCleanupLoop() {
}
}

func panicTelemetryHandler(panicName string) {
activity := wshrpc.ActivityUpdate{NumPanics: 1}
err := telemetry.UpdateActivity(context.Background(), activity)
if err != nil {
log.Printf("error updating activity (panicTelemetryHandler): %v\n", err)
}
telemetry.RecordTEvent(context.Background(), telemetrydata.MakeTEvent("debug:panic", telemetrydata.TEventProps{
PanicType: panicName,
}))
}

func sendTelemetryWrapper() {
defer func() {
panichandler.PanicHandler("sendTelemetryWrapper", recover())
}()
ctx, cancelFn := context.WithTimeout(context.Background(), 15*time.Second)
defer cancelFn()
beforeSendActivityUpdate(ctx)
clientId := wstore.GetClientId()
err := wcloud.SendAllTelemetry(clientId)
if err != nil {
log.Printf("[error] sending telemetry: %v\n", err)
}
}

func updateTelemetryCounts(lastCounts telemetrydata.TEventProps) telemetrydata.TEventProps {
ctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Second)
defer cancelFn()
var props telemetrydata.TEventProps
props.CountBlocks, _ = wstore.DBGetCount[*waveobj.Block](ctx)
props.CountTabs, _ = wstore.DBGetCount[*waveobj.Tab](ctx)
props.CountWindows, _ = wstore.DBGetCount[*waveobj.Window](ctx)
props.CountWorkspaces, _, _ = wstore.DBGetWSCounts(ctx)
props.CountSSHConn = conncontroller.GetNumSSHHasConnected()
props.CountWSLConn = wslconn.GetNumWSLHasConnected()
props.CountJobs = jobcontroller.GetNumJobsRunning()
props.CountJobsConnected = jobcontroller.GetNumJobsConnected()
props.CountViews, _ = wstore.DBGetBlockViewCounts(ctx)

fullConfig := wconfig.GetWatcher().GetFullConfig()
customWidgets := fullConfig.CountCustomWidgets()
customAIPresets := fullConfig.CountCustomAIPresets()
customSettings := wconfig.CountCustomSettings()
customAIModes := fullConfig.CountCustomAIModes()

props.UserSet = &telemetrydata.TEventUserProps{
SettingsCustomWidgets: customWidgets,
SettingsCustomAIPresets: customAIPresets,
SettingsCustomSettings: customSettings,
SettingsCustomAIModes: customAIModes,
}

secretsCount, err := secretstore.CountSecrets()
if err == nil {
props.UserSet.SettingsSecretsCount = secretsCount
}

if utilfn.CompareAsMarshaledJson(props, lastCounts) {
return lastCounts
}
tevent := telemetrydata.MakeTEvent("app:counts", props)
err = telemetry.RecordTEvent(ctx, tevent)
if err != nil {
log.Printf("error recording counts tevent: %v\n", err)
}
return props
}

func updateTelemetryCountsLoop() {
defer func() {
panichandler.PanicHandler("updateTelemetryCountsLoop", recover())
}()
var nextSend int64
var lastCounts telemetrydata.TEventProps
time.Sleep(TelemetryInitialCountsWait)
for {
if time.Now().Unix() > nextSend {
nextSend = time.Now().Add(TelemetryCountsInterval).Unix()
lastCounts = updateTelemetryCounts(lastCounts)
}
time.Sleep(TelemetryTick)
}
}

func beforeSendActivityUpdate(ctx context.Context) {
activity := wshrpc.ActivityUpdate{}
activity.NumTabs, _ = wstore.DBGetCount[*waveobj.Tab](ctx)
activity.NumBlocks, _ = wstore.DBGetCount[*waveobj.Block](ctx)
activity.Blocks, _ = wstore.DBGetBlockViewCounts(ctx)
activity.NumWindows, _ = wstore.DBGetCount[*waveobj.Window](ctx)
activity.NumSSHConn = conncontroller.GetNumSSHHasConnected()
activity.NumWSLConn = wslconn.GetNumWSLHasConnected()
activity.NumWSNamed, activity.NumWS, _ = wstore.DBGetWSCounts(ctx)
err := telemetry.UpdateActivity(ctx, activity)
if err != nil {
log.Printf("error updating before activity: %v\n", err)
}
}

func startupActivityUpdate(firstLaunch bool) {
defer func() {
panichandler.PanicHandler("startupActivityUpdate", recover())
}()
ctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Second)
defer cancelFn()
activity := wshrpc.ActivityUpdate{Startup: 1}
err := telemetry.UpdateActivity(ctx, activity) // set at least one record into activity (don't use go routine wrap here)
if err != nil {
log.Printf("error updating startup activity: %v\n", err)
}
autoUpdateChannel := telemetry.AutoUpdateChannel()
autoUpdateEnabled := telemetry.IsAutoUpdateEnabled()
shellType, shellVersion, shellErr := shellutil.DetectShellTypeAndVersion()
if shellErr != nil {
shellType = "error"
shellVersion = ""
}
userSetOnce := &telemetrydata.TEventUserProps{
ClientInitialVersion: "v" + WaveVersion,
}
tosTs := telemetry.GetTosAgreedTs()
var cohortTime time.Time
if tosTs > 0 {
cohortTime = time.UnixMilli(tosTs)
} else {
cohortTime = time.Now()
}
cohortMonth := cohortTime.Format("2006-01")
year, week := cohortTime.ISOWeek()
cohortISOWeek := fmt.Sprintf("%04d-W%02d", year, week)
userSetOnce.CohortMonth = cohortMonth
userSetOnce.CohortISOWeek = cohortISOWeek
fullConfig := wconfig.GetWatcher().GetFullConfig()
props := telemetrydata.TEventProps{
UserSet: &telemetrydata.TEventUserProps{
ClientVersion: "v" + wavebase.WaveVersion,
ClientBuildTime: wavebase.BuildTime,
ClientArch: wavebase.ClientArch(),
ClientOSRelease: wavebase.UnameKernelRelease(),
ClientIsDev: wavebase.IsDevMode(),
ClientPackageType: wavebase.ClientPackageType(),
ClientMacOSVersion: wavebase.ClientMacOSVersion(),
AutoUpdateChannel: autoUpdateChannel,
AutoUpdateEnabled: autoUpdateEnabled,
LocalShellType: shellType,
LocalShellVersion: shellVersion,
SettingsTransparent: fullConfig.Settings.WindowTransparent,
},
UserSetOnce: userSetOnce,
}
if firstLaunch {
props.AppFirstLaunch = true
}
tevent := telemetrydata.MakeTEvent("app:startup", props)
err = telemetry.RecordTEvent(ctx, tevent)
if err != nil {
log.Printf("error recording startup event: %v\n", err)
}
}

func shutdownActivityUpdate() {
ctx, cancelFn := context.WithTimeout(context.Background(), 1*time.Second)
defer cancelFn()
activity := wshrpc.ActivityUpdate{Shutdown: 1}
err := telemetry.UpdateActivity(ctx, activity) // do NOT use the go routine wrap here (this needs to be synchronous)
if err != nil {
log.Printf("error updating shutdown activity: %v\n", err)
}
err = telemetry.TruncateActivityTEventForShutdown(ctx)
if err != nil {
log.Printf("error truncating activity t-event for shutdown: %v\n", err)
}
tevent := telemetrydata.MakeTEvent("app:shutdown", telemetrydata.TEventProps{})
err = telemetry.RecordTEvent(ctx, tevent)
if err != nil {
log.Printf("error recording shutdown event: %v\n", err)
}
}

func createMainWshClient() {
rpc := wshserver.GetMainRpcClient()
wshutil.DefaultRouter.RegisterTrustedLeaf(rpc, wshutil.DefaultRoute)
Expand Down Expand Up @@ -525,7 +263,6 @@ func main() {
log.Printf("error initializing wstore: %v\n", err)
return
}
panichandler.PanicTelemetryHandler = panicTelemetryHandler
go func() {
defer func() {
panichandler.PanicHandler("InitCustomShellStartupFiles", recover())
Expand Down Expand Up @@ -566,12 +303,7 @@ func main() {
aiusechat.InitAIModeConfigWatcher()
maybeStartPprofServer()
go stdinReadWatch()
go telemetryLoop()
go diagnosticLoop()
setupTelemetryConfigHandler()
go updateTelemetryCountsLoop()
go backupCleanupLoop()
go startupActivityUpdate(firstLaunch) // must be after startConfigWatcher()
blocklogger.InitBlockLogger()
jobcontroller.InitJobController()
blockcontroller.InitBlockController()
Expand Down
Loading
Loading