mirror of
https://github.com/wavetermdev/waveterm.git
synced 2024-12-23 16:58:27 +01:00
implement /cd and /compgen
This commit is contained in:
parent
66f547a695
commit
a175d236c1
@ -3,6 +3,8 @@ package cmdrunner
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@ -32,6 +34,7 @@ type resolvedIds struct {
|
|||||||
ScreenId string
|
ScreenId string
|
||||||
WindowId string
|
WindowId string
|
||||||
RemoteId string
|
RemoteId string
|
||||||
|
RemoteName string
|
||||||
RemoteState *sstore.RemoteState
|
RemoteState *sstore.RemoteState
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,6 +62,13 @@ func firstArg(pk *scpacket.FeCommandPacketType) string {
|
|||||||
return pk.Args[0]
|
return pk.Args[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func argN(pk *scpacket.FeCommandPacketType, n int) string {
|
||||||
|
if len(pk.Args) <= n {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return pk.Args[n]
|
||||||
|
}
|
||||||
|
|
||||||
func resolveBool(arg string, def bool) bool {
|
func resolveBool(arg string, def bool) bool {
|
||||||
if arg == "" {
|
if arg == "" {
|
||||||
return def
|
return def
|
||||||
@ -138,22 +148,22 @@ func resolveScreenId(ctx context.Context, pk *scpacket.FeCommandPacketType, sess
|
|||||||
return resolveSessionScreen(ctx, sessionId, screenArg)
|
return resolveSessionScreen(ctx, sessionId, screenArg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func resolveRemote(ctx context.Context, pk *scpacket.FeCommandPacketType, sessionId string, windowId string) (string, *sstore.RemoteState, error) {
|
func resolveRemote(ctx context.Context, pk *scpacket.FeCommandPacketType, sessionId string, windowId string) (string, string, *sstore.RemoteState, error) {
|
||||||
remoteArg := pk.Kwargs["remote"]
|
remoteName := pk.Kwargs["remote"]
|
||||||
if remoteArg == "" {
|
if remoteName == "" {
|
||||||
return "", nil, nil
|
return "", "", nil, nil
|
||||||
}
|
}
|
||||||
remoteId, state, err := sstore.GetRemoteState(ctx, remoteArg, sessionId, windowId)
|
remoteId, state, err := sstore.GetRemoteState(ctx, remoteName, sessionId, windowId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil, fmt.Errorf("cannot resolve remote '%s': %w", remoteArg, err)
|
return "", "", nil, fmt.Errorf("cannot resolve remote '%s': %w", remoteName, err)
|
||||||
}
|
}
|
||||||
if state == nil {
|
if state == nil {
|
||||||
state, err = remote.GetDefaultRemoteStateById(remoteId)
|
state, err = remote.GetDefaultRemoteStateById(remoteId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil, fmt.Errorf("cannot resolve remote '%s': %w", remoteArg, err)
|
return "", "", nil, fmt.Errorf("cannot resolve remote '%s': %w", remoteName, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return remoteId, state, nil
|
return remoteName, remoteId, state, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func resolveIds(ctx context.Context, pk *scpacket.FeCommandPacketType, rtype int) (resolvedIds, error) {
|
func resolveIds(ctx context.Context, pk *scpacket.FeCommandPacketType, rtype int) (resolvedIds, error) {
|
||||||
@ -191,7 +201,7 @@ func resolveIds(ctx context.Context, pk *scpacket.FeCommandPacketType, rtype int
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (rtype&R_Remote)+(rtype&R_RemoteOpt) > 0 {
|
if (rtype&R_Remote)+(rtype&R_RemoteOpt) > 0 {
|
||||||
rtn.RemoteId, rtn.RemoteState, err = resolveRemote(ctx, pk, rtn.SessionId, rtn.WindowId)
|
rtn.RemoteName, rtn.RemoteId, rtn.RemoteState, err = resolveRemote(ctx, pk, rtn.SessionId, rtn.WindowId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return rtn, err
|
return rtn, err
|
||||||
}
|
}
|
||||||
@ -222,6 +232,9 @@ func HandleCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstor
|
|||||||
case "cd":
|
case "cd":
|
||||||
return CdCommand(ctx, pk)
|
return CdCommand(ctx, pk)
|
||||||
|
|
||||||
|
case "compgen":
|
||||||
|
return CompGenCommand(ctx, pk)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("invalid command '/%s', no handler", pk.MetaCmd)
|
return nil, fmt.Errorf("invalid command '/%s', no handler", pk.MetaCmd)
|
||||||
}
|
}
|
||||||
@ -354,19 +367,102 @@ func CdCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.Up
|
|||||||
return nil, fmt.Errorf("/cd error: %w", err)
|
return nil, fmt.Errorf("/cd error: %w", err)
|
||||||
}
|
}
|
||||||
newDir := firstArg(pk)
|
newDir := firstArg(pk)
|
||||||
|
if newDir == "" {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
if !strings.HasPrefix(newDir, "/") {
|
||||||
|
if ids.RemoteState == nil {
|
||||||
|
return nil, fmt.Errorf("/cd error: cannot get current remote directory (can only cd with absolute path)")
|
||||||
|
}
|
||||||
|
newDir = path.Join(ids.RemoteState.Cwd, newDir)
|
||||||
|
newDir, err = filepath.Abs(newDir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("/cd error: error canonicalizing new directory: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Printf("cd [%s] => [%s]\n", firstArg(pk), newDir)
|
||||||
cdPacket := packet.MakeCdPacket()
|
cdPacket := packet.MakeCdPacket()
|
||||||
cdPacket.ReqId = uuid.New().String()
|
cdPacket.ReqId = uuid.New().String()
|
||||||
cdPacket.Dir = newDir
|
cdPacket.Dir = newDir
|
||||||
localRemote := remote.GetRemoteById(ids.RemoteId)
|
curRemote := remote.GetRemoteById(ids.RemoteId)
|
||||||
if localRemote == nil {
|
if curRemote == nil {
|
||||||
return nil, fmt.Errorf("invalid remote, cannot execute command")
|
return nil, fmt.Errorf("invalid remote, cannot execute command")
|
||||||
}
|
}
|
||||||
resp, err := localRemote.PacketRpc(ctx, cdPacket)
|
_, err = curRemote.PacketRpc(ctx, cdPacket)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
fmt.Printf("GOT cd RESP: %v\n", resp)
|
remote, err := sstore.UpdateRemoteCwd(ctx, ids.RemoteName, ids.SessionId, ids.WindowId, ids.RemoteId, newDir)
|
||||||
return nil, nil
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
update := sstore.WindowUpdate{
|
||||||
|
Window: sstore.WindowType{
|
||||||
|
SessionId: ids.SessionId,
|
||||||
|
WindowId: ids.WindowId,
|
||||||
|
Remotes: []*sstore.RemoteInstance{remote},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return update, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getStrArr(v interface{}, field string) []string {
|
||||||
|
if v == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
m, ok := v.(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
fieldVal := m[field]
|
||||||
|
if fieldVal == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
iarr, ok := fieldVal.([]interface{})
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var sarr []string
|
||||||
|
for _, iv := range iarr {
|
||||||
|
if sv, ok := iv.(string); ok {
|
||||||
|
sarr = append(sarr, sv)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sarr
|
||||||
|
}
|
||||||
|
|
||||||
|
func CompGenCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) {
|
||||||
|
ids, err := resolveIds(ctx, pk, R_Session|R_Window|R_Remote)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("/compgen error: %w", err)
|
||||||
|
}
|
||||||
|
compType := argN(pk, 0)
|
||||||
|
prefix := argN(pk, 1)
|
||||||
|
if !packet.IsValidCompGenType(compType) {
|
||||||
|
return nil, fmt.Errorf("/compgen invalid type '%s'", compType)
|
||||||
|
}
|
||||||
|
cgPacket := packet.MakeCompGenPacket()
|
||||||
|
cgPacket.ReqId = uuid.New().String()
|
||||||
|
cgPacket.CompType = compType
|
||||||
|
cgPacket.Prefix = prefix
|
||||||
|
if ids.RemoteState == nil {
|
||||||
|
return nil, fmt.Errorf("/compgen invalid remote state")
|
||||||
|
}
|
||||||
|
cgPacket.Cwd = ids.RemoteState.Cwd
|
||||||
|
curRemote := remote.GetRemoteById(ids.RemoteId)
|
||||||
|
if curRemote == nil {
|
||||||
|
return nil, fmt.Errorf("invalid remote, cannot execute command")
|
||||||
|
}
|
||||||
|
resp, err := curRemote.PacketRpc(ctx, cgPacket)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
comps := getStrArr(resp.Data, "comps")
|
||||||
|
update := sstore.InfoUpdate{
|
||||||
|
InfoTitle: fmt.Sprintf("%s completions", compType),
|
||||||
|
InfoStrings: comps,
|
||||||
|
}
|
||||||
|
return update, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func CommentCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) {
|
func CommentCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) {
|
||||||
|
@ -503,3 +503,34 @@ func GetRemoteState(ctx context.Context, rname string, sessionId string, windowI
|
|||||||
})
|
})
|
||||||
return remoteId, remoteState, txErr
|
return remoteId, remoteState, txErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func UpdateRemoteCwd(ctx context.Context, rname string, sessionId string, windowId string, remoteId string, cwd string) (*RemoteInstance, error) {
|
||||||
|
var ri RemoteInstance
|
||||||
|
txErr := WithTx(ctx, func(tx *TxWrap) error {
|
||||||
|
query := `SELECT windowid FROM window WHERE sessionid = ? AND windowid = ?`
|
||||||
|
if !tx.Exists(query, sessionId, windowId) {
|
||||||
|
return fmt.Errorf("cannot update remote instance cwd, no window found")
|
||||||
|
}
|
||||||
|
query = `SELECT * FROM remote_instance WHERE sessionid = ? AND windowid = ? AND name = ?`
|
||||||
|
found := tx.GetWrap(&ri, query, sessionId, windowId, rname)
|
||||||
|
if !found {
|
||||||
|
ri = RemoteInstance{
|
||||||
|
RIId: uuid.New().String(),
|
||||||
|
Name: rname,
|
||||||
|
SessionId: sessionId,
|
||||||
|
WindowId: windowId,
|
||||||
|
RemoteId: remoteId,
|
||||||
|
SessionScope: (windowId == ""),
|
||||||
|
State: RemoteState{Cwd: cwd},
|
||||||
|
}
|
||||||
|
query = `INSERT INTO remote_instance (riid, name, sessionid, windowid, remoteid, sessionscope, state) VALUES (:riid, :name, :sessionid, :windowid, :remoteid, :sessionscope, :state)`
|
||||||
|
tx.NamedExecWrap(query, ri)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
ri.State.Cwd = cwd
|
||||||
|
query = `UPDATE remote_instance SET state = ? WHERE sessionid = ? AND windowid = ? AND name = ?`
|
||||||
|
tx.ExecWrap(query, ri.State, ri.SessionId, ri.WindowId, ri.Name)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return &ri, txErr
|
||||||
|
}
|
||||||
|
@ -9,6 +9,7 @@ const SessionUpdateStr = "session"
|
|||||||
const WindowUpdateStr = "window"
|
const WindowUpdateStr = "window"
|
||||||
const CmdUpdateStr = "cmd"
|
const CmdUpdateStr = "cmd"
|
||||||
const LineCmdUpdateStr = "line+cmd"
|
const LineCmdUpdateStr = "line+cmd"
|
||||||
|
const InfoUpdateStr = "info"
|
||||||
|
|
||||||
type UpdatePacket interface {
|
type UpdatePacket interface {
|
||||||
UpdateType() string
|
UpdateType() string
|
||||||
@ -36,7 +37,7 @@ type WindowUpdate struct {
|
|||||||
Remove bool `json:"remove,omitempty"`
|
Remove bool `json:"remove,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (WindowUpdate) WindowUpdate() string {
|
func (WindowUpdate) UpdateType() string {
|
||||||
return WindowUpdateStr
|
return WindowUpdateStr
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,6 +71,15 @@ func (LineUpdate) UpdateType() string {
|
|||||||
return LineCmdUpdateStr
|
return LineCmdUpdateStr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type InfoUpdate struct {
|
||||||
|
InfoTitle string `json:"infotitle"`
|
||||||
|
InfoStrings []string `json:"infostrings"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (InfoUpdate) UpdateType() string {
|
||||||
|
return InfoUpdateStr
|
||||||
|
}
|
||||||
|
|
||||||
type UpdateChannel struct {
|
type UpdateChannel struct {
|
||||||
SessionId string
|
SessionId string
|
||||||
ClientId string
|
ClientId string
|
||||||
|
Loading…
Reference in New Issue
Block a user