2023-10-17 06:31:13 +02:00
|
|
|
// Copyright 2023, Command Line Inc.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
2022-07-13 23:16:08 +02:00
|
|
|
package sstore
|
|
|
|
|
2022-09-01 21:47:10 +02:00
|
|
|
import (
|
|
|
|
"fmt"
|
2022-10-31 20:40:45 +01:00
|
|
|
"log"
|
2022-09-01 21:47:10 +02:00
|
|
|
"sync"
|
2023-12-26 21:59:25 +01:00
|
|
|
|
2024-01-12 02:34:23 +01:00
|
|
|
"github.com/wavetermdev/waveterm/waveshell/pkg/packet"
|
zsh support (#227)
adds zsh support to waveterm. big change, lots going on here. lots of other improvements and bug fixes added while debugging and building out the feature.
Commits:
* refactor shexec parser.go into new package shellenv. separate out bash specific parsing from generic functions
* checkpoint
* work on refactoring shexec. created two new packages shellapi (for bash/zsh specific stuff), and shellutil (shared between shellapi and shexec)
* more refactoring
* create shellapi interface to abstract bash specific functionality
* more refactoring, move bash shell state parsing to shellapi
* move makeRcFile to shellapi. remove all of the 'client' options CLI options from waveshell
* get shellType passed through to server/single paths for waveshell
* add a local shelltype detector
* mock out a zshapi
* move shelltype through more of the code
* get a command to run via zsh
* zsh can now switch directories. poc, needs cleanup
* working on ShellState encoding differences between zsh/bash. Working on parsing zsh decls. move utilfn package into waveshell (shouldn't have been in wavesrv)
* switch to use []byte for vardecl serialization + diffs
* progress on zsh environment. still have issues reconciling init environment with trap environment
* fix typeset argument parsing
* parse promptvars, more zsh specific ignores
* fix bug with promptvar not getting set (wrong check in FeState func)
* add sdk (issue #188) to list of rtnstate commands
* more zsh compatibility -- working with a larger ohmyzsh environment. ignore more variables, handle exit trap better. unique path/fpath. add a processtype variable to base.
* must return a value
* zsh alias parsing/restoring. diff changes (and rtnstate changes). introduces linediff v1.
* force zmodload of zsh/parameter
* starting work on zsh functions
* need a v1 of mapdiff as well (to handle null chars)
* pack/unpack of ints was wrong (one used int and one use uint). turned out we only ever encoded '0' so it worked. that also means it is safe to change unpack to unpackUInt
* reworking for binary encoding of aliases and functions (because of zsh allows any character, including nulls, in names and values)
* fixes, working on functions, issue with line endings
* zsh functions. lots of ugliness here around dealing with line dicipline and cooked stty. new runcommand function to grab output from a non-tty fd. note that we still to run the actual command in a stty to get the proper output.
* write uuid tempdir, cleanup with tmprcfilename code
* hack in some simple zsh function declaration finding code for rtnstate. create function diff for rtnstate that supports zsh
* make sure key order is constant so shell hashes are consistent
* fix problems with state diffs to support new zsh formats. add diff/apply code to shellapi (moved from shellenv), that is now specific to zsh or bash
* add log packet and new shellstate packets
* switch to shellstate map that's also keyed by shelltype
* add shelltype to remoteinstance
* remove shell argument from waveshell
* added new shelltype statemap to remote.go (msh), deal with fallout
* move shellstate out of init packet, and move to an explicit reinit call. try to initialize all of the active shell states
* change dont always store init state (only store on demand). initialize shell states on demand (if not already initialized). allow reset to change shells
* add shellpref field to remote table. use to drive the default shell choice for new tabs
* show shelltag on cmdinput, pass through ri and remote (defaultshellstate)
* bump mshell version to v0.4
* better version validation for shellstate. also relax compatibility requirements for diffing states (shelltype + major version need to match)
* better error handling, check shellstate compatibility during run (on waveshell server)
* add extra separator for bash shellstate processing to deal with spurious output from rc files
* special migration for v30 -- flag invalid bash shell states and show special button in UI to fix
* format
* remove zsh-decls (unused)
* remove test code
* remove debug print
* fix typo
2024-01-17 01:11:04 +01:00
|
|
|
"github.com/wavetermdev/waveterm/waveshell/pkg/utilfn"
|
2022-09-01 21:47:10 +02:00
|
|
|
)
|
2022-07-13 23:16:08 +02:00
|
|
|
|
|
|
|
var MainBus *UpdateBus = MakeUpdateBus()
|
|
|
|
|
2022-07-16 02:37:32 +02:00
|
|
|
const PtyDataUpdateStr = "pty"
|
2022-08-24 11:14:16 +02:00
|
|
|
const ModelUpdateStr = "model"
|
2022-09-15 09:17:23 +02:00
|
|
|
const UpdateChSize = 100
|
2022-07-16 02:37:32 +02:00
|
|
|
|
|
|
|
type UpdatePacket interface {
|
|
|
|
UpdateType() string
|
2023-05-09 01:06:51 +02:00
|
|
|
Clean()
|
2022-07-16 02:37:32 +02:00
|
|
|
}
|
|
|
|
|
2022-07-13 23:16:08 +02:00
|
|
|
type PtyDataUpdate struct {
|
2023-03-16 02:12:55 +01:00
|
|
|
ScreenId string `json:"screenid,omitempty"`
|
2023-07-31 02:16:43 +02:00
|
|
|
LineId string `json:"lineid,omitempty"`
|
2022-09-15 08:10:35 +02:00
|
|
|
RemoteId string `json:"remoteid,omitempty"`
|
2022-07-13 23:16:08 +02:00
|
|
|
PtyPos int64 `json:"ptypos"`
|
|
|
|
PtyData64 string `json:"ptydata64"`
|
|
|
|
PtyDataLen int64 `json:"ptydatalen"`
|
|
|
|
}
|
|
|
|
|
2023-05-09 01:06:51 +02:00
|
|
|
func (*PtyDataUpdate) UpdateType() string {
|
2022-07-16 02:37:32 +02:00
|
|
|
return PtyDataUpdateStr
|
|
|
|
}
|
|
|
|
|
2023-05-09 01:06:51 +02:00
|
|
|
func (pdu *PtyDataUpdate) Clean() {}
|
|
|
|
|
2022-08-24 11:14:16 +02:00
|
|
|
type ModelUpdate struct {
|
2024-01-28 22:47:36 +01:00
|
|
|
Sessions []*SessionType `json:"sessions,omitempty"`
|
|
|
|
ActiveSessionId string `json:"activesessionid,omitempty"`
|
|
|
|
Screens []*ScreenType `json:"screens,omitempty"`
|
|
|
|
ScreenLines *ScreenLinesType `json:"screenlines,omitempty"`
|
|
|
|
Line *LineType `json:"line,omitempty"`
|
|
|
|
Lines []*LineType `json:"lines,omitempty"`
|
|
|
|
Cmd *CmdType `json:"cmd,omitempty"`
|
|
|
|
CmdLine *utilfn.StrWithPos `json:"cmdline,omitempty"`
|
|
|
|
Info *InfoMsgType `json:"info,omitempty"`
|
|
|
|
ClearInfo bool `json:"clearinfo,omitempty"`
|
|
|
|
Remotes []RemoteRuntimeState `json:"remotes,omitempty"`
|
|
|
|
History *HistoryInfoType `json:"history,omitempty"`
|
|
|
|
Interactive bool `json:"interactive"`
|
|
|
|
Connect bool `json:"connect,omitempty"`
|
|
|
|
MainView string `json:"mainview,omitempty"`
|
|
|
|
Bookmarks []*BookmarkType `json:"bookmarks,omitempty"`
|
|
|
|
SelectedBookmark string `json:"selectedbookmark,omitempty"`
|
|
|
|
HistoryViewData *HistoryViewData `json:"historyviewdata,omitempty"`
|
|
|
|
ClientData *ClientData `json:"clientdata,omitempty"`
|
|
|
|
RemoteView *RemoteViewType `json:"remoteview,omitempty"`
|
|
|
|
ScreenTombstones []*ScreenTombstoneType `json:"screentombstones,omitempty"`
|
|
|
|
SessionTombstones []*SessionTombstoneType `json:"sessiontombstones,omitempty"`
|
|
|
|
OpenAICmdInfoChat []*packet.OpenAICmdInfoChatMessage `json:"openaicmdinfochat,omitempty"`
|
|
|
|
AlertMessage *AlertMessageType `json:"alertmessage,omitempty"`
|
|
|
|
ScreenStatusIndicator *ScreenStatusIndicatorType `json:"screenstatusindicator,omitempty"`
|
|
|
|
ScreenNumRunningCommands *ScreenNumRunningCommandsType `json:"screennumrunningcommands,omitempty"`
|
2022-07-15 10:57:45 +02:00
|
|
|
}
|
|
|
|
|
2023-05-09 01:06:51 +02:00
|
|
|
func (*ModelUpdate) UpdateType() string {
|
2022-08-24 11:14:16 +02:00
|
|
|
return ModelUpdateStr
|
2022-07-16 02:37:32 +02:00
|
|
|
}
|
|
|
|
|
2023-05-09 01:06:51 +02:00
|
|
|
func (update *ModelUpdate) Clean() {
|
|
|
|
if update == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
update.ClientData = update.ClientData.Clean()
|
|
|
|
}
|
|
|
|
|
Implement a Sidebar for Tabs (#157)
* work on basic sidebar layout
* fix more golang warnings
* sidebar open/close
* add ability to set width of split
* sidebar add and remove, set width, etc.
* almost working sidebar implementation -- still needs height/width, input control, and bug with initial add, but getting there
* add isSidebarOpen() method
* fix resize jump -- must set width in error handler as well (before window is loaded)
* sidebar UI touchups and help
* more sidebar progress, render more like regular lines, just in the right column
* merge
* move migration to 26
* simplify sidebar types
* checkpoint
* proxy things through parent screen object for sidebar
* checkpoint, add/remove from sidebar
* work on add/remove icons for sidebar
* fix height calculation, remove close button
* bring back close button when no line is selected
* add sidebar flag to run command to run new command output in sidebar
* implement 'sidebar' kwarg in eval. this lets sidebar work for slashcommands as well that produce lines (codeedit, mdview, etc.)
* prettier
* minor fixes
* working on resizing. must exclude sidebar entries and send separate resize events based on size of sidebar (implement exclude / include for resize)
* fix sidebar terminal command resizing
* add sidebar header (toggles for half/partial width and close). add hotkey to open/close sidebar (Cmd-Ctrl-S). more robust calculation for sidebar width. add width validation. minimum sidebar width is 200px. other fixes, etc.
2023-12-18 08:46:53 +01:00
|
|
|
func (update *ModelUpdate) UpdateScreen(newScreen *ScreenType) {
|
|
|
|
if newScreen == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
for idx, screen := range update.Screens {
|
|
|
|
if screen.ScreenId == newScreen.ScreenId {
|
|
|
|
update.Screens[idx] = newScreen
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
update.Screens = append(update.Screens, newScreen)
|
|
|
|
}
|
|
|
|
|
|
|
|
// only sets InfoError if InfoError is not already set
|
|
|
|
func (update *ModelUpdate) AddInfoError(errStr string) {
|
|
|
|
if update.Info == nil {
|
|
|
|
update.Info = &InfoMsgType{}
|
|
|
|
}
|
|
|
|
if update.Info.InfoError == "" {
|
|
|
|
update.Info.InfoError = errStr
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-04 00:55:36 +02:00
|
|
|
type RemoteViewType struct {
|
|
|
|
RemoteShowAll bool `json:"remoteshowall,omitempty"`
|
|
|
|
PtyRemoteId string `json:"ptyremoteid,omitempty"`
|
|
|
|
RemoteEdit *RemoteEditType `json:"remoteedit,omitempty"`
|
|
|
|
}
|
|
|
|
|
2022-09-01 21:47:10 +02:00
|
|
|
func InfoMsgUpdate(infoMsgFmt string, args ...interface{}) *ModelUpdate {
|
|
|
|
msg := fmt.Sprintf(infoMsgFmt, args...)
|
|
|
|
return &ModelUpdate{
|
|
|
|
Info: &InfoMsgType{InfoMsg: msg},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-02 09:31:19 +01:00
|
|
|
type HistoryViewData struct {
|
2023-03-06 20:47:44 +01:00
|
|
|
Items []*HistoryItemType `json:"items"`
|
|
|
|
Offset int `json:"offset"`
|
2023-03-06 22:54:38 +01:00
|
|
|
RawOffset int `json:"rawoffset"`
|
|
|
|
NextRawOffset int `json:"nextrawoffset"`
|
2023-03-06 20:47:44 +01:00
|
|
|
HasMore bool `json:"hasmore"`
|
|
|
|
Lines []*LineType `json:"lines"`
|
|
|
|
Cmds []*CmdType `json:"cmds"`
|
2023-03-02 09:31:19 +01:00
|
|
|
}
|
|
|
|
|
2022-09-30 23:46:51 +02:00
|
|
|
type RemoteEditType struct {
|
2022-10-04 04:04:48 +02:00
|
|
|
RemoteEdit bool `json:"remoteedit"`
|
|
|
|
RemoteId string `json:"remoteid,omitempty"`
|
|
|
|
ErrorStr string `json:"errorstr,omitempty"`
|
|
|
|
InfoStr string `json:"infostr,omitempty"`
|
|
|
|
KeyStr string `json:"keystr,omitempty"`
|
|
|
|
HasPassword bool `json:"haspassword,omitempty"`
|
2022-09-30 23:46:51 +02:00
|
|
|
}
|
|
|
|
|
2024-01-10 01:13:23 +01:00
|
|
|
type AlertMessageType struct {
|
|
|
|
Title string `json:"title,omitempty"`
|
|
|
|
Message string `json:"message"`
|
|
|
|
Confirm bool `json:"confirm,omitempty"`
|
|
|
|
Markdown bool `json:"markdown,omitempty"`
|
|
|
|
}
|
|
|
|
|
2022-08-11 03:33:32 +02:00
|
|
|
type InfoMsgType struct {
|
2023-04-04 00:55:36 +02:00
|
|
|
InfoTitle string `json:"infotitle"`
|
|
|
|
InfoError string `json:"infoerror,omitempty"`
|
|
|
|
InfoMsg string `json:"infomsg,omitempty"`
|
|
|
|
InfoMsgHtml bool `json:"infomsghtml,omitempty"`
|
|
|
|
WebShareLink bool `json:"websharelink,omitempty"`
|
|
|
|
InfoComps []string `json:"infocomps,omitempty"`
|
|
|
|
InfoCompsMore bool `json:"infocompssmore,omitempty"`
|
|
|
|
InfoLines []string `json:"infolines,omitempty"`
|
|
|
|
TimeoutMs int64 `json:"timeoutms,omitempty"`
|
2022-08-11 03:33:32 +02:00
|
|
|
}
|
|
|
|
|
2022-08-30 01:31:06 +02:00
|
|
|
type HistoryInfoType struct {
|
2022-09-01 08:12:26 +02:00
|
|
|
HistoryType string `json:"historytype"`
|
|
|
|
SessionId string `json:"sessionid,omitempty"`
|
2023-03-15 00:37:22 +01:00
|
|
|
ScreenId string `json:"screenid,omitempty"`
|
2022-09-01 08:12:26 +02:00
|
|
|
Items []*HistoryItemType `json:"items"`
|
|
|
|
Show bool `json:"show"`
|
2022-08-30 01:31:06 +02:00
|
|
|
}
|
|
|
|
|
2022-07-13 23:16:08 +02:00
|
|
|
type UpdateChannel struct {
|
2023-03-21 03:20:57 +01:00
|
|
|
ScreenId string
|
|
|
|
ClientId string
|
|
|
|
Ch chan interface{}
|
2022-07-13 23:16:08 +02:00
|
|
|
}
|
|
|
|
|
2023-03-21 03:20:57 +01:00
|
|
|
func (uch UpdateChannel) Match(screenId string) bool {
|
|
|
|
if screenId == "" {
|
2022-07-13 23:16:08 +02:00
|
|
|
return true
|
|
|
|
}
|
2023-03-21 03:20:57 +01:00
|
|
|
return screenId == uch.ScreenId
|
2022-07-13 23:16:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
type UpdateBus struct {
|
|
|
|
Lock *sync.Mutex
|
|
|
|
Channels map[string]UpdateChannel
|
|
|
|
}
|
|
|
|
|
|
|
|
func MakeUpdateBus() *UpdateBus {
|
|
|
|
return &UpdateBus{
|
|
|
|
Lock: &sync.Mutex{},
|
|
|
|
Channels: make(map[string]UpdateChannel),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-05 21:42:09 +02:00
|
|
|
// always returns a new channel
|
2023-03-21 03:20:57 +01:00
|
|
|
func (bus *UpdateBus) RegisterChannel(clientId string, screenId string) chan interface{} {
|
2022-07-13 23:16:08 +02:00
|
|
|
bus.Lock.Lock()
|
|
|
|
defer bus.Lock.Unlock()
|
|
|
|
uch, found := bus.Channels[clientId]
|
|
|
|
if found {
|
|
|
|
close(uch.Ch)
|
2023-03-21 03:20:57 +01:00
|
|
|
uch.ScreenId = screenId
|
2022-09-15 09:17:23 +02:00
|
|
|
uch.Ch = make(chan interface{}, UpdateChSize)
|
2022-07-13 23:16:08 +02:00
|
|
|
} else {
|
|
|
|
uch = UpdateChannel{
|
2023-03-21 03:20:57 +01:00
|
|
|
ClientId: clientId,
|
|
|
|
ScreenId: screenId,
|
|
|
|
Ch: make(chan interface{}, UpdateChSize),
|
2022-07-13 23:16:08 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
bus.Channels[clientId] = uch
|
|
|
|
return uch.Ch
|
|
|
|
}
|
|
|
|
|
|
|
|
func (bus *UpdateBus) UnregisterChannel(clientId string) {
|
|
|
|
bus.Lock.Lock()
|
|
|
|
defer bus.Lock.Unlock()
|
|
|
|
uch, found := bus.Channels[clientId]
|
|
|
|
if found {
|
|
|
|
close(uch.Ch)
|
|
|
|
delete(bus.Channels, clientId)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-09 01:06:51 +02:00
|
|
|
func (bus *UpdateBus) SendUpdate(update UpdatePacket) {
|
|
|
|
if update == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
update.Clean()
|
2023-03-21 03:20:57 +01:00
|
|
|
bus.Lock.Lock()
|
|
|
|
defer bus.Lock.Unlock()
|
|
|
|
for _, uch := range bus.Channels {
|
|
|
|
select {
|
|
|
|
case uch.Ch <- update:
|
|
|
|
|
|
|
|
default:
|
|
|
|
log.Printf("[error] dropped update on updatebus uch clientid=%s\n", uch.ClientId)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-09 01:06:51 +02:00
|
|
|
func (bus *UpdateBus) SendScreenUpdate(screenId string, update UpdatePacket) {
|
|
|
|
if update == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
update.Clean()
|
2022-07-13 23:16:08 +02:00
|
|
|
bus.Lock.Lock()
|
|
|
|
defer bus.Lock.Unlock()
|
|
|
|
for _, uch := range bus.Channels {
|
2023-03-21 03:20:57 +01:00
|
|
|
if uch.Match(screenId) {
|
2022-09-15 09:17:23 +02:00
|
|
|
select {
|
|
|
|
case uch.Ch <- update:
|
|
|
|
|
|
|
|
default:
|
2022-10-31 20:40:45 +01:00
|
|
|
log.Printf("[error] dropped update on updatebus uch clientid=%s\n", uch.ClientId)
|
2022-09-15 09:17:23 +02:00
|
|
|
}
|
2022-07-13 23:16:08 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-08-24 11:14:16 +02:00
|
|
|
|
|
|
|
func MakeSessionsUpdateForRemote(sessionId string, ri *RemoteInstance) []*SessionType {
|
|
|
|
return []*SessionType{
|
Implement a Sidebar for Tabs (#157)
* work on basic sidebar layout
* fix more golang warnings
* sidebar open/close
* add ability to set width of split
* sidebar add and remove, set width, etc.
* almost working sidebar implementation -- still needs height/width, input control, and bug with initial add, but getting there
* add isSidebarOpen() method
* fix resize jump -- must set width in error handler as well (before window is loaded)
* sidebar UI touchups and help
* more sidebar progress, render more like regular lines, just in the right column
* merge
* move migration to 26
* simplify sidebar types
* checkpoint
* proxy things through parent screen object for sidebar
* checkpoint, add/remove from sidebar
* work on add/remove icons for sidebar
* fix height calculation, remove close button
* bring back close button when no line is selected
* add sidebar flag to run command to run new command output in sidebar
* implement 'sidebar' kwarg in eval. this lets sidebar work for slashcommands as well that produce lines (codeedit, mdview, etc.)
* prettier
* minor fixes
* working on resizing. must exclude sidebar entries and send separate resize events based on size of sidebar (implement exclude / include for resize)
* fix sidebar terminal command resizing
* add sidebar header (toggles for half/partial width and close). add hotkey to open/close sidebar (Cmd-Ctrl-S). more robust calculation for sidebar width. add width validation. minimum sidebar width is 200px. other fixes, etc.
2023-12-18 08:46:53 +01:00
|
|
|
{
|
2022-08-24 11:14:16 +02:00
|
|
|
SessionId: sessionId,
|
|
|
|
Remotes: []*RemoteInstance{ri},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
2023-02-21 07:08:23 +01:00
|
|
|
|
|
|
|
type BookmarksViewType struct {
|
|
|
|
Bookmarks []*BookmarkType `json:"bookmarks"`
|
|
|
|
}
|
2024-01-17 19:07:01 +01:00
|
|
|
|
|
|
|
type ScreenStatusIndicatorType struct {
|
|
|
|
ScreenId string `json:"screenid"`
|
|
|
|
Status StatusIndicatorLevel `json:"status"`
|
|
|
|
}
|
2024-01-28 22:47:36 +01:00
|
|
|
|
|
|
|
type ScreenNumRunningCommandsType struct {
|
|
|
|
ScreenId string `json:"screenid"`
|
|
|
|
Num int `json:"num"`
|
|
|
|
}
|