2025-01-05 04:56:57 +00:00
|
|
|
// Copyright 2025, Command Line Inc.
|
2024-06-12 00:42:10 +00:00
|
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
|
|
|
|
|
package main
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
|
|
|
|
"fmt"
|
|
|
|
|
"log"
|
|
|
|
|
"os"
|
2024-06-15 21:59:14 +00:00
|
|
|
|
2024-06-12 00:42:10 +00:00
|
|
|
"runtime"
|
2024-06-13 01:17:56 +00:00
|
|
|
"sync"
|
2024-06-12 00:42:10 +00:00
|
|
|
"time"
|
|
|
|
|
|
New AIPanel (#2370)
Massive PR, over 13k LOC updated, 128 commits to implement the first pass at the new Wave AI panel. Two backend adapters (OpenAI and Anthropic), layout changes to support the panel, keyboard shortcuts, and a huge focus/layout change to integrate the panel seamlessly into the UI.
Also fixes some small issues found during the Wave AI journey (zoom fixes, documentation, more scss removal, circular dependency issues, settings, etc)
2025-10-07 20:32:10 +00:00
|
|
|
"github.com/joho/godotenv"
|
2024-09-05 21:25:45 +00:00
|
|
|
"github.com/wavetermdev/waveterm/pkg/authkey"
|
|
|
|
|
"github.com/wavetermdev/waveterm/pkg/blockcontroller"
|
2025-01-10 22:09:32 +00:00
|
|
|
"github.com/wavetermdev/waveterm/pkg/blocklogger"
|
2024-09-05 21:25:45 +00:00
|
|
|
"github.com/wavetermdev/waveterm/pkg/filestore"
|
2024-11-21 02:05:13 +00:00
|
|
|
"github.com/wavetermdev/waveterm/pkg/panichandler"
|
2024-11-16 00:09:26 +00:00
|
|
|
"github.com/wavetermdev/waveterm/pkg/remote/conncontroller"
|
2025-01-22 22:50:09 +00:00
|
|
|
"github.com/wavetermdev/waveterm/pkg/remote/fileshare/wshfs"
|
2024-09-05 21:25:45 +00:00
|
|
|
"github.com/wavetermdev/waveterm/pkg/service"
|
|
|
|
|
"github.com/wavetermdev/waveterm/pkg/telemetry"
|
2025-02-03 23:32:44 +00:00
|
|
|
"github.com/wavetermdev/waveterm/pkg/telemetry/telemetrydata"
|
2024-09-05 21:25:45 +00:00
|
|
|
"github.com/wavetermdev/waveterm/pkg/util/shellutil"
|
2025-01-29 20:42:55 +00:00
|
|
|
"github.com/wavetermdev/waveterm/pkg/util/sigutil"
|
2025-02-03 23:32:44 +00:00
|
|
|
"github.com/wavetermdev/waveterm/pkg/util/utilfn"
|
2024-09-05 21:25:45 +00:00
|
|
|
"github.com/wavetermdev/waveterm/pkg/wavebase"
|
|
|
|
|
"github.com/wavetermdev/waveterm/pkg/waveobj"
|
|
|
|
|
"github.com/wavetermdev/waveterm/pkg/wcloud"
|
|
|
|
|
"github.com/wavetermdev/waveterm/pkg/wconfig"
|
|
|
|
|
"github.com/wavetermdev/waveterm/pkg/wcore"
|
|
|
|
|
"github.com/wavetermdev/waveterm/pkg/web"
|
|
|
|
|
"github.com/wavetermdev/waveterm/pkg/wps"
|
|
|
|
|
"github.com/wavetermdev/waveterm/pkg/wshrpc"
|
|
|
|
|
"github.com/wavetermdev/waveterm/pkg/wshrpc/wshremote"
|
|
|
|
|
"github.com/wavetermdev/waveterm/pkg/wshrpc/wshserver"
|
|
|
|
|
"github.com/wavetermdev/waveterm/pkg/wshutil"
|
2025-01-16 23:54:58 +00:00
|
|
|
"github.com/wavetermdev/waveterm/pkg/wslconn"
|
2024-09-05 21:25:45 +00:00
|
|
|
"github.com/wavetermdev/waveterm/pkg/wstore"
|
2024-06-12 00:42:10 +00:00
|
|
|
)
|
|
|
|
|
|
2024-08-01 06:47:33 +00:00
|
|
|
// these are set at build time
|
|
|
|
|
var WaveVersion = "0.0.0"
|
|
|
|
|
var BuildTime = "0"
|
|
|
|
|
|
2024-09-23 20:33:39 +00:00
|
|
|
const InitialTelemetryWait = 10 * time.Second
|
|
|
|
|
const TelemetryTick = 2 * time.Minute
|
2024-08-09 01:24:54 +00:00
|
|
|
const TelemetryInterval = 4 * time.Hour
|
2025-02-03 23:32:44 +00:00
|
|
|
const TelemetryInitialCountsWait = 5 * time.Second
|
|
|
|
|
const TelemetryCountsInterval = 1 * time.Hour
|
2024-08-09 01:24:54 +00:00
|
|
|
|
2024-06-13 01:17:56 +00:00
|
|
|
var shutdownOnce sync.Once
|
|
|
|
|
|
New AIPanel (#2370)
Massive PR, over 13k LOC updated, 128 commits to implement the first pass at the new Wave AI panel. Two backend adapters (OpenAI and Anthropic), layout changes to support the panel, keyboard shortcuts, and a huge focus/layout change to integrate the panel seamlessly into the UI.
Also fixes some small issues found during the Wave AI journey (zoom fixes, documentation, more scss removal, circular dependency issues, settings, etc)
2025-10-07 20:32:10 +00:00
|
|
|
func init() {
|
|
|
|
|
envFilePath := os.Getenv("WAVETERM_ENVFILE")
|
|
|
|
|
if envFilePath != "" {
|
|
|
|
|
log.Printf("applying env file: %s\n", envFilePath)
|
|
|
|
|
_ = godotenv.Load(envFilePath)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-12 00:42:10 +00:00
|
|
|
func doShutdown(reason string) {
|
2024-06-13 01:17:56 +00:00
|
|
|
shutdownOnce.Do(func() {
|
|
|
|
|
log.Printf("shutting down: %s\n", reason)
|
|
|
|
|
ctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Second)
|
|
|
|
|
defer cancelFn()
|
2024-09-05 07:21:08 +00:00
|
|
|
go blockcontroller.StopAllBlockControllers()
|
2024-08-09 01:24:54 +00:00
|
|
|
shutdownActivityUpdate()
|
|
|
|
|
sendTelemetryWrapper()
|
2024-06-13 01:17:56 +00:00
|
|
|
// TODO deal with flush in progress
|
2024-11-21 02:48:46 +00:00
|
|
|
clearTempFiles()
|
2024-06-13 01:17:56 +00:00
|
|
|
filestore.WFS.FlushCache(ctx)
|
2024-06-20 06:59:41 +00:00
|
|
|
watcher := wconfig.GetWatcher()
|
|
|
|
|
if watcher != nil {
|
|
|
|
|
watcher.Close()
|
|
|
|
|
}
|
2024-09-05 07:21:08 +00:00
|
|
|
time.Sleep(500 * time.Millisecond)
|
2024-10-06 20:40:02 +00:00
|
|
|
log.Printf("shutdown complete\n")
|
2024-06-13 01:17:56 +00:00
|
|
|
os.Exit(0)
|
|
|
|
|
})
|
2024-06-12 00:42:10 +00:00
|
|
|
}
|
|
|
|
|
|
2024-06-13 01:17:56 +00:00
|
|
|
// watch stdin, kill server if stdin is closed
|
|
|
|
|
func stdinReadWatch() {
|
|
|
|
|
buf := make([]byte, 1024)
|
|
|
|
|
for {
|
|
|
|
|
_, err := os.Stdin.Read(buf)
|
|
|
|
|
if err != nil {
|
|
|
|
|
doShutdown(fmt.Sprintf("stdin closed/error (%v)", err))
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-03 23:32:44 +00:00
|
|
|
func startConfigWatcher() {
|
2024-06-20 06:59:41 +00:00
|
|
|
watcher := wconfig.GetWatcher()
|
|
|
|
|
if watcher != nil {
|
|
|
|
|
watcher.Start()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-09 01:24:54 +00:00
|
|
|
func telemetryLoop() {
|
|
|
|
|
var nextSend int64
|
|
|
|
|
time.Sleep(InitialTelemetryWait)
|
|
|
|
|
for {
|
|
|
|
|
if time.Now().Unix() > nextSend {
|
|
|
|
|
nextSend = time.Now().Add(TelemetryInterval).Unix()
|
|
|
|
|
sendTelemetryWrapper()
|
|
|
|
|
}
|
|
|
|
|
time.Sleep(TelemetryTick)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-03 23:32:44 +00:00
|
|
|
func panicTelemetryHandler(panicName string) {
|
2024-11-28 00:52:00 +00:00
|
|
|
activity := wshrpc.ActivityUpdate{NumPanics: 1}
|
2024-11-21 02:05:13 +00:00
|
|
|
err := telemetry.UpdateActivity(context.Background(), activity)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Printf("error updating activity (panicTelemetryHandler): %v\n", err)
|
|
|
|
|
}
|
2025-02-03 23:32:44 +00:00
|
|
|
telemetry.RecordTEvent(context.Background(), telemetrydata.MakeTEvent("debug:panic", telemetrydata.TEventProps{
|
|
|
|
|
PanicType: panicName,
|
|
|
|
|
}))
|
2024-11-21 02:05:13 +00:00
|
|
|
}
|
|
|
|
|
|
2024-08-09 01:24:54 +00:00
|
|
|
func sendTelemetryWrapper() {
|
2024-12-31 17:31:55 +00:00
|
|
|
defer func() {
|
|
|
|
|
panichandler.PanicHandler("sendTelemetryWrapper", recover())
|
|
|
|
|
}()
|
2025-10-16 18:17:24 +00:00
|
|
|
ctx, cancelFn := context.WithTimeout(context.Background(), 15*time.Second)
|
2024-08-09 01:24:54 +00:00
|
|
|
defer cancelFn()
|
2024-11-16 00:09:26 +00:00
|
|
|
beforeSendActivityUpdate(ctx)
|
2024-08-20 21:56:48 +00:00
|
|
|
client, err := wstore.DBGetSingleton[*waveobj.Client](ctx)
|
2024-08-09 01:24:54 +00:00
|
|
|
if err != nil {
|
|
|
|
|
log.Printf("[error] getting client data for telemetry: %v\n", err)
|
|
|
|
|
return
|
|
|
|
|
}
|
2025-10-16 18:17:24 +00:00
|
|
|
err = wcloud.SendAllTelemetry(client.OID)
|
2024-08-09 01:24:54 +00:00
|
|
|
if err != nil {
|
|
|
|
|
log.Printf("[error] sending telemetry: %v\n", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-03 23:32:44 +00:00
|
|
|
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.CountViews, _ = wstore.DBGetBlockViewCounts(ctx)
|
2025-08-26 23:08:39 +00:00
|
|
|
|
|
|
|
|
fullConfig := wconfig.GetWatcher().GetFullConfig()
|
|
|
|
|
customWidgets := fullConfig.CountCustomWidgets()
|
|
|
|
|
customAIPresets := fullConfig.CountCustomAIPresets()
|
|
|
|
|
customSettings := wconfig.CountCustomSettings()
|
|
|
|
|
|
|
|
|
|
props.UserSet = &telemetrydata.TEventUserProps{
|
|
|
|
|
SettingsCustomWidgets: customWidgets,
|
|
|
|
|
SettingsCustomAIPresets: customAIPresets,
|
|
|
|
|
SettingsCustomSettings: customSettings,
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-03 23:32:44 +00:00
|
|
|
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)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-16 00:09:26 +00:00
|
|
|
func beforeSendActivityUpdate(ctx context.Context) {
|
2024-11-28 00:52:00 +00:00
|
|
|
activity := wshrpc.ActivityUpdate{}
|
2024-11-16 00:09:26 +00:00
|
|
|
activity.NumTabs, _ = wstore.DBGetCount[*waveobj.Tab](ctx)
|
|
|
|
|
activity.NumBlocks, _ = wstore.DBGetCount[*waveobj.Block](ctx)
|
2024-11-26 02:07:29 +00:00
|
|
|
activity.Blocks, _ = wstore.DBGetBlockViewCounts(ctx)
|
2024-11-16 00:09:26 +00:00
|
|
|
activity.NumWindows, _ = wstore.DBGetCount[*waveobj.Window](ctx)
|
|
|
|
|
activity.NumSSHConn = conncontroller.GetNumSSHHasConnected()
|
2025-01-16 23:54:58 +00:00
|
|
|
activity.NumWSLConn = wslconn.GetNumWSLHasConnected()
|
2024-12-05 18:35:54 +00:00
|
|
|
activity.NumWSNamed, activity.NumWS, _ = wstore.DBGetWSCounts(ctx)
|
2024-11-16 00:09:26 +00:00
|
|
|
err := telemetry.UpdateActivity(ctx, activity)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Printf("error updating before activity: %v\n", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-28 21:15:31 +00:00
|
|
|
func startupActivityUpdate(firstLaunch bool) {
|
2024-08-09 01:24:54 +00:00
|
|
|
ctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Second)
|
|
|
|
|
defer cancelFn()
|
2024-11-28 00:52:00 +00:00
|
|
|
activity := wshrpc.ActivityUpdate{Startup: 1}
|
2024-08-09 01:24:54 +00:00
|
|
|
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)
|
|
|
|
|
}
|
2025-02-03 23:32:44 +00:00
|
|
|
autoUpdateChannel := telemetry.AutoUpdateChannel()
|
|
|
|
|
autoUpdateEnabled := telemetry.IsAutoUpdateEnabled()
|
2025-10-17 19:19:40 +00:00
|
|
|
shellType, shellVersion, shellErr := shellutil.DetectShellTypeAndVersion()
|
|
|
|
|
if shellErr != nil {
|
|
|
|
|
shellType = "error"
|
|
|
|
|
shellVersion = ""
|
|
|
|
|
}
|
2025-08-28 21:15:31 +00:00
|
|
|
props := telemetrydata.TEventProps{
|
2025-02-03 23:32:44 +00:00
|
|
|
UserSet: &telemetrydata.TEventUserProps{
|
|
|
|
|
ClientVersion: "v" + WaveVersion,
|
|
|
|
|
ClientBuildTime: BuildTime,
|
|
|
|
|
ClientArch: wavebase.ClientArch(),
|
|
|
|
|
ClientOSRelease: wavebase.UnameKernelRelease(),
|
|
|
|
|
ClientIsDev: wavebase.IsDevMode(),
|
|
|
|
|
AutoUpdateChannel: autoUpdateChannel,
|
|
|
|
|
AutoUpdateEnabled: autoUpdateEnabled,
|
2025-10-17 19:19:40 +00:00
|
|
|
LocalShellType: shellType,
|
|
|
|
|
LocalShellVersion: shellVersion,
|
2025-02-03 23:32:44 +00:00
|
|
|
},
|
|
|
|
|
UserSetOnce: &telemetrydata.TEventUserProps{
|
|
|
|
|
ClientInitialVersion: "v" + WaveVersion,
|
|
|
|
|
},
|
2025-08-28 21:15:31 +00:00
|
|
|
}
|
|
|
|
|
if firstLaunch {
|
|
|
|
|
props.AppFirstLaunch = true
|
|
|
|
|
}
|
|
|
|
|
tevent := telemetrydata.MakeTEvent("app:startup", props)
|
2025-02-03 23:32:44 +00:00
|
|
|
err = telemetry.RecordTEvent(ctx, tevent)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Printf("error recording startup event: %v\n", err)
|
|
|
|
|
}
|
2024-08-09 01:24:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func shutdownActivityUpdate() {
|
|
|
|
|
ctx, cancelFn := context.WithTimeout(context.Background(), 1*time.Second)
|
|
|
|
|
defer cancelFn()
|
2024-11-28 00:52:00 +00:00
|
|
|
activity := wshrpc.ActivityUpdate{Shutdown: 1}
|
2024-08-09 01:24:54 +00:00
|
|
|
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)
|
|
|
|
|
}
|
2025-02-03 23:32:44 +00:00
|
|
|
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)
|
|
|
|
|
}
|
2024-08-09 01:24:54 +00:00
|
|
|
}
|
|
|
|
|
|
2024-08-13 23:52:35 +00:00
|
|
|
func createMainWshClient() {
|
|
|
|
|
rpc := wshserver.GetMainRpcClient()
|
2025-01-22 22:50:09 +00:00
|
|
|
wshfs.RpcClient = rpc
|
2024-10-24 05:43:17 +00:00
|
|
|
wshutil.DefaultRouter.RegisterRoute(wshutil.DefaultRoute, rpc, true)
|
2024-08-14 01:19:29 +00:00
|
|
|
wps.Broker.SetClient(wshutil.DefaultRouter)
|
2025-01-28 06:38:19 +00:00
|
|
|
localConnWsh := wshutil.MakeWshRpc(nil, nil, wshrpc.RpcContext{Conn: wshrpc.LocalConnName}, &wshremote.ServerImpl{}, "conn:local")
|
2024-08-30 18:33:04 +00:00
|
|
|
go wshremote.RunSysInfoLoop(localConnWsh, wshrpc.LocalConnName)
|
2024-10-24 05:43:17 +00:00
|
|
|
wshutil.DefaultRouter.RegisterRoute(wshutil.MakeConnectionRouteId(wshrpc.LocalConnName), localConnWsh, true)
|
2024-08-13 23:52:35 +00:00
|
|
|
}
|
|
|
|
|
|
2024-10-27 20:12:41 +00:00
|
|
|
func grabAndRemoveEnvVars() error {
|
|
|
|
|
err := authkey.SetAuthKeyFromEnv()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("setting auth key: %v", err)
|
|
|
|
|
}
|
|
|
|
|
err = wavebase.CacheAndRemoveEnvVars()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
err = wcloud.CacheAndRemoveEnvVars()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2025-08-26 23:23:48 +00:00
|
|
|
|
|
|
|
|
// Remove WAVETERM env vars that leak from prod => dev
|
|
|
|
|
os.Unsetenv("WAVETERM_CLIENTID")
|
|
|
|
|
os.Unsetenv("WAVETERM_WORKSPACEID")
|
|
|
|
|
os.Unsetenv("WAVETERM_TABID")
|
|
|
|
|
os.Unsetenv("WAVETERM_BLOCKID")
|
|
|
|
|
os.Unsetenv("WAVETERM_CONN")
|
|
|
|
|
os.Unsetenv("WAVETERM_JWT")
|
|
|
|
|
os.Unsetenv("WAVETERM_VERSION")
|
|
|
|
|
|
2024-10-27 20:12:41 +00:00
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-21 02:48:46 +00:00
|
|
|
func clearTempFiles() error {
|
|
|
|
|
ctx, cancelFn := context.WithTimeout(context.Background(), 2*time.Second)
|
|
|
|
|
defer cancelFn()
|
|
|
|
|
client, err := wstore.DBGetSingleton[*waveobj.Client](ctx)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("error getting client: %v", err)
|
|
|
|
|
}
|
|
|
|
|
filestore.WFS.DeleteZone(ctx, client.TempOID)
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-12 00:42:10 +00:00
|
|
|
func main() {
|
2024-06-13 02:33:44 +00:00
|
|
|
log.SetFlags(log.LstdFlags | log.Lmicroseconds)
|
|
|
|
|
log.SetPrefix("[wavesrv] ")
|
2024-08-01 06:47:33 +00:00
|
|
|
wavebase.WaveVersion = WaveVersion
|
2024-08-09 01:24:54 +00:00
|
|
|
wavebase.BuildTime = BuildTime
|
2024-06-13 02:33:44 +00:00
|
|
|
|
2024-10-27 20:12:41 +00:00
|
|
|
err := grabAndRemoveEnvVars()
|
2024-08-21 22:04:39 +00:00
|
|
|
if err != nil {
|
2024-10-27 20:12:41 +00:00
|
|
|
log.Printf("[error] %v\n", err)
|
2024-08-21 22:04:39 +00:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
err = service.ValidateServiceMap()
|
2024-06-12 00:42:10 +00:00
|
|
|
if err != nil {
|
|
|
|
|
log.Printf("error validating service map: %v\n", err)
|
|
|
|
|
return
|
|
|
|
|
}
|
Update data and config paths to match platform defaults (#1047)
Going forward for new installations, config and data files will be
stored at the platform default paths, as defined by
[env-paths](https://www.npmjs.com/package/env-paths).
For backwards compatibility, if the `~/.waveterm` or `WAVETERM_HOME`
directory exists and contains valid data, it will be used. If this check
fails, then `WAVETERM_DATA_HOME` and `WAVETERM_CONFIG_HOME` will be
used. If these are not defined, then `XDG_DATA_HOME` and
`XDG_CONFIG_HOME` will be used. Finally, if none of these are defined,
the [env-paths](https://www.npmjs.com/package/env-paths) defaults will
be used.
As with the existing app, dev instances will write to `waveterm-dev`
directories, while all others will write to `waveterm`.
2024-10-22 16:26:58 +00:00
|
|
|
err = wavebase.EnsureWaveDataDir()
|
2024-06-12 00:42:10 +00:00
|
|
|
if err != nil {
|
|
|
|
|
log.Printf("error ensuring wave home dir: %v\n", err)
|
|
|
|
|
return
|
|
|
|
|
}
|
2024-09-05 21:05:42 +00:00
|
|
|
err = wavebase.EnsureWaveDBDir()
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Printf("error ensuring wave db dir: %v\n", err)
|
|
|
|
|
return
|
|
|
|
|
}
|
Update data and config paths to match platform defaults (#1047)
Going forward for new installations, config and data files will be
stored at the platform default paths, as defined by
[env-paths](https://www.npmjs.com/package/env-paths).
For backwards compatibility, if the `~/.waveterm` or `WAVETERM_HOME`
directory exists and contains valid data, it will be used. If this check
fails, then `WAVETERM_DATA_HOME` and `WAVETERM_CONFIG_HOME` will be
used. If these are not defined, then `XDG_DATA_HOME` and
`XDG_CONFIG_HOME` will be used. Finally, if none of these are defined,
the [env-paths](https://www.npmjs.com/package/env-paths) defaults will
be used.
As with the existing app, dev instances will write to `waveterm-dev`
directories, while all others will write to `waveterm`.
2024-10-22 16:26:58 +00:00
|
|
|
err = wavebase.EnsureWaveConfigDir()
|
2024-09-11 16:26:43 +00:00
|
|
|
if err != nil {
|
|
|
|
|
log.Printf("error ensuring wave config dir: %v\n", err)
|
|
|
|
|
return
|
|
|
|
|
}
|
2024-10-21 23:51:18 +00:00
|
|
|
|
|
|
|
|
// TODO: rather than ensure this dir exists, we should let the editor recursively create parent dirs on save
|
Update data and config paths to match platform defaults (#1047)
Going forward for new installations, config and data files will be
stored at the platform default paths, as defined by
[env-paths](https://www.npmjs.com/package/env-paths).
For backwards compatibility, if the `~/.waveterm` or `WAVETERM_HOME`
directory exists and contains valid data, it will be used. If this check
fails, then `WAVETERM_DATA_HOME` and `WAVETERM_CONFIG_HOME` will be
used. If these are not defined, then `XDG_DATA_HOME` and
`XDG_CONFIG_HOME` will be used. Finally, if none of these are defined,
the [env-paths](https://www.npmjs.com/package/env-paths) defaults will
be used.
As with the existing app, dev instances will write to `waveterm-dev`
directories, while all others will write to `waveterm`.
2024-10-22 16:26:58 +00:00
|
|
|
err = wavebase.EnsureWavePresetsDir()
|
2024-10-21 23:51:18 +00:00
|
|
|
if err != nil {
|
|
|
|
|
log.Printf("error ensuring wave presets dir: %v\n", err)
|
|
|
|
|
return
|
|
|
|
|
}
|
2024-06-12 00:42:10 +00:00
|
|
|
waveLock, err := wavebase.AcquireWaveLock()
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Printf("error acquiring wave lock (another instance of Wave is likely running): %v\n", err)
|
|
|
|
|
return
|
|
|
|
|
}
|
2024-06-15 21:59:14 +00:00
|
|
|
defer func() {
|
2024-10-04 19:20:52 +00:00
|
|
|
err = waveLock.Close()
|
2024-06-15 21:59:14 +00:00
|
|
|
if err != nil {
|
|
|
|
|
log.Printf("error releasing wave lock: %v\n", err)
|
|
|
|
|
}
|
|
|
|
|
}()
|
2024-08-01 06:47:33 +00:00
|
|
|
log.Printf("wave version: %s (%s)\n", WaveVersion, BuildTime)
|
Update data and config paths to match platform defaults (#1047)
Going forward for new installations, config and data files will be
stored at the platform default paths, as defined by
[env-paths](https://www.npmjs.com/package/env-paths).
For backwards compatibility, if the `~/.waveterm` or `WAVETERM_HOME`
directory exists and contains valid data, it will be used. If this check
fails, then `WAVETERM_DATA_HOME` and `WAVETERM_CONFIG_HOME` will be
used. If these are not defined, then `XDG_DATA_HOME` and
`XDG_CONFIG_HOME` will be used. Finally, if none of these are defined,
the [env-paths](https://www.npmjs.com/package/env-paths) defaults will
be used.
As with the existing app, dev instances will write to `waveterm-dev`
directories, while all others will write to `waveterm`.
2024-10-22 16:26:58 +00:00
|
|
|
log.Printf("wave data dir: %s\n", wavebase.GetWaveDataDir())
|
|
|
|
|
log.Printf("wave config dir: %s\n", wavebase.GetWaveConfigDir())
|
2024-06-12 00:42:10 +00:00
|
|
|
err = filestore.InitFilestore()
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Printf("error initializing filestore: %v\n", err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
err = wstore.InitWStore()
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Printf("error initializing wstore: %v\n", err)
|
|
|
|
|
return
|
|
|
|
|
}
|
2024-11-21 02:05:13 +00:00
|
|
|
panichandler.PanicTelemetryHandler = panicTelemetryHandler
|
2024-08-01 06:47:33 +00:00
|
|
|
go func() {
|
2024-12-31 17:31:55 +00:00
|
|
|
defer func() {
|
|
|
|
|
panichandler.PanicHandler("InitCustomShellStartupFiles", recover())
|
|
|
|
|
}()
|
2024-08-01 06:47:33 +00:00
|
|
|
err := shellutil.InitCustomShellStartupFiles()
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Printf("error initializing wsh and shell-integration files: %v\n", err)
|
|
|
|
|
}
|
|
|
|
|
}()
|
2025-08-28 21:15:31 +00:00
|
|
|
firstLaunch, err := wcore.EnsureInitialData()
|
2024-06-12 00:42:10 +00:00
|
|
|
if err != nil {
|
|
|
|
|
log.Printf("error ensuring initial data: %v\n", err)
|
|
|
|
|
return
|
|
|
|
|
}
|
2025-08-28 21:15:31 +00:00
|
|
|
if firstLaunch {
|
|
|
|
|
log.Printf("first launch detected")
|
|
|
|
|
}
|
2024-11-21 02:48:46 +00:00
|
|
|
err = clearTempFiles()
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Printf("error clearing temp files: %v\n", err)
|
|
|
|
|
return
|
|
|
|
|
}
|
2024-12-02 19:44:08 +00:00
|
|
|
|
2024-08-13 23:52:35 +00:00
|
|
|
createMainWshClient()
|
2025-01-29 20:42:55 +00:00
|
|
|
sigutil.InstallShutdownSignalHandlers(doShutdown)
|
|
|
|
|
sigutil.InstallSIGUSR1Handler()
|
2025-02-03 23:32:44 +00:00
|
|
|
startConfigWatcher()
|
2024-06-13 01:17:56 +00:00
|
|
|
go stdinReadWatch()
|
2024-08-09 01:24:54 +00:00
|
|
|
go telemetryLoop()
|
2025-02-03 23:32:44 +00:00
|
|
|
go updateTelemetryCountsLoop()
|
2025-10-17 19:19:40 +00:00
|
|
|
go startupActivityUpdate(firstLaunch) // must be after startConfigWatcher()
|
2025-01-10 22:09:32 +00:00
|
|
|
blocklogger.InitBlockLogger()
|
2025-10-17 19:19:40 +00:00
|
|
|
go wavebase.GetSystemSummary() // get this cached (used in AI)
|
2025-02-03 23:32:44 +00:00
|
|
|
|
2024-07-18 01:42:49 +00:00
|
|
|
webListener, err := web.MakeTCPListener("web")
|
2024-07-09 20:47:39 +00:00
|
|
|
if err != nil {
|
|
|
|
|
log.Printf("error creating web listener: %v\n", err)
|
2024-07-09 21:31:16 +00:00
|
|
|
return
|
|
|
|
|
}
|
2024-07-18 01:42:49 +00:00
|
|
|
wsListener, err := web.MakeTCPListener("websocket")
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Printf("error creating websocket listener: %v\n", err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
go web.RunWebSocketServer(wsListener)
|
2024-07-17 22:24:43 +00:00
|
|
|
unixListener, err := web.MakeUnixListener()
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Printf("error creating unix listener: %v\n", err)
|
|
|
|
|
return
|
2024-07-09 20:47:39 +00:00
|
|
|
}
|
2024-06-12 00:42:10 +00:00
|
|
|
go func() {
|
2024-10-27 20:12:41 +00:00
|
|
|
if BuildTime == "" {
|
|
|
|
|
BuildTime = "0"
|
2024-06-12 00:42:10 +00:00
|
|
|
}
|
2024-10-27 20:12:41 +00:00
|
|
|
// use fmt instead of log here to make sure it goes directly to stderr
|
|
|
|
|
fmt.Fprintf(os.Stderr, "WAVESRV-ESTART ws:%s web:%s version:%s buildtime:%s\n", wsListener.Addr(), webListener.Addr(), WaveVersion, BuildTime)
|
2024-06-12 00:42:10 +00:00
|
|
|
}()
|
2024-08-17 18:21:25 +00:00
|
|
|
go wshutil.RunWshRpcOverListener(unixListener)
|
2024-07-09 20:47:39 +00:00
|
|
|
web.RunWebServer(webListener) // blocking
|
2024-06-12 00:42:10 +00:00
|
|
|
runtime.KeepAlive(waveLock)
|
|
|
|
|
}
|