waveterm/wavesrv/pkg/scpacket/scpacket.go
Mike Sawka 3c3eec73aa
reinit updates (#500)
* working on re-init when you create a tab.  some refactoring of existing reinit to make the messaging clearer.  auto-connect, etc.

* working to remove the 'default' shell states out of MShellProc.  each tab should have its own state that gets set on open.

* refactor newtab settings into individual components (and move to a new file)

* more refactoring of tab settings -- use same control in settings and newtab

* have screensettings use the same newtab settings components

* use same conn dropdown, fix classes, update some of the confirm messages to be less confusing (replace screen with tab)

* force a cr on a new tab to initialize state in a new line.  poc right now, need to add to new workspace workflow as well

* small fixups

* remove nohist from GetRawStr, make const

* update hover behavior for tabs

* fix interaction between change remote dropdown, cmdinput, error handling, and selecting a remote

* only switch screen remote if the activemainview is session (new remote flow).  don't switch it if we're on the connections page which is confusing.  also make it interactive

* fix wording on tos modal

* allow empty workspaces. also allow the last workspace to be deleted.  (prep for new startup sequence where we initialize the first session after tos modal)

* add some dead code that might come in use later (when we change how we show connection in cmdinput)

* working a cople different angles.  new settings tab-pulldown (likely orphaned).  and then allowing null activeScreen and null activeSession in workspaceview (show appropriate messages, and give buttons to create new tabs/workspaces).  prep for new startup flow

* don't call initActiveShells anymore.  also call ensureWorkspace() on TOS close

* trying to use new pulldown screen settings

* experiment with an escape keybinding

* working on tab settings close triggers

* close tab settings on tab switch

* small updates to tos popup, reorder, update button text/size, small wording updates

* when deleting a screen, send SIGHUP to all running commands

* not sure how this happened, lineid should not be passed to setLineFocus

* remove context timeouts for ReInit (it is now interactive, so it gets canceled like a normal command -- via ^C, and should not timeout on its own)

* deal with screen/session tombstones updates (ignore to quite warning)

* remove defaultfestate from remote

* fix issue with removing default ris

* remove dead code

* open the settings pulldown for new screens

* update prompt to show when the shell is still initializing (or if it failed)

* switch buttons to use wave button class, update messages, and add warning for no shell state

* all an override of rptr for dyncmds.  needed for the 'connect' command (we need to set the rptr to the *new* connection rather than the old one)

* remove old commented out code
2024-03-27 00:22:57 -07:00

221 lines
5.8 KiB
Go

// Copyright 2023, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
package scpacket
import (
"encoding/base64"
"fmt"
"reflect"
"regexp"
"strings"
"github.com/alessio/shellescape"
"github.com/google/uuid"
"github.com/wavetermdev/waveterm/waveshell/pkg/base"
"github.com/wavetermdev/waveterm/waveshell/pkg/packet"
"github.com/wavetermdev/waveterm/waveshell/pkg/utilfn"
)
var RemoteNameRe = regexp.MustCompile("^\\*?[a-zA-Z0-9_-]+$")
type RemotePtrType struct {
OwnerId string `json:"ownerid"`
RemoteId string `json:"remoteid"`
Name string `json:"name"`
}
func (r RemotePtrType) IsSessionScope() bool {
return strings.HasPrefix(r.Name, "*")
}
func (rptr *RemotePtrType) GetDisplayName(baseDisplayName string) string {
name := baseDisplayName
if rptr == nil {
return name
}
if rptr.Name != "" {
name = name + ":" + rptr.Name
}
if rptr.OwnerId != "" {
name = "@" + rptr.OwnerId + ":" + name
}
return name
}
func (r RemotePtrType) Validate() error {
if r.OwnerId != "" {
if _, err := uuid.Parse(r.OwnerId); err != nil {
return fmt.Errorf("invalid ownerid format: %v", err)
}
}
if r.RemoteId != "" {
if _, err := uuid.Parse(r.RemoteId); err != nil {
return fmt.Errorf("invalid remoteid format: %v", err)
}
}
if r.Name != "" {
ok := RemoteNameRe.MatchString(r.Name)
if !ok {
return fmt.Errorf("invalid remote name")
}
}
return nil
}
func (r RemotePtrType) MakeFullRemoteRef() string {
if r.RemoteId == "" {
return ""
}
if r.OwnerId == "" && r.Name == "" {
return r.RemoteId
}
if r.OwnerId != "" && r.Name == "" {
return fmt.Sprintf("@%s:%s", r.OwnerId, r.RemoteId)
}
if r.OwnerId == "" && r.Name != "" {
return fmt.Sprintf("%s:%s", r.RemoteId, r.Name)
}
return fmt.Sprintf("@%s:%s:%s", r.OwnerId, r.RemoteId, r.Name)
}
const FeCommandPacketStr = "fecmd"
const WatchScreenPacketStr = "watchscreen"
const FeInputPacketStr = "feinput"
const RemoteInputPacketStr = "remoteinput"
const CmdInputTextPacketStr = "cmdinputtext"
type FeCommandPacketType struct {
Type string `json:"type"`
MetaCmd string `json:"metacmd"`
MetaSubCmd string `json:"metasubcmd,omitempty"`
Args []string `json:"args,omitempty"`
Kwargs map[string]string `json:"kwargs,omitempty"`
RawStr string `json:"rawstr,omitempty"`
UIContext *UIContextType `json:"uicontext,omitempty"`
Interactive bool `json:"interactive"`
}
func (pk *FeCommandPacketType) GetRawStr() string {
if pk.RawStr != "" {
return pk.RawStr
}
cmd := "/" + pk.MetaCmd
if pk.MetaSubCmd != "" {
cmd = cmd + ":" + pk.MetaSubCmd
}
var args []string
for k, v := range pk.Kwargs {
if k == "nohist" {
continue
}
argStr := fmt.Sprintf("%s=%s", shellescape.Quote(k), shellescape.Quote(v))
args = append(args, argStr)
}
for _, arg := range pk.Args {
args = append(args, shellescape.Quote(arg))
}
if len(args) == 0 {
return cmd
}
return cmd + " " + strings.Join(args, " ")
}
type UIContextType struct {
SessionId string `json:"sessionid"`
ScreenId string `json:"screenid"`
Remote *RemotePtrType `json:"remote,omitempty"`
WinSize *packet.WinSize `json:"winsize,omitempty"`
Build string `json:"build,omitempty"`
}
type FeInputPacketType struct {
Type string `json:"type"`
CK base.CommandKey `json:"ck"`
Remote RemotePtrType `json:"remote"`
InputData64 string `json:"inputdata64"`
SigName string `json:"signame,omitempty"`
WinSize *packet.WinSize `json:"winsize,omitempty"`
}
type RemoteInputPacketType struct {
Type string `json:"type"`
RemoteId string `json:"remoteid"`
InputData64 string `json:"inputdata64"`
}
type WatchScreenPacketType struct {
Type string `json:"type"`
SessionId string `json:"sessionid"`
ScreenId string `json:"screenid"`
Connect bool `json:"connect"`
AuthKey string `json:"authkey"`
}
type CmdInputTextPacketType struct {
Type string `json:"type"`
SeqNum int `json:"seqnum"`
ScreenId string `json:"screenid"`
Text utilfn.StrWithPos `json:"text"`
}
func init() {
packet.RegisterPacketType(FeCommandPacketStr, reflect.TypeOf(FeCommandPacketType{}))
packet.RegisterPacketType(WatchScreenPacketStr, reflect.TypeOf(WatchScreenPacketType{}))
packet.RegisterPacketType(FeInputPacketStr, reflect.TypeOf(FeInputPacketType{}))
packet.RegisterPacketType(RemoteInputPacketStr, reflect.TypeOf(RemoteInputPacketType{}))
packet.RegisterPacketType(CmdInputTextPacketStr, reflect.TypeOf(CmdInputTextPacketType{}))
}
type PacketType interface {
GetType() string
}
func (*CmdInputTextPacketType) GetType() string {
return CmdInputTextPacketStr
}
func MakeCmdInputTextPacket(screenId string) *CmdInputTextPacketType {
return &CmdInputTextPacketType{Type: CmdInputTextPacketStr, ScreenId: screenId}
}
func (*FeCommandPacketType) GetType() string {
return FeCommandPacketStr
}
func MakeFeCommandPacket() *FeCommandPacketType {
return &FeCommandPacketType{Type: FeCommandPacketStr}
}
func (*FeInputPacketType) GetType() string {
return FeInputPacketStr
}
func MakeFeInputPacket() *FeInputPacketType {
return &FeInputPacketType{Type: FeInputPacketStr}
}
func (pk *FeInputPacketType) DecodeData() ([]byte, error) {
return base64.StdEncoding.DecodeString(pk.InputData64)
}
func (pk *FeInputPacketType) SetData(data []byte) {
pk.InputData64 = base64.StdEncoding.EncodeToString(data)
}
func (*WatchScreenPacketType) GetType() string {
return WatchScreenPacketStr
}
func MakeWatchScreenPacket() *WatchScreenPacketType {
return &WatchScreenPacketType{Type: WatchScreenPacketStr}
}
func MakeRemoteInputPacket() *RemoteInputPacketType {
return &RemoteInputPacketType{Type: RemoteInputPacketStr}
}
func (*RemoteInputPacketType) GetType() string {
return RemoteInputPacketStr
}