From 669762a9c9db954af4316d96b7fb8a121d331074 Mon Sep 17 00:00:00 2001 From: Evan Simkowitz Date: Tue, 26 Mar 2024 12:44:29 -0700 Subject: [PATCH] save work --- wavesrv/pkg/history/history.go | 9 +- wavesrv/pkg/sstore/dbops.go | 96 +---------------- wavesrv/pkg/sstore/sstore.go | 114 -------------------- wavesrv/pkg/workspaces/line/dbops.go | 118 +++++++++++++++++++++ wavesrv/pkg/workspaces/line/line.go | 135 ++++++++++++++++++++++++ wavesrv/pkg/workspaces/screen/dbops.go | 73 +------------ wavesrv/pkg/workspaces/screen/screen.go | 12 --- wavesrv/pkg/workspaces/workspaces.go | 83 ++++++++++++--- 8 files changed, 329 insertions(+), 311 deletions(-) create mode 100644 wavesrv/pkg/workspaces/line/dbops.go create mode 100644 wavesrv/pkg/workspaces/line/line.go diff --git a/wavesrv/pkg/history/history.go b/wavesrv/pkg/history/history.go index b38994f23..f94dcafbc 100644 --- a/wavesrv/pkg/history/history.go +++ b/wavesrv/pkg/history/history.go @@ -11,6 +11,7 @@ import ( "github.com/google/uuid" "github.com/wavetermdev/waveterm/wavesrv/pkg/dbutil" "github.com/wavetermdev/waveterm/wavesrv/pkg/sstore" + "github.com/wavetermdev/waveterm/wavesrv/pkg/workspaces/line" ) type HistoryItemType struct { @@ -114,7 +115,7 @@ type HistoryViewData struct { RawOffset int `json:"rawoffset"` NextRawOffset int `json:"nextrawoffset"` HasMore bool `json:"hasmore"` - Lines []*sstore.LineType `json:"lines"` + Lines []*line.LineType `json:"lines"` Cmds []*sstore.CmdType `json:"cmds"` } @@ -303,14 +304,14 @@ func getLineIdsFromHistoryItems(historyItems []*HistoryItemType) []string { return rtn } -func GetLineCmdsFromHistoryItems(ctx context.Context, historyItems []*HistoryItemType) ([]*sstore.LineType, []*sstore.CmdType, error) { +func GetLineCmdsFromHistoryItems(ctx context.Context, historyItems []*HistoryItemType) ([]*line.LineType, []*sstore.CmdType, error) { if len(historyItems) == 0 { return nil, nil, nil } - return sstore.WithTxRtn3(ctx, func(tx *sstore.TxWrap) ([]*sstore.LineType, []*sstore.CmdType, error) { + return sstore.WithTxRtn3(ctx, func(tx *sstore.TxWrap) ([]*line.LineType, []*sstore.CmdType, error) { lineIdsJsonArr := dbutil.QuickJsonArr(getLineIdsFromHistoryItems(historyItems)) query := `SELECT * FROM line WHERE lineid IN (SELECT value FROM json_each(?))` - lineArr := dbutil.SelectMappable[*sstore.LineType](tx, query, lineIdsJsonArr) + lineArr := dbutil.SelectMappable[*line.LineType](tx, query, lineIdsJsonArr) query = `SELECT * FROM cmd WHERE lineid IN (SELECT value FROM json_each(?))` cmdArr := dbutil.SelectMapsGen[*sstore.CmdType](tx, query, lineIdsJsonArr) return lineArr, cmdArr, nil diff --git a/wavesrv/pkg/sstore/dbops.go b/wavesrv/pkg/sstore/dbops.go index 180add4b9..ba3b557a4 100644 --- a/wavesrv/pkg/sstore/dbops.go +++ b/wavesrv/pkg/sstore/dbops.go @@ -8,7 +8,6 @@ import ( "errors" "fmt" "log" - "strconv" "strings" "sync" "time" @@ -291,99 +290,6 @@ func FmtUniqueName(name string, defaultFmtStr string, startIdx int, strs []strin } } -// special "E" returns last unarchived line, "EA" returns last line (even if archived) -func FindLineIdByArg(ctx context.Context, screenId string, lineArg string) (string, error) { - return WithTxRtn(ctx, func(tx *TxWrap) (string, error) { - if lineArg == "E" { - query := `SELECT lineid FROM line WHERE screenid = ? AND NOT archived ORDER BY linenum DESC LIMIT 1` - lineId := tx.GetString(query, screenId) - return lineId, nil - } - if lineArg == "EA" { - query := `SELECT lineid FROM line WHERE screenid = ? ORDER BY linenum DESC LIMIT 1` - lineId := tx.GetString(query, screenId) - return lineId, nil - } - lineNum, err := strconv.Atoi(lineArg) - if err == nil { - // valid linenum - query := `SELECT lineid FROM line WHERE screenid = ? AND linenum = ?` - lineId := tx.GetString(query, screenId, lineNum) - return lineId, nil - } else if len(lineArg) == 8 { - // prefix id string match - query := `SELECT lineid FROM line WHERE screenid = ? AND substr(lineid, 1, 8) = ?` - lineId := tx.GetString(query, screenId, lineArg) - return lineId, nil - } else { - // id match - query := `SELECT lineid FROM line WHERE screenid = ? AND lineid = ?` - lineId := tx.GetString(query, screenId, lineArg) - return lineId, nil - } - }) -} - -func GetLineCmdByLineId(ctx context.Context, screenId string, lineId string) (*LineType, *CmdType, error) { - return WithTxRtn3(ctx, func(tx *TxWrap) (*LineType, *CmdType, error) { - query := `SELECT * FROM line WHERE screenid = ? AND lineid = ?` - lineVal := dbutil.GetMappable[*LineType](tx, query, screenId, lineId) - if lineVal == nil { - return nil, nil, nil - } - var cmdRtn *CmdType - query = `SELECT * FROM cmd WHERE screenid = ? AND lineid = ?` - cmdRtn = dbutil.GetMapGen[*CmdType](tx, query, screenId, lineId) - return lineVal, cmdRtn, nil - }) -} - -func InsertLine(ctx context.Context, line *LineType, cmd *CmdType) error { - if line == nil { - return fmt.Errorf("line cannot be nil") - } - if line.LineId == "" { - return fmt.Errorf("line must have lineid set") - } - if line.LineNum != 0 { - return fmt.Errorf("line should not hage linenum set") - } - if cmd != nil && cmd.ScreenId == "" { - return fmt.Errorf("cmd should have screenid set") - } - qjs := dbutil.QuickJson(line.LineState) - if len(qjs) > MaxLineStateSize { - return fmt.Errorf("linestate exceeds maxsize, size[%d] max[%d]", len(qjs), MaxLineStateSize) - } - return WithTx(ctx, func(tx *TxWrap) error { - query := `SELECT screenid FROM screen WHERE screenid = ?` - if !tx.Exists(query, line.ScreenId) { - return fmt.Errorf("screen not found, cannot insert line[%s]", line.ScreenId) - } - query = `SELECT nextlinenum FROM screen WHERE screenid = ?` - nextLineNum := tx.GetInt(query, line.ScreenId) - line.LineNum = int64(nextLineNum) - query = `INSERT INTO line ( screenid, userid, lineid, ts, linenum, linenumtemp, linelocal, linetype, linestate, text, renderer, ephemeral, contentheight, star, archived) - VALUES (:screenid,:userid,:lineid,:ts,:linenum,:linenumtemp,:linelocal,:linetype,:linestate,:text,:renderer,:ephemeral,:contentheight,:star,:archived)` - tx.NamedExec(query, dbutil.ToDBMap(line, false)) - query = `UPDATE screen SET nextlinenum = ? WHERE screenid = ?` - tx.Exec(query, nextLineNum+1, line.ScreenId) - if cmd != nil { - cmd.OrigTermOpts = cmd.TermOpts - cmdMap := cmd.ToMap() - query = ` -INSERT INTO cmd ( screenid, lineid, remoteownerid, remoteid, remotename, cmdstr, rawcmdstr, festate, statebasehash, statediffhasharr, termopts, origtermopts, status, cmdpid, remotepid, donets, restartts, exitcode, durationms, rtnstate, runout, rtnbasehash, rtndiffhasharr) - VALUES (:screenid,:lineid,:remoteownerid,:remoteid,:remotename,:cmdstr,:rawcmdstr,:festate,:statebasehash,:statediffhasharr,:termopts,:origtermopts,:status,:cmdpid,:remotepid,:donets,:restartts,:exitcode,:durationms,:rtnstate,:runout,:rtnbasehash,:rtndiffhasharr) -` - tx.NamedExec(query, cmdMap) - } - if IsWebShare(tx, line.ScreenId) { - InsertScreenLineUpdate(tx, line.ScreenId, line.LineId, UpdateType_LineNew) - } - return nil - }) -} - func GetCmdByScreenId(ctx context.Context, screenId string, lineId string) (*CmdType, error) { return WithTxRtn(ctx, func(tx *TxWrap) (*CmdType, error) { query := `SELECT * FROM cmd WHERE screenid = ? AND lineid = ?` @@ -574,7 +480,7 @@ func HangupCmd(ctx context.Context, ck base.CommandKey) (*ScreenType, error) { }) } -func getNextId(ids []string, delId string) string { +func GetNextId(ids []string, delId string) string { if len(ids) == 0 { return "" } diff --git a/wavesrv/pkg/sstore/sstore.go b/wavesrv/pkg/sstore/sstore.go index 9dd355937..504b83fe0 100644 --- a/wavesrv/pkg/sstore/sstore.go +++ b/wavesrv/pkg/sstore/sstore.go @@ -17,7 +17,6 @@ import ( "path" "strings" "sync" - "time" "github.com/google/uuid" "github.com/jmoiron/sqlx" @@ -35,14 +34,12 @@ import ( type RemotePtrType = scpacket.RemotePtrType -const LineNoHeight = -1 const DBFileName = "waveterm.db" const DBWALFileName = "waveterm.db-wal" const DBFileNameBackup = "backup.waveterm.db" const DBWALFileNameBackup = "backup.waveterm.db-wal" const MaxWebShareLineCount = 50 const MaxWebShareScreenCount = 3 -const MaxLineStateSize = 4 * 1024 // 4k for now, can raise if needed const DefaultSessionName = "default" const LocalRemoteAlias = "local" @@ -54,23 +51,6 @@ const APITokenSentinel = "--apitoken--" // be passed to waveshell (it should always get resolved prior to sending a run packet) const ShellTypePref_Detect = "detect" -const ( - LineTypeCmd = "cmd" - LineTypeText = "text" - LineTypeOpenAI = "openai" -) - -const ( - LineState_Source = "prompt:source" - LineState_File = "prompt:file" - LineState_FileUrl = "wave:fileurl" - LineState_Min = "wave:min" - LineState_Template = "template" - LineState_Mode = "mode" - LineState_Lang = "lang" - LineState_Minimap = "minimap" -) - const ( MainViewSession = "session" MainViewBookmarks = "bookmarks" @@ -452,27 +432,6 @@ func (ri *RemoteInstance) ToMap() map[string]interface{} { return rtn } -type LineType struct { - ScreenId string `json:"screenid"` - UserId string `json:"userid"` - LineId string `json:"lineid"` - Ts int64 `json:"ts"` - LineNum int64 `json:"linenum"` - LineNumTemp bool `json:"linenumtemp,omitempty"` - LineLocal bool `json:"linelocal"` - LineType string `json:"linetype"` - LineState map[string]any `json:"linestate"` - Renderer string `json:"renderer,omitempty"` - Text string `json:"text,omitempty"` - Ephemeral bool `json:"ephemeral,omitempty"` - ContentHeight int64 `json:"contentheight,omitempty"` - Star bool `json:"star,omitempty"` - Archived bool `json:"archived,omitempty"` - Remove bool `json:"remove,omitempty"` -} - -func (LineType) UseDBMap() {} - type OpenAIUsage struct { PromptTokens int `json:"prompt_tokens"` CompletionTokens int `json:"completion_tokens"` @@ -783,79 +742,6 @@ func (cmd *CmdType) IsRunning() bool { return cmd.Status == CmdStatusRunning || cmd.Status == CmdStatusDetached } -func makeNewLineCmd(screenId string, userId string, lineId string, renderer string, lineState map[string]any) *LineType { - rtn := &LineType{} - rtn.ScreenId = screenId - rtn.UserId = userId - rtn.LineId = lineId - rtn.Ts = time.Now().UnixMilli() - rtn.LineLocal = true - rtn.LineType = LineTypeCmd - rtn.LineId = lineId - rtn.ContentHeight = LineNoHeight - rtn.Renderer = renderer - if lineState == nil { - lineState = make(map[string]any) - } - rtn.LineState = lineState - return rtn -} - -func makeNewLineText(screenId string, userId string, text string) *LineType { - rtn := &LineType{} - rtn.ScreenId = screenId - rtn.UserId = userId - rtn.LineId = scbase.GenWaveUUID() - rtn.Ts = time.Now().UnixMilli() - rtn.LineLocal = true - rtn.LineType = LineTypeText - rtn.Text = text - rtn.ContentHeight = LineNoHeight - rtn.LineState = make(map[string]any) - return rtn -} - -func makeNewLineOpenAI(screenId string, userId string, lineId string) *LineType { - rtn := &LineType{} - rtn.ScreenId = screenId - rtn.UserId = userId - rtn.LineId = lineId - rtn.Ts = time.Now().UnixMilli() - rtn.LineLocal = true - rtn.LineType = LineTypeOpenAI - rtn.ContentHeight = LineNoHeight - rtn.Renderer = CmdRendererOpenAI - rtn.LineState = make(map[string]any) - return rtn -} - -func AddCommentLine(ctx context.Context, screenId string, userId string, commentText string) (*LineType, error) { - rtnLine := makeNewLineText(screenId, userId, commentText) - err := InsertLine(ctx, rtnLine, nil) - if err != nil { - return nil, err - } - return rtnLine, nil -} - -func AddOpenAILine(ctx context.Context, screenId string, userId string, cmd *CmdType) (*LineType, error) { - rtnLine := makeNewLineOpenAI(screenId, userId, cmd.LineId) - err := InsertLine(ctx, rtnLine, cmd) - if err != nil { - return nil, err - } - return rtnLine, nil -} - -func AddCmdLine(ctx context.Context, screenId string, userId string, cmd *CmdType, renderer string, lineState map[string]any) (*LineType, error) { - rtnLine := makeNewLineCmd(screenId, userId, cmd.LineId, renderer, lineState) - err := InsertLine(ctx, rtnLine, cmd) - if err != nil { - return nil, err - } - return rtnLine, nil -} - func EnsureLocalRemote(ctx context.Context) error { remote, err := GetLocalRemote(ctx) if err != nil { diff --git a/wavesrv/pkg/workspaces/line/dbops.go b/wavesrv/pkg/workspaces/line/dbops.go new file mode 100644 index 000000000..f2ca91af1 --- /dev/null +++ b/wavesrv/pkg/workspaces/line/dbops.go @@ -0,0 +1,118 @@ +package line + +import ( + "context" + "fmt" + "strconv" + + "github.com/wavetermdev/waveterm/wavesrv/pkg/dbutil" + "github.com/wavetermdev/waveterm/wavesrv/pkg/sstore" +) + +// special "E" returns last unarchived line, "EA" returns last line (even if archived) +func FindLineIdByArg(ctx context.Context, screenId string, lineArg string) (string, error) { + return sstore.WithTxRtn(ctx, func(tx *sstore.TxWrap) (string, error) { + if lineArg == "E" { + query := `SELECT lineid FROM line WHERE screenid = ? AND NOT archived ORDER BY linenum DESC LIMIT 1` + lineId := tx.GetString(query, screenId) + return lineId, nil + } + if lineArg == "EA" { + query := `SELECT lineid FROM line WHERE screenid = ? ORDER BY linenum DESC LIMIT 1` + lineId := tx.GetString(query, screenId) + return lineId, nil + } + lineNum, err := strconv.Atoi(lineArg) + if err == nil { + // valid linenum + query := `SELECT lineid FROM line WHERE screenid = ? AND linenum = ?` + lineId := tx.GetString(query, screenId, lineNum) + return lineId, nil + } else if len(lineArg) == 8 { + // prefix id string match + query := `SELECT lineid FROM line WHERE screenid = ? AND substr(lineid, 1, 8) = ?` + lineId := tx.GetString(query, screenId, lineArg) + return lineId, nil + } else { + // id match + query := `SELECT lineid FROM line WHERE screenid = ? AND lineid = ?` + lineId := tx.GetString(query, screenId, lineArg) + return lineId, nil + } + }) +} + +func GetLineCmdByLineId(ctx context.Context, screenId string, lineId string) (*LineType, *sstore.CmdType, error) { + return sstore.WithTxRtn3(ctx, func(tx *sstore.TxWrap) (*LineType, *sstore.CmdType, error) { + query := `SELECT * FROM line WHERE screenid = ? AND lineid = ?` + lineVal := dbutil.GetMappable[*LineType](tx, query, screenId, lineId) + if lineVal == nil { + return nil, nil, nil + } + var cmdRtn *sstore.CmdType + query = `SELECT * FROM cmd WHERE screenid = ? AND lineid = ?` + cmdRtn = dbutil.GetMapGen[*sstore.CmdType](tx, query, screenId, lineId) + return lineVal, cmdRtn, nil + }) +} + +func InsertLine(ctx context.Context, line *LineType, cmd *sstore.CmdType) error { + if line == nil { + return fmt.Errorf("line cannot be nil") + } + if line.LineId == "" { + return fmt.Errorf("line must have lineid set") + } + if line.LineNum != 0 { + return fmt.Errorf("line should not hage linenum set") + } + if cmd != nil && cmd.ScreenId == "" { + return fmt.Errorf("cmd should have screenid set") + } + qjs := dbutil.QuickJson(line.LineState) + if len(qjs) > MaxLineStateSize { + return fmt.Errorf("linestate exceeds maxsize, size[%d] max[%d]", len(qjs), MaxLineStateSize) + } + return sstore.WithTx(ctx, func(tx *sstore.TxWrap) error { + query := `SELECT screenid FROM screen WHERE screenid = ?` + if !tx.Exists(query, line.ScreenId) { + return fmt.Errorf("screen not found, cannot insert line[%s]", line.ScreenId) + } + query = `SELECT nextlinenum FROM screen WHERE screenid = ?` + nextLineNum := tx.GetInt(query, line.ScreenId) + line.LineNum = int64(nextLineNum) + query = `INSERT INTO line ( screenid, userid, lineid, ts, linenum, linenumtemp, linelocal, linetype, linestate, text, renderer, ephemeral, contentheight, star, archived) + VALUES (:screenid,:userid,:lineid,:ts,:linenum,:linenumtemp,:linelocal,:linetype,:linestate,:text,:renderer,:ephemeral,:contentheight,:star,:archived)` + tx.NamedExec(query, dbutil.ToDBMap(line, false)) + query = `UPDATE screen SET nextlinenum = ? WHERE screenid = ?` + tx.Exec(query, nextLineNum+1, line.ScreenId) + if cmd != nil { + cmd.OrigTermOpts = cmd.TermOpts + cmdMap := cmd.ToMap() + query = ` +INSERT INTO cmd ( screenid, lineid, remoteownerid, remoteid, remotename, cmdstr, rawcmdstr, festate, statebasehash, statediffhasharr, termopts, origtermopts, status, cmdpid, remotepid, donets, restartts, exitcode, durationms, rtnstate, runout, rtnbasehash, rtndiffhasharr) + VALUES (:screenid,:lineid,:remoteownerid,:remoteid,:remotename,:cmdstr,:rawcmdstr,:festate,:statebasehash,:statediffhasharr,:termopts,:origtermopts,:status,:cmdpid,:remotepid,:donets,:restartts,:exitcode,:durationms,:rtnstate,:runout,:rtnbasehash,:rtndiffhasharr) +` + tx.NamedExec(query, cmdMap) + } + if sstore.IsWebShare(tx, line.ScreenId) { + sstore.InsertScreenLineUpdate(tx, line.ScreenId, line.LineId, sstore.UpdateType_LineNew) + } + return nil + }) +} + +func GetScreenLinesById(ctx context.Context, screenId string) (*ScreenLinesType, error) { + return sstore.WithTxRtn(ctx, func(tx *sstore.TxWrap) (*ScreenLinesType, error) { + query := `SELECT screenid FROM screen WHERE screenid = ?` + screen := dbutil.GetMappable[*ScreenLinesType](tx, query, screenId) + if screen == nil { + return nil, nil + } + query = `SELECT * FROM line WHERE screenid = ? ORDER BY linenum` + screen.Lines = dbutil.SelectMappable[*LineType](tx, query, screen.ScreenId) + query = `SELECT * FROM cmd WHERE screenid = ?` + screen.Cmds = dbutil.SelectMapsGen[*sstore.CmdType](tx, query, screen.ScreenId) + return screen, nil + }) +} diff --git a/wavesrv/pkg/workspaces/line/line.go b/wavesrv/pkg/workspaces/line/line.go new file mode 100644 index 000000000..8e373fc34 --- /dev/null +++ b/wavesrv/pkg/workspaces/line/line.go @@ -0,0 +1,135 @@ +package line + +import ( + "context" + "time" + + "github.com/wavetermdev/waveterm/wavesrv/pkg/scbase" + "github.com/wavetermdev/waveterm/wavesrv/pkg/sstore" +) + +const MaxLineStateSize = 4 * 1024 // 4k for now, can raise if needed +const LineNoHeight = -1 + +const ( + LineTypeCmd = "cmd" + LineTypeText = "text" + LineTypeOpenAI = "openai" +) + +const ( + LineState_Source = "prompt:source" + LineState_File = "prompt:file" + LineState_FileUrl = "wave:fileurl" + LineState_Min = "wave:min" + LineState_Template = "template" + LineState_Mode = "mode" + LineState_Lang = "lang" + LineState_Minimap = "minimap" +) + +type LineType struct { + ScreenId string `json:"screenid"` + UserId string `json:"userid"` + LineId string `json:"lineid"` + Ts int64 `json:"ts"` + LineNum int64 `json:"linenum"` + LineNumTemp bool `json:"linenumtemp,omitempty"` + LineLocal bool `json:"linelocal"` + LineType string `json:"linetype"` + LineState map[string]any `json:"linestate"` + Renderer string `json:"renderer,omitempty"` + Text string `json:"text,omitempty"` + Ephemeral bool `json:"ephemeral,omitempty"` + ContentHeight int64 `json:"contentheight,omitempty"` + Star bool `json:"star,omitempty"` + Archived bool `json:"archived,omitempty"` + Remove bool `json:"remove,omitempty"` +} + +func (LineType) UseDBMap() {} + +type ScreenLinesType struct { + ScreenId string `json:"screenid"` + Lines []*LineType `json:"lines" dbmap:"-"` + Cmds []*sstore.CmdType `json:"cmds" dbmap:"-"` +} + +func (ScreenLinesType) UseDBMap() {} + +func (ScreenLinesType) GetType() string { + return "screenlines" +} + +func makeNewLineCmd(screenId string, userId string, lineId string, renderer string, lineState map[string]any) *LineType { + rtn := &LineType{} + rtn.ScreenId = screenId + rtn.UserId = userId + rtn.LineId = lineId + rtn.Ts = time.Now().UnixMilli() + rtn.LineLocal = true + rtn.LineType = LineTypeCmd + rtn.LineId = lineId + rtn.ContentHeight = LineNoHeight + rtn.Renderer = renderer + if lineState == nil { + lineState = make(map[string]any) + } + rtn.LineState = lineState + return rtn +} + +func makeNewLineText(screenId string, userId string, text string) *LineType { + rtn := &LineType{} + rtn.ScreenId = screenId + rtn.UserId = userId + rtn.LineId = scbase.GenWaveUUID() + rtn.Ts = time.Now().UnixMilli() + rtn.LineLocal = true + rtn.LineType = LineTypeText + rtn.Text = text + rtn.ContentHeight = LineNoHeight + rtn.LineState = make(map[string]any) + return rtn +} + +func makeNewLineOpenAI(screenId string, userId string, lineId string) *LineType { + rtn := &LineType{} + rtn.ScreenId = screenId + rtn.UserId = userId + rtn.LineId = lineId + rtn.Ts = time.Now().UnixMilli() + rtn.LineLocal = true + rtn.LineType = LineTypeOpenAI + rtn.ContentHeight = LineNoHeight + rtn.Renderer = sstore.CmdRendererOpenAI + rtn.LineState = make(map[string]any) + return rtn +} + +func AddCommentLine(ctx context.Context, screenId string, userId string, commentText string) (*LineType, error) { + rtnLine := makeNewLineText(screenId, userId, commentText) + err := InsertLine(ctx, rtnLine, nil) + if err != nil { + return nil, err + } + return rtnLine, nil +} + +func AddOpenAILine(ctx context.Context, screenId string, userId string, cmd *sstore.CmdType) (*LineType, error) { + rtnLine := makeNewLineOpenAI(screenId, userId, cmd.LineId) + err := InsertLine(ctx, rtnLine, cmd) + if err != nil { + return nil, err + } + return rtnLine, nil +} + +func AddCmdLine(ctx context.Context, screenId string, userId string, cmd *sstore.CmdType, renderer string, lineState map[string]any) (*LineType, error) { + rtnLine := makeNewLineCmd(screenId, userId, cmd.LineId, renderer, lineState) + err := InsertLine(ctx, rtnLine, cmd) + if err != nil { + return nil, err + } + return rtnLine, nil +} diff --git a/wavesrv/pkg/workspaces/screen/dbops.go b/wavesrv/pkg/workspaces/screen/dbops.go index e85e8e429..bb6ace391 100644 --- a/wavesrv/pkg/workspaces/screen/dbops.go +++ b/wavesrv/pkg/workspaces/screen/dbops.go @@ -7,7 +7,6 @@ import ( "time" "github.com/wavetermdev/waveterm/wavesrv/pkg/dbutil" - "github.com/wavetermdev/waveterm/wavesrv/pkg/scbus" "github.com/wavetermdev/waveterm/wavesrv/pkg/sstore" ) @@ -19,21 +18,6 @@ func GetScreenById(ctx context.Context, screenId string) (*ScreenType, error) { }) } -func GetScreenLinesById(ctx context.Context, screenId string) (*ScreenLinesType, error) { - return sstore.WithTxRtn(ctx, func(tx *sstore.TxWrap) (*ScreenLinesType, error) { - query := `SELECT screenid FROM screen WHERE screenid = ?` - screen := dbutil.GetMappable[*ScreenLinesType](tx, query, screenId) - if screen == nil { - return nil, nil - } - query = `SELECT * FROM line WHERE screenid = ? ORDER BY linenum` - screen.Lines = dbutil.SelectMappable[*sstore.LineType](tx, query, screen.ScreenId) - query = `SELECT * FROM cmd WHERE screenid = ?` - screen.Cmds = dbutil.SelectMapsGen[*sstore.CmdType](tx, query, screen.ScreenId) - return screen, nil - }) -} - // includes archived screens func GetSessionScreens(ctx context.Context, sessionId string) ([]*ScreenType, error) { return sstore.WithTxRtn(ctx, func(tx *sstore.TxWrap) ([]*ScreenType, error) { @@ -46,7 +30,7 @@ func GetSessionScreens(ctx context.Context, sessionId string) ([]*ScreenType, er // screen may not exist at this point (so don't query screen table) func cleanScreenCmds(ctx context.Context, screenId string) error { var removedCmds []string - txErr := WithTx(ctx, func(tx *sstore.TxWrap) error { + txErr := sstore.WithTx(ctx, func(tx *sstore.TxWrap) error { query := `SELECT lineid FROM cmd WHERE screenid = ? AND lineid NOT IN (SELECT lineid FROM line WHERE screenid = ?)` removedCmds = tx.SelectStrings(query, screenId, screenId) query = `DELETE FROM cmd WHERE screenid = ? AND lineid NOT IN (SELECT lineid FROM line WHERE screenid = ?)` @@ -57,62 +41,13 @@ func cleanScreenCmds(ctx context.Context, screenId string) error { return txErr } for _, lineId := range removedCmds { - DeletePtyOutFile(ctx, screenId, lineId) + sstore.DeletePtyOutFile(ctx, screenId, lineId) } return nil } -func ArchiveScreen(ctx context.Context, sessionId string, screenId string) (scbus.UpdatePacket, error) { - var isActive bool - txErr := WithTx(ctx, func(tx *sstore.TxWrap) error { - query := `SELECT screenid FROM screen WHERE sessionid = ? AND screenid = ?` - if !tx.Exists(query, sessionId, screenId) { - return fmt.Errorf("cannot close screen (not found)") - } - if isWebShare(tx, screenId) { - return fmt.Errorf("cannot archive screen while web-sharing. stop web-sharing before trying to archive.") - } - query = `SELECT archived FROM screen WHERE sessionid = ? AND screenid = ?` - closeVal := tx.GetBool(query, sessionId, screenId) - if closeVal { - return nil - } - query = `SELECT count(*) FROM screen WHERE sessionid = ? AND NOT archived` - numScreens := tx.GetInt(query, sessionId) - if numScreens <= 1 { - return fmt.Errorf("cannot archive the last screen in a session") - } - query = `UPDATE screen SET archived = 1, archivedts = ?, screenidx = 0 WHERE sessionid = ? AND screenid = ?` - tx.Exec(query, time.Now().UnixMilli(), sessionId, screenId) - isActive = tx.Exists(`SELECT sessionid FROM session WHERE sessionid = ? AND activescreenid = ?`, sessionId, screenId) - if isActive { - screenIds := tx.SelectStrings(`SELECT screenid FROM screen WHERE sessionid = ? AND NOT archived ORDER BY screenidx`, sessionId) - nextId := getNextId(screenIds, screenId) - tx.Exec(`UPDATE session SET activescreenid = ? WHERE sessionid = ?`, nextId, sessionId) - } - return nil - }) - if txErr != nil { - return nil, txErr - } - newScreen, err := GetScreenById(ctx, screenId) - if err != nil { - return nil, fmt.Errorf("cannot retrive archived screen: %w", err) - } - update := scbus.MakeUpdatePacket() - update.AddUpdate(*newScreen) - if isActive { - bareSession, err := GetBareSessionById(ctx, sessionId) - if err != nil { - return nil, err - } - update.AddUpdate(*bareSession) - } - return update, nil -} - func UnArchiveScreen(ctx context.Context, sessionId string, screenId string) error { - txErr := WithTx(ctx, func(tx *sstore.TxWrap) error { + txErr := sstore.WithTx(ctx, func(tx *sstore.TxWrap) error { query := `SELECT screenid FROM screen WHERE sessionid = ? AND screenid = ? AND archived` if !tx.Exists(query, sessionId, screenId) { return fmt.Errorf("cannot re-open screen (not found or not archived)") @@ -155,7 +90,7 @@ func handleScreenDelUpdate(tx *sstore.TxWrap, screenId string) { // don't insert UpdateType_ScreenDel (we already processed it in cmdrunner) } -func insertScreenDelUpdate(tx *sstore.TxWrap, screenId string) { +func InsertScreenDelUpdate(tx *sstore.TxWrap, screenId string) { handleScreenDelUpdate(tx, screenId) insertScreenUpdate(tx, screenId, sstore.UpdateType_ScreenDel) // don't insert UpdateType_ScreenDel (we already processed it in cmdrunner) diff --git a/wavesrv/pkg/workspaces/screen/screen.go b/wavesrv/pkg/workspaces/screen/screen.go index adfa9dbdc..13c44aaf5 100644 --- a/wavesrv/pkg/workspaces/screen/screen.go +++ b/wavesrv/pkg/workspaces/screen/screen.go @@ -17,18 +17,6 @@ type ScreenOptsType struct { PTerm string `json:"pterm,omitempty"` } -type ScreenLinesType struct { - ScreenId string `json:"screenid"` - Lines []*sstore.LineType `json:"lines" dbmap:"-"` - Cmds []*sstore.CmdType `json:"cmds" dbmap:"-"` -} - -func (ScreenLinesType) UseDBMap() {} - -func (ScreenLinesType) GetType() string { - return "screenlines" -} - type ScreenWebShareOpts struct { ShareName string `json:"sharename"` ViewKey string `json:"viewkey"` diff --git a/wavesrv/pkg/workspaces/workspaces.go b/wavesrv/pkg/workspaces/workspaces.go index b1df178f5..1ac64b336 100644 --- a/wavesrv/pkg/workspaces/workspaces.go +++ b/wavesrv/pkg/workspaces/workspaces.go @@ -142,11 +142,11 @@ func DeleteScreen(ctx context.Context, screenId string, sessionDel bool, update var isActive bool var screenTombstone *screen.ScreenTombstoneType txErr := sstore.WithTx(ctx, func(tx *sstore.TxWrap) error { - screen, err := screen.GetScreenById(tx.Context(), screenId) + scr, err := screen.GetScreenById(tx.Context(), screenId) if err != nil { return fmt.Errorf("cannot get screen to delete: %w", err) } - if screen == nil { + if scr == nil { return fmt.Errorf("cannot delete screen (not found)") } webSharing := sstore.IsWebShare(tx, screenId) @@ -164,16 +164,16 @@ func DeleteScreen(ctx context.Context, screenId string, sessionDel bool, update isActive = tx.Exists(`SELECT sessionid FROM session WHERE sessionid = ? AND activescreenid = ?`, sessionId, screenId) if isActive { screenIds := tx.SelectStrings(`SELECT screenid FROM screen WHERE sessionid = ? AND NOT archived ORDER BY screenidx`, sessionId) - nextId := getNextId(screenIds, screenId) + nextId := sstore.GetNextId(screenIds, screenId) tx.Exec(`UPDATE session SET activescreenid = ? WHERE sessionid = ?`, nextId, sessionId) } } - screenTombstone = &ScreenTombstoneType{ - ScreenId: screen.ScreenId, - SessionId: screen.SessionId, - Name: screen.Name, + screenTombstone = &screen.ScreenTombstoneType{ + ScreenId: scr.ScreenId, + SessionId: scr.SessionId, + Name: scr.Name, DeletedTs: time.Now().UnixMilli(), - ScreenOpts: screen.ScreenOpts, + ScreenOpts: scr.ScreenOpts, } query := `INSERT INTO screen_tombstone ( screenid, sessionid, name, deletedts, screenopts) VALUES (:screenid,:sessionid,:name,:deletedts,:screenopts)` @@ -187,7 +187,7 @@ func DeleteScreen(ctx context.Context, screenId string, sessionDel bool, update query = `UPDATE history SET lineid = '', linenum = 0 WHERE screenid = ?` tx.Exec(query, screenId) if webSharing { - insertScreenDelUpdate(tx, screenId) + screen.InsertScreenDelUpdate(tx, screenId) } return nil }) @@ -195,15 +195,64 @@ func DeleteScreen(ctx context.Context, screenId string, sessionDel bool, update return nil, txErr } if !sessionDel { - GoDeleteScreenDirs(screenId) + sstore.GoDeleteScreenDirs(screenId) } if update == nil { update = scbus.MakeUpdatePacket() } update.AddUpdate(*screenTombstone) - update.AddUpdate(ScreenType{SessionId: sessionId, ScreenId: screenId, Remove: true}) + update.AddUpdate(screen.ScreenType{SessionId: sessionId, ScreenId: screenId, Remove: true}) if isActive { - bareSession, err := GetBareSessionById(ctx, sessionId) + bareSession, err := session.GetBareSessionById(ctx, sessionId) + if err != nil { + return nil, err + } + update.AddUpdate(*bareSession) + } + return update, nil +} + +func ArchiveScreen(ctx context.Context, sessionId string, screenId string) (scbus.UpdatePacket, error) { + var isActive bool + txErr := sstore.WithTx(ctx, func(tx *sstore.TxWrap) error { + query := `SELECT screenid FROM screen WHERE sessionid = ? AND screenid = ?` + if !tx.Exists(query, sessionId, screenId) { + return fmt.Errorf("cannot close screen (not found)") + } + if sstore.IsWebShare(tx, screenId) { + return fmt.Errorf("cannot archive screen while web-sharing. stop web-sharing before trying to archive.") + } + query = `SELECT archived FROM screen WHERE sessionid = ? AND screenid = ?` + closeVal := tx.GetBool(query, sessionId, screenId) + if closeVal { + return nil + } + query = `SELECT count(*) FROM screen WHERE sessionid = ? AND NOT archived` + numScreens := tx.GetInt(query, sessionId) + if numScreens <= 1 { + return fmt.Errorf("cannot archive the last screen in a session") + } + query = `UPDATE screen SET archived = 1, archivedts = ?, screenidx = 0 WHERE sessionid = ? AND screenid = ?` + tx.Exec(query, time.Now().UnixMilli(), sessionId, screenId) + isActive = tx.Exists(`SELECT sessionid FROM session WHERE sessionid = ? AND activescreenid = ?`, sessionId, screenId) + if isActive { + screenIds := tx.SelectStrings(`SELECT screenid FROM screen WHERE sessionid = ? AND NOT archived ORDER BY screenidx`, sessionId) + nextId := sstore.GetNextId(screenIds, screenId) + tx.Exec(`UPDATE session SET activescreenid = ? WHERE sessionid = ?`, nextId, sessionId) + } + return nil + }) + if txErr != nil { + return nil, txErr + } + newScreen, err := screen.GetScreenById(ctx, screenId) + if err != nil { + return nil, fmt.Errorf("cannot retrive archived screen: %w", err) + } + update := scbus.MakeUpdatePacket() + update.AddUpdate(*newScreen) + if isActive { + bareSession, err := session.GetBareSessionById(ctx, sessionId) if err != nil { return nil, err } @@ -274,15 +323,15 @@ func SwitchScreenById(ctx context.Context, sessionId string, screenId string) (* return nil, err } update := scbus.MakeUpdatePacket() - update.AddUpdate(ActiveSessionIdUpdate(sessionId)) + update.AddUpdate(session.ActiveSessionIdUpdate(sessionId)) update.AddUpdate(*bareSession) - memState := GetScreenMemState(screenId) + memState := sstore.GetScreenMemState(screenId) if memState != nil { - update.AddUpdate(CmdLineUpdate(memState.CmdInputText)) - UpdateWithCurrentOpenAICmdInfoChat(screenId, update) + update.AddUpdate(sstore.CmdLineUpdate(memState.CmdInputText)) + sstore.UpdateWithCurrentOpenAICmdInfoChat(screenId, update) // Clear any previous status indicator for this screen - err := ResetStatusIndicator_Update(update, screenId) + err := sstore.ResetStatusIndicator_Update(update, screenId) if err != nil { // This is not a fatal error, so just log it log.Printf("error resetting status indicator when switching screens: %v\n", err)