From 0060c8ffc298a0efa3ea567bd92eadc954f968b4 Mon Sep 17 00:00:00 2001 From: sawka Date: Thu, 27 Oct 2022 00:33:50 -0700 Subject: [PATCH] big update to handle cmd returnstate (still need to process new state with donepacket) --- cmd/main-server.go | 31 ++++++ db/migrations/000001_init.up.sql | 3 +- pkg/cmdrunner/cmdrunner.go | 166 ++++++------------------------- pkg/cmdrunner/shparse.go | 66 +++++++++--- pkg/remote/remote.go | 1 + pkg/sstore/dbops.go | 8 +- pkg/sstore/sstore.go | 44 ++++---- 7 files changed, 148 insertions(+), 171 deletions(-) diff --git a/cmd/main-server.go b/cmd/main-server.go index 508fea72..c1ed442b 100644 --- a/cmd/main-server.go +++ b/cmd/main-server.go @@ -182,6 +182,36 @@ func HandleGetWindow(w http.ResponseWriter, r *http.Request) { return } +func HandleRtnState(w http.ResponseWriter, r *http.Request) { + qvals := r.URL.Query() + sessionId := qvals.Get("sessionid") + cmdId := qvals.Get("cmdid") + if sessionId == "" || cmdId == "" { + w.WriteHeader(500) + w.Write([]byte(fmt.Sprintf("must specify sessionid and cmdid"))) + return + } + if _, err := uuid.Parse(sessionId); err != nil { + w.WriteHeader(500) + w.Write([]byte(fmt.Sprintf("invalid sessionid: %v", err))) + return + } + if _, err := uuid.Parse(cmdId); err != nil { + w.WriteHeader(500) + w.Write([]byte(fmt.Sprintf("invalid cmdid: %v", err))) + return + } + data, err := cmdrunner.GetRtnStateDiff(r.Context(), sessionId, cmdId) + if err != nil { + w.WriteHeader(500) + w.Write([]byte(fmt.Sprintf("cannot get rtnstate diff: %v", err))) + return + } + w.WriteHeader(http.StatusOK) + w.Write(data) + return +} + func HandleRemotePty(w http.ResponseWriter, r *http.Request) { w.Header().Set("Access-Control-Allow-Origin", r.Header.Get("Origin")) w.Header().Set("Access-Control-Allow-Credentials", "true") @@ -393,6 +423,7 @@ func main() { gr := mux.NewRouter() gr.HandleFunc("/api/ptyout", HandleGetPtyOut) gr.HandleFunc("/api/remote-pty", HandleRemotePty) + gr.HandleFunc("/api/rtnstate", HandleRtnState) gr.HandleFunc("/api/get-window", HandleGetWindow) gr.HandleFunc("/api/run-command", HandleRunCommand).Methods("GET", "POST", "OPTIONS") gr.HandleFunc("/api/get-client-data", HandleGetClientData) diff --git a/db/migrations/000001_init.up.sql b/db/migrations/000001_init.up.sql index 55b4c25a..bc54d74d 100644 --- a/db/migrations/000001_init.up.sql +++ b/db/migrations/000001_init.up.sql @@ -79,6 +79,7 @@ CREATE TABLE line ( text text NOT NULL, cmdid varchar(36) NOT NULL, ephemeral boolean NOT NULL, + contentheight int NOT NULL, PRIMARY KEY (sessionid, windowid, lineid) ); @@ -116,7 +117,7 @@ CREATE TABLE cmd ( startpk json NOT NULL, donepk json NOT NULL, runout json NOT NULL, - usedrows int NOT NULL, + rtnstate bool NOT NULL, PRIMARY KEY (sessionid, cmdid) ); diff --git a/pkg/cmdrunner/cmdrunner.go b/pkg/cmdrunner/cmdrunner.go index c9fd3975..3bed888e 100644 --- a/pkg/cmdrunner/cmdrunner.go +++ b/pkg/cmdrunner/cmdrunner.go @@ -39,7 +39,7 @@ var ColorNames = []string{"black", "red", "green", "yellow", "blue", "magenta", var RemoteColorNames = []string{"red", "green", "yellow", "blue", "magenta", "cyan", "white", "orange"} var RemoteSetArgs = []string{"alias", "connectmode", "key", "password", "autoinstall", "color"} -var WindowCmds = []string{"run", "comment", "cd", "cr", "setenv", "unset", "clear", "sw", "alias", "unalias", "function", "source", "reset"} +var WindowCmds = []string{"run", "comment", "cd", "cr", "clear", "sw", "alias", "unalias", "function", "reset"} var NoHistCmds = []string{"compgen", "line", "history"} var GlobalCmds = []string{"session", "screen", "remote"} @@ -72,11 +72,9 @@ func init() { registerCmdFn("run", RunCommand) registerCmdFn("eval", EvalCommand) registerCmdFn("comment", CommentCommand) - registerCmdFn("cd", CdCommand) + // registerCmdFn("cd", CdCommand) registerCmdFn("cr", CrCommand) registerCmdFn("compgen", CompGenCommand) - registerCmdFn("setenv", SetEnvCommand) - registerCmdFn("unset", UnSetCommand) registerCmdFn("clear", ClearCommand) registerCmdFn("reset", ResetCommand) @@ -114,8 +112,6 @@ func init() { registerCmdFn("line:show", LineShowCommand) registerCmdFn("history", HistoryCommand) - - registerCmdFn("source", SourceCommand) } func getValidCommands() []string { @@ -239,40 +235,6 @@ func getUITermOpts(uiContext *scpacket.UIContextType) *packet.TermOpts { return termOpts } -func SourceCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) { - ids, err := resolveUiIds(ctx, pk, R_Session|R_Screen|R_Window|R_RemoteConnected) - if err != nil { - return nil, fmt.Errorf("/source error: %w", err) - } - if len(pk.Args) != 1 { - return nil, fmt.Errorf("/source takes one argument (the file to source)") - } - cmdId := scbase.GenSCUUID() - runPacket := packet.MakeRunPacket() - runPacket.ReqId = uuid.New().String() - runPacket.CK = base.MakeCommandKey(ids.SessionId, cmdId) - runPacket.State = ids.Remote.RemoteState - runPacket.StateComplete = true - runPacket.UsePty = true - runPacket.TermOpts = getUITermOpts(pk.UIContext) - runPacket.Command = strings.TrimSpace(fmt.Sprintf("source %s", shellescape.Quote(pk.Args[0]))) - runPacket.ReturnState = true - cmd, callback, err := remote.RunCommand(ctx, cmdId, ids.Remote.RemotePtr, ids.Remote.RemoteState, runPacket) - if callback != nil { - defer callback() - } - if err != nil { - return nil, err - } - update, err := addLineForCmd(ctx, "/source", true, ids, cmd) - if err != nil { - return nil, err - } - update.Interactive = pk.Interactive - sstore.MainBus.SendUpdate(ids.SessionId, update) - return nil, nil -} - func RunCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) { ids, err := resolveUiIds(ctx, pk, R_Session|R_Screen|R_Window|R_RemoteConnected) if err != nil { @@ -280,6 +242,7 @@ func RunCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.U } cmdId := scbase.GenSCUUID() cmdStr := firstArg(pk) + isRtnStateCmd := IsReturnStateCommand(cmdStr) runPacket := packet.MakeRunPacket() runPacket.ReqId = uuid.New().String() runPacket.CK = base.MakeCommandKey(ids.SessionId, cmdId) @@ -288,6 +251,7 @@ func RunCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.U runPacket.UsePty = true runPacket.TermOpts = getUITermOpts(pk.UIContext) runPacket.Command = strings.TrimSpace(cmdStr) + runPacket.ReturnState = resolveBool(pk.Kwargs["rtnstate"], isRtnStateCmd) cmd, callback, err := remote.RunCommand(ctx, cmdId, ids.Remote.RemotePtr, ids.Remote.RemoteState, runPacket) if callback != nil { defer callback() @@ -522,43 +486,6 @@ func SwSetCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore return sstore.ModelUpdate{ScreenWindows: []*sstore.ScreenWindowType{sw}}, nil } -func UnSetCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) { - ids, err := resolveUiIds(ctx, pk, R_Session|R_Window|R_RemoteConnected) - if err != nil { - return nil, fmt.Errorf("cannot unset: %v", err) - } - declMap := shexec.DeclMapFromState(ids.Remote.RemoteState) - unsetVars := make(map[string]bool) - for _, argStr := range pk.Args { - eqIdx := strings.Index(argStr, "=") - if eqIdx != -1 { - return nil, fmt.Errorf("invalid argument to setenv, '%s' (cannot contain equal sign)", argStr) - } - delete(declMap, argStr) - unsetVars[argStr] = true - } - if len(unsetVars) == 0 { - return nil, fmt.Errorf("no variables provided to unset") - } - state := *ids.Remote.RemoteState - state.ShellVars = shexec.SerializeDeclMap(declMap) - remoteInst, err := sstore.UpdateRemoteState(ctx, ids.SessionId, ids.WindowId, ids.Remote.RemotePtr, state) - if err != nil { - return nil, err - } - var cmdOutput bytes.Buffer - displayStateUpdate(&cmdOutput, *ids.Remote.RemoteState, remoteInst.State) - cmd, err := makeStaticCmd(ctx, "unset", ids, pk.GetRawStr(), cmdOutput.Bytes()) - update, err := addLineForCmd(ctx, "/unset", false, ids, cmd) - if err != nil { - // TODO tricky error since the command was a success, but we can't show the output - return nil, err - } - update.Interactive = pk.Interactive - update.Sessions = sstore.MakeSessionsUpdateForRemote(ids.SessionId, remoteInst) - return update, nil -} - func RemoteInstallCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) { ids, err := resolveUiIds(ctx, pk, R_Session|R_Window|R_Remote) if err != nil { @@ -939,56 +866,6 @@ func RemoteCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstor return nil, fmt.Errorf("/remote requires a subcommand: %s", formatStrs([]string{"show"}, "or", false)) } -func SetEnvCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) { - ids, err := resolveUiIds(ctx, pk, R_Session|R_Window|R_RemoteConnected) - if err != nil { - return nil, fmt.Errorf("cannot setenv: %v", err) - } - declMap := shexec.DeclMapFromState(ids.Remote.RemoteState) - if len(pk.Args) == 0 { - var infoLines []string - for _, decl := range declMap { - line := fmt.Sprintf("%s=%s", decl.Name, shellescape.Quote(decl.Value)) - infoLines = append(infoLines, line) - } - update := sstore.ModelUpdate{ - Info: &sstore.InfoMsgType{ - InfoTitle: fmt.Sprintf("environment for remote [%s]", ids.Remote.DisplayName), - InfoLines: infoLines, - }, - } - return update, nil - } - setVars := make(map[string]bool) - for _, argStr := range pk.Args { - eqIdx := strings.Index(argStr, "=") - if eqIdx == -1 { - return nil, fmt.Errorf("invalid argument to setenv, '%s' (no equal sign)", argStr) - } - envName := argStr[:eqIdx] - envVal := argStr[eqIdx+1:] - declMap[envName] = &shexec.DeclareDeclType{Args: "x", Name: envName, Value: envVal} - setVars[envName] = true - } - state := *ids.Remote.RemoteState - state.ShellVars = shexec.SerializeDeclMap(declMap) - remoteInst, err := sstore.UpdateRemoteState(ctx, ids.SessionId, ids.WindowId, ids.Remote.RemotePtr, state) - if err != nil { - return nil, err - } - var cmdOutput bytes.Buffer - displayStateUpdate(&cmdOutput, *ids.Remote.RemoteState, remoteInst.State) - cmd, err := makeStaticCmd(ctx, "setenv", ids, pk.GetRawStr(), cmdOutput.Bytes()) - update, err := addLineForCmd(ctx, "/setenv", false, ids, cmd) - if err != nil { - // TODO tricky error since the command was a success, but we can't show the output - return nil, err - } - update.Interactive = pk.Interactive - update.Sessions = sstore.MakeSessionsUpdateForRemote(ids.SessionId, remoteInst) - return update, nil -} - func CrCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) { ids, err := resolveUiIds(ctx, pk, R_Session|R_Window) if err != nil { @@ -1814,21 +1691,25 @@ func formatTextTable(totalCols int, data [][]string, colMeta []ColMeta) []string func displayStateUpdate(buf *bytes.Buffer, oldState packet.ShellState, newState packet.ShellState) { if newState.Cwd != oldState.Cwd { - buf.WriteString(fmt.Sprintf("cwd %s\r\n", newState.Cwd)) + buf.WriteString(fmt.Sprintf("cwd %s\n", newState.Cwd)) } if !bytes.Equal(newState.ShellVars, oldState.ShellVars) { newEnvMap := shexec.DeclMapFromState(&newState) oldEnvMap := shexec.DeclMapFromState(&oldState) for key, newVal := range newEnvMap { oldVal, found := oldEnvMap[key] - if !found || oldVal.Value != newVal.Value { - buf.WriteString(fmt.Sprintf("%s=%s\r\n", key, shellescape.Quote(newVal.Value))) + if !found || ((oldVal.Value != newVal.Value) || (oldVal.IsExport() != newVal.IsExport())) { + var exportStr string + if newVal.IsExport() { + exportStr = "export " + } + buf.WriteString(fmt.Sprintf("%s%s=%s\n", exportStr, key, ShellQuote(newVal.Value, false, 50))) } } for key, _ := range oldEnvMap { _, found := newEnvMap[key] if !found { - buf.WriteString(fmt.Sprintf("unset %s\r\n", key)) + buf.WriteString(fmt.Sprintf("unset %s\n", key)) } } } @@ -1844,7 +1725,7 @@ func displayStateUpdate(buf *bytes.Buffer, oldState packet.ShellState, newState for aliasName, _ := range oldAliasMap { _, found := newAliasMap[aliasName] if !found { - buf.WriteString(fmt.Sprintf("unalias %s\r\n", shellescape.Quote(aliasName))) + buf.WriteString(fmt.Sprintf("unalias %s\n", shellescape.Quote(aliasName))) } } } @@ -1860,8 +1741,27 @@ func displayStateUpdate(buf *bytes.Buffer, oldState packet.ShellState, newState for funcName, _ := range oldFuncMap { _, found := newFuncMap[funcName] if !found { - buf.WriteString(fmt.Sprintf("unset -f %s\r\n", shellescape.Quote(funcName))) + buf.WriteString(fmt.Sprintf("unset -f %s\n", shellescape.Quote(funcName))) } } } } + +func GetRtnStateDiff(ctx context.Context, sessionId string, cmdId string) ([]byte, error) { + cmd, err := sstore.GetCmdById(ctx, sessionId, cmdId) + if err != nil { + return nil, err + } + if cmd == nil { + return nil, nil + } + if !cmd.RtnState { + return nil, nil + } + if cmd.DonePk == nil || cmd.DonePk.FinalState == nil { + return nil, nil + } + var outputBytes bytes.Buffer + displayStateUpdate(&outputBytes, cmd.RemoteState, *cmd.DonePk.FinalState) + return outputBytes.Bytes(), nil +} diff --git a/pkg/cmdrunner/shparse.go b/pkg/cmdrunner/shparse.go index 3a841e4c..898db34f 100644 --- a/pkg/cmdrunner/shparse.go +++ b/pkg/cmdrunner/shparse.go @@ -59,14 +59,8 @@ type BareMetaCmdDecl struct { } var BareMetaCmds = []BareMetaCmdDecl{ - BareMetaCmdDecl{"cd", "cd"}, BareMetaCmdDecl{"cr", "cr"}, - BareMetaCmdDecl{"setenv", "setenv"}, - BareMetaCmdDecl{"export", "setenv"}, - BareMetaCmdDecl{"unset", "unset"}, BareMetaCmdDecl{"clear", "clear"}, - BareMetaCmdDecl{".", "source"}, - BareMetaCmdDecl{"source", "source"}, BareMetaCmdDecl{"reset", "reset"}, } @@ -123,7 +117,7 @@ func onlyRawArgs(metaCmd string, metaSubCmd string) bool { } // minimum maxlen=6 -func ForceQuote(val string, maxLen int) string { +func ShellQuote(val string, forceQuote bool, maxLen int) string { if maxLen < 6 { maxLen = 6 } @@ -134,10 +128,17 @@ func ForceQuote(val string, maxLen int) string { } return rtn } - if len(rtn) > maxLen-2 { - return "\"" + rtn[0:maxLen-5] + "...\"" + if forceQuote { + if len(rtn) > maxLen-2 { + return "\"" + rtn[0:maxLen-5] + "...\"" + } + return "\"" + rtn + "\"" + } else { + if len(rtn) > maxLen { + return rtn[0:maxLen-3] + "..." + } + return rtn } - return "\"" + rtn + "\"" } func setBracketArgs(argMap map[string]string, bracketStr string) error { @@ -163,7 +164,7 @@ func setBracketArgs(argMap map[string]string, bracketStr string) error { varVal = litStr[eqIdx+1:] } if !shexec.IsValidBashIdentifier(varName) { - wordErr = fmt.Errorf("invalid identifier %s in bracket args", ForceQuote(varName, 20)) + wordErr = fmt.Errorf("invalid identifier %s in bracket args", ShellQuote(varName, true, 20)) return false } if varVal == "" { @@ -181,6 +182,35 @@ func setBracketArgs(argMap map[string]string, bracketStr string) error { return nil } +// detects: export, declare, ., source, X=1, unset +func IsReturnStateCommand(cmdStr string) bool { + cmdReader := strings.NewReader(cmdStr) + parser := syntax.NewParser(syntax.Variant(syntax.LangBash)) + file, err := parser.Parse(cmdReader, "cmd") + if err != nil { + return false + } + for _, stmt := range file.Stmts { + if callExpr, ok := stmt.Cmd.(*syntax.CallExpr); ok { + if len(callExpr.Assigns) > 0 && len(callExpr.Args) == 0 { + return true + } + if len(callExpr.Args) > 0 && len(callExpr.Args[0].Parts) > 0 { + lit, ok := callExpr.Args[0].Parts[0].(*syntax.Lit) + if ok { + if lit.Value == "." || lit.Value == "source" || lit.Value == "unset" || lit.Value == "cd" { + return true + } + } + + } + } else if _, ok := stmt.Cmd.(*syntax.DeclClause); ok { + return true + } + } + return false +} + func EvalBracketArgs(origCmdStr string) (map[string]string, string, error) { rtn := make(map[string]string) if strings.HasPrefix(origCmdStr, " ") { @@ -210,7 +240,11 @@ func EvalMetaCommand(ctx context.Context, origPk *scpacket.FeCommandPacketType) if strings.TrimSpace(origPk.Args[0]) == "" { return nil, fmt.Errorf("empty command") } - metaCmd, metaSubCmd, commandArgs := parseMetaCmd(origPk.Args[0]) + bracketArgs, cmdStr, err := EvalBracketArgs(origPk.Args[0]) + if err != nil { + return nil, err + } + metaCmd, metaSubCmd, commandArgs := parseMetaCmd(cmdStr) rtnPk := scpacket.MakeFeCommandPacket() rtnPk.MetaCmd = metaCmd rtnPk.MetaSubCmd = metaSubCmd @@ -220,6 +254,9 @@ func EvalMetaCommand(ctx context.Context, origPk *scpacket.FeCommandPacketType) for key, val := range origPk.Kwargs { rtnPk.Kwargs[key] = val } + for key, val := range bracketArgs { + rtnPk.Kwargs[key] = val + } if onlyRawArgs(metaCmd, metaSubCmd) { // don't evaluate arguments for /run or /comment rtnPk.Args = []string{commandArgs} @@ -228,7 +265,7 @@ func EvalMetaCommand(ctx context.Context, origPk *scpacket.FeCommandPacketType) commandReader := strings.NewReader(commandArgs) parser := syntax.NewParser(syntax.Variant(syntax.LangBash)) var words []*syntax.Word - err := parser.Words(commandReader, func(w *syntax.Word) bool { + err = parser.Words(commandReader, func(w *syntax.Word) bool { words = append(words, w) return true }) @@ -338,6 +375,9 @@ func ParseFuncs(funcs string) (map[string]string, error) { fmt.Printf("stmt-err: %v\n", err) continue } + if strings.HasPrefix(funcName, "_scripthaus_") { + continue + } if funcName != "" { rtn[funcName] = funcVal } diff --git a/pkg/remote/remote.go b/pkg/remote/remote.go index cc325e8a..7b76ebb9 100644 --- a/pkg/remote/remote.go +++ b/pkg/remote/remote.go @@ -1106,6 +1106,7 @@ func RunCommand(ctx context.Context, cmdId string, remotePtr sstore.RemotePtrTyp StartPk: startPk, DonePk: nil, RunOut: nil, + RtnState: runPacket.ReturnState, } err = sstore.CreateCmdPtyFile(ctx, cmd.SessionId, cmd.CmdId, cmd.TermOpts.MaxPtySize) if err != nil { diff --git a/pkg/sstore/dbops.go b/pkg/sstore/dbops.go index 986c022c..fa07751e 100644 --- a/pkg/sstore/dbops.go +++ b/pkg/sstore/dbops.go @@ -623,8 +623,8 @@ func InsertLine(ctx context.Context, line *LineType, cmd *CmdType) error { query = `SELECT nextlinenum FROM window WHERE sessionid = ? AND windowid = ?` nextLineNum := tx.GetInt(query, line.SessionId, line.WindowId) line.LineNum = int64(nextLineNum) - query = `INSERT INTO line ( sessionid, windowid, userid, lineid, ts, linenum, linenumtemp, linelocal, linetype, text, cmdid, ephemeral) - VALUES (:sessionid,:windowid,:userid,:lineid,:ts,:linenum,:linenumtemp,:linelocal,:linetype,:text,:cmdid,:ephemeral)` + query = `INSERT INTO line ( sessionid, windowid, userid, lineid, ts, linenum, linenumtemp, linelocal, linetype, text, cmdid, ephemeral, contentheight) + VALUES (:sessionid,:windowid,:userid,:lineid,:ts,:linenum,:linenumtemp,:linelocal,:linetype,:text,:cmdid,:ephemeral,:contentheight)` tx.NamedExecWrap(query, line) query = `UPDATE window SET nextlinenum = ? WHERE sessionid = ? AND windowid = ?` tx.ExecWrap(query, nextLineNum+1, line.SessionId, line.WindowId) @@ -632,8 +632,8 @@ func InsertLine(ctx context.Context, line *LineType, cmd *CmdType) error { cmd.OrigTermOpts = cmd.TermOpts cmdMap := cmd.ToMap() query = ` -INSERT INTO cmd ( sessionid, cmdid, remoteownerid, remoteid, remotename, cmdstr, remotestate, termopts, origtermopts, status, startpk, donepk, runout, usedrows) - VALUES (:sessionid,:cmdid,:remoteownerid,:remoteid,:remotename,:cmdstr,:remotestate,:termopts,:origtermopts,:status,:startpk,:donepk,:runout,:usedrows) +INSERT INTO cmd ( sessionid, cmdid, remoteownerid, remoteid, remotename, cmdstr, remotestate, termopts, origtermopts, status, startpk, donepk, rtnstate, runout) + VALUES (:sessionid,:cmdid,:remoteownerid,:remoteid,:remotename,:cmdstr,:remotestate,:termopts,:origtermopts,:status,:startpk,:donepk,:rtnstate,:runout) ` tx.NamedExecWrap(query, cmdMap) } diff --git a/pkg/sstore/sstore.go b/pkg/sstore/sstore.go index 0350296c..175173f9 100644 --- a/pkg/sstore/sstore.go +++ b/pkg/sstore/sstore.go @@ -28,6 +28,7 @@ import ( const LineTypeCmd = "cmd" const LineTypeText = "text" +const LineNoHeight = -1 const DBFileName = "sh2.db" const DefaultSessionName = "default" @@ -496,19 +497,20 @@ func RIFromMap(m map[string]interface{}) *RemoteInstance { } type LineType struct { - SessionId string `json:"sessionid"` - WindowId string `json:"windowid"` - 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"` - Text string `json:"text,omitempty"` - CmdId string `json:"cmdid,omitempty"` - Ephemeral bool `json:"ephemeral,omitempty"` - Remove bool `json:"remove,omitempty"` + SessionId string `json:"sessionid"` + WindowId string `json:"windowid"` + 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"` + Text string `json:"text,omitempty"` + CmdId string `json:"cmdid,omitempty"` + Ephemeral bool `json:"ephemeral,omitempty"` + Remove bool `json:"remove,omitempty"` + ContentHeight int64 `json:"contentheight,omitempty"` } type ResolveItem struct { @@ -575,11 +577,11 @@ type CmdType struct { TermOpts TermOpts `json:"termopts"` OrigTermOpts TermOpts `json:"origtermopts"` Status string `json:"status"` - StartPk *packet.CmdStartPacketType `json:"startpk"` - DonePk *packet.CmdDonePacketType `json:"donepk"` - UsedRows int64 `json:"usedrows"` - RunOut []packet.PacketType `json:"runout"` - Remove bool `json:"remove"` + StartPk *packet.CmdStartPacketType `json:"startpk,omitempty"` + DonePk *packet.CmdDonePacketType `json:"donepk,omitempty"` + RunOut []packet.PacketType `json:"runout,omitempty"` + RtnState bool `json:"rtnstate,omitempty"` + Remove bool `json:"remove,omitempty"` } func (r *RemoteType) ToMap() map[string]interface{} { @@ -644,7 +646,7 @@ func (cmd *CmdType) ToMap() map[string]interface{} { rtn["startpk"] = quickJson(cmd.StartPk) rtn["donepk"] = quickJson(cmd.DonePk) rtn["runout"] = quickJson(cmd.RunOut) - rtn["usedrows"] = cmd.UsedRows + rtn["rtnstate"] = cmd.RtnState return rtn } @@ -666,7 +668,7 @@ func CmdFromMap(m map[string]interface{}) *CmdType { quickSetJson(&cmd.StartPk, m, "startpk") quickSetJson(&cmd.DonePk, m, "donepk") quickSetJson(&cmd.RunOut, m, "runout") - quickSetInt64(&cmd.UsedRows, m, "usedrows") + quickSetBool(&cmd.RtnState, m, "rtnstate") return &cmd } @@ -680,6 +682,7 @@ func makeNewLineCmd(sessionId string, windowId string, userId string, cmdId stri rtn.LineLocal = true rtn.LineType = LineTypeCmd rtn.CmdId = cmdId + rtn.ContentHeight = LineNoHeight return rtn } @@ -693,6 +696,7 @@ func makeNewLineText(sessionId string, windowId string, userId string, text stri rtn.LineLocal = true rtn.LineType = LineTypeText rtn.Text = text + rtn.ContentHeight = LineNoHeight return rtn }