mirror of
https://github.com/wavetermdev/waveterm.git
synced 2024-12-21 16:38:23 +01:00
Retry Without Wsh on Fail (#1406)
Adds the ability for connections to continue without wsh if they fail. This involves creating a menu that warns the user that wsh could not be used.
This commit is contained in:
parent
9f10be5629
commit
6dfc85b324
@ -16,11 +16,18 @@ The easiest way to access connections is to click the <i className="fa-sharp fa-
|
||||
|
||||
## What are wsh Shell Extensions?
|
||||
|
||||
`wsh` is a small program that helps manage waveterm regardless of which machine you are currently connected to. In order to not interrupt the normal flow of the remote session, we install it on your remote machine at `~/.waveterm/bin/wsh`. Then, when wave connects to your connection (and only when wave connects to your connection), `~/.waveterm/bin` is added to your `PATH` for that individual session. For more info on what `wsh` is capable of, see [wsh command](/wsh). And if you wish to view the source code of `wsh`, you can find it [here](https://github.com/wavetermdev/waveterm/tree/main/cmd/wsh).
|
||||
`wsh` is a small program that helps manage waveterm regardless of which machine you are currently connected to. It is always included on your host machine, but you also have the option to install it when connecting to a remote machine. If it is installed on the remote machine, it is installed at `~/.waveterm/bin/wsh`. Then, when wave connects to your connection (and only when wave connects to your connection), `~/.waveterm/bin` is added to your `PATH` for that individual session. For more info on what `wsh` is capable of, see [wsh command](/wsh). And if you wish to view the source code of `wsh`, you can find it [here](https://github.com/wavetermdev/waveterm/tree/main/cmd/wsh).
|
||||
|
||||
With `wsh` installed, you have the ability to view certain widgets from the remote machine as if it were your host. In addition, `wsh` can be used to influence the widgets across various machines. As a very simple example, you can close a widget on the host machine by using the `wsh` command in a terminal window on a remote machine. For more information on what you can accomplish with `wsh`, take a look [here](/wsh).
|
||||
|
||||
## Add a New Connection to the Dropdown
|
||||
|
||||
The SSH values that are loaded into the dropdown by default are obtained by parsing your `~/.ssh/config` and `/etc/ssh/ssh_config` files. Adding a new connection is as simple as adding a new `Host` to one of these files, typically the `~/.ssh/config` file.
|
||||
The SSH values that are loaded into the dropdown by default are obtained by parsing the internal `config/connections.json` file in addition to your `~/.ssh/config` and `/etc/ssh/ssh_config` files. Adding a new connection can be added in a couple ways:
|
||||
|
||||
- adding a new `Host` to one of your ssh config files, typically the `~/.ssh/config` file
|
||||
- adding a new entry in the internal `config/connections.json` file
|
||||
- manually typing your connection into the connection box (if this successfully connects, the connection will be added to the internal `config/connections.json` file)
|
||||
- use `wsh ssh [user]@[host]` in your terminal (if this successfully connects, the connection will be added to the internal `config/connections.json` file)
|
||||
|
||||
WSL values are added by searching the installed WSL distributions as they appear in the Windows Registry.
|
||||
|
||||
@ -55,6 +62,20 @@ Host myhost
|
||||
|
||||
You would then be able to access this connection with `myhost` or `username@myhost`. And if you wanted to manually specify a port such as port 2222, you could do that by either adding `Port 2222` to the config file or connecting to `username@myhost:2222`.
|
||||
|
||||
## Internal SSH Configuration
|
||||
|
||||
In addition to the regular ssh config file, wave also has its own config file to manage separate variables. These include
|
||||
| Keyword | Description |
|
||||
|---------|-------------|
|
||||
| conn:wshenabled | This boolean allows wsh to be used for your connection, if it is set to `false`, `wsh` will never be used for that connection. It defaults to `true`.|
|
||||
| conn:askbeforewshinstall | This boolean is used to prompt the user before installing wsh. If it is set to false, `wsh` will automatically be installed instead without prompting. It defaults to `true`.|
|
||||
| display:hidden | This boolean hides the connection from the dropdown list. It defaults to `false` |
|
||||
| display:order | This float determines the order of connections in the connection dropdown. It defaults to `0`.|
|
||||
| term:fontsize | This int can be used to override the terminal font size for blocks using this connection. The block metadata takes priority over this setting. It defaults to null which means the global setting will be used instead. |
|
||||
| term:fontfamily | This string can be used to specify a terminal font family for blocks using this connection. The block metadata takes priority over this setting. It defaults to null which means the global setting will be used instead. |
|
||||
| term:theme | This string can be used to specify a terminal theme for blocks using this connection. The block metadata takes priority over this setting. It defaults to null which means the global setting will be used instead. |
|
||||
| ssh:identityfile | A list of strings containing the paths to identity files that will be used. If a `wsh ssh` command using the `-i` flag is successful, the identity file will automatically be added here. |
|
||||
|
||||
## Managing Connections with the CLI
|
||||
|
||||
The `wsh` command gives some commands specifically for interacting with the connections. You can view these [here](/wsh#conn).
|
||||
|
@ -298,7 +298,7 @@ This will delete the block with the specified id.
|
||||
wsh ssh [user@host]
|
||||
```
|
||||
|
||||
This will use Wave's internal ssh implementation to connect to the specified remote machine.
|
||||
This will use Wave's internal ssh implementation to connect to the specified remote machine. The `-i` flag can be used to specify a path to an identity file.
|
||||
|
||||
---
|
||||
|
||||
|
@ -166,6 +166,7 @@
|
||||
flex: 1 2 auto;
|
||||
overflow: hidden;
|
||||
padding-right: 4px;
|
||||
@include mixins.ellipsis()
|
||||
}
|
||||
|
||||
.connecting-svg {
|
||||
|
@ -185,8 +185,11 @@ const BlockFrame_Header = ({
|
||||
const manageConnection = util.useAtomValueSafe(viewModel?.manageConnection);
|
||||
const dragHandleRef = preview ? null : nodeModel.dragHandleRef;
|
||||
const connName = blockData?.meta?.connection;
|
||||
const allSettings = jotai.useAtomValue(atoms.fullConfigAtom);
|
||||
const wshEnabled = allSettings?.connections?.[connName]?.["conn:wshenabled"] ?? true;
|
||||
const connStatus = util.useAtomValueSafe(getConnStatusAtom(connName));
|
||||
const wshEnabled =
|
||||
(connName &&
|
||||
(connStatus?.status == "connecting" || (connStatus?.wshenabled && connStatus?.status == "connected"))) ??
|
||||
true;
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!magnified || preview || prevMagifiedState.current) {
|
||||
@ -342,6 +345,8 @@ const ConnStatusOverlay = React.memo(
|
||||
const [overlayRefCallback, _, domRect] = useDimensionsWithCallbackRef(30);
|
||||
const width = domRect?.width;
|
||||
const [showError, setShowError] = React.useState(false);
|
||||
const fullConfig = jotai.useAtomValue(atoms.fullConfigAtom);
|
||||
const [showWshError, setShowWshError] = React.useState(false);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (width) {
|
||||
@ -356,12 +361,40 @@ const ConnStatusOverlay = React.memo(
|
||||
prtn.catch((e) => console.log("error reconnecting", connName, e));
|
||||
}, [connName]);
|
||||
|
||||
const handleDisableWsh = React.useCallback(async () => {
|
||||
// using unknown is a hack. we need proper types for the
|
||||
// connection config on the frontend
|
||||
const metamaptype: unknown = {
|
||||
"conn:wshenabled": false,
|
||||
};
|
||||
const data: ConnConfigRequest = {
|
||||
host: connName,
|
||||
metamaptype: metamaptype,
|
||||
};
|
||||
try {
|
||||
await RpcApi.SetConnectionsConfigCommand(TabRpcClient, data);
|
||||
} catch (e) {
|
||||
console.log("problem setting connection config: ", e);
|
||||
}
|
||||
}, [connName]);
|
||||
|
||||
const handleRemoveWshError = React.useCallback(async () => {
|
||||
try {
|
||||
await RpcApi.DismissWshFailCommand(TabRpcClient, connName);
|
||||
} catch (e) {
|
||||
console.log("unable to dismiss wsh error: ", e);
|
||||
}
|
||||
}, [connName]);
|
||||
|
||||
let statusText = `Disconnected from "${connName}"`;
|
||||
let showReconnect = true;
|
||||
if (connStatus.status == "connecting") {
|
||||
statusText = `Connecting to "${connName}"...`;
|
||||
showReconnect = false;
|
||||
}
|
||||
if (connStatus.status == "connected") {
|
||||
showReconnect = false;
|
||||
}
|
||||
let reconDisplay = null;
|
||||
let reconClassName = "outlined grey";
|
||||
if (width && width < 350) {
|
||||
@ -373,18 +406,37 @@ const ConnStatusOverlay = React.memo(
|
||||
}
|
||||
const showIcon = connStatus.status != "connecting";
|
||||
|
||||
if (isLayoutMode || connStatus.status == "connected" || connModalOpen) {
|
||||
const wshConfigEnabled = fullConfig?.connections?.[connName]?.["conn:wshenabled"] ?? true;
|
||||
React.useEffect(() => {
|
||||
const showWshErrorTemp =
|
||||
connStatus.status == "connected" &&
|
||||
connStatus.wsherror &&
|
||||
connStatus.wsherror != "" &&
|
||||
wshConfigEnabled;
|
||||
|
||||
setShowWshError(showWshErrorTemp);
|
||||
}, [connStatus, wshConfigEnabled]);
|
||||
|
||||
if (!showWshError && (isLayoutMode || connStatus.status == "connected" || connModalOpen)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="connstatus-overlay" ref={overlayRefCallback}>
|
||||
<div className="connstatus-content">
|
||||
<div className={clsx("connstatus-status-icon-wrapper", { "has-error": showError })}>
|
||||
<div className={clsx("connstatus-status-icon-wrapper", { "has-error": showError || showWshError })}>
|
||||
{showIcon && <i className="fa-solid fa-triangle-exclamation"></i>}
|
||||
<div className="connstatus-status">
|
||||
<div className="connstatus-status-text">{statusText}</div>
|
||||
{showError ? <div className="connstatus-error">error: {connStatus.error}</div> : null}
|
||||
{showWshError ? (
|
||||
<div className="connstatus-error">unable to use wsh: {connStatus.wsherror}</div>
|
||||
) : null}
|
||||
{showWshError && (
|
||||
<Button className={reconClassName} onClick={handleDisableWsh}>
|
||||
always disable wsh
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{showReconnect ? (
|
||||
@ -394,6 +446,11 @@ const ConnStatusOverlay = React.memo(
|
||||
</Button>
|
||||
</div>
|
||||
) : null}
|
||||
{showWshError ? (
|
||||
<div className="connstatus-actions">
|
||||
<Button className={`fa-xmark fa-solid ${reconClassName}`} onClick={handleRemoveWshError} />
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -92,6 +92,11 @@ class RpcApiType {
|
||||
return client.wshRpcCall("deletesubblock", data, opts);
|
||||
}
|
||||
|
||||
// command "dismisswshfail" [call]
|
||||
DismissWshFailCommand(client: WshClient, data: string, opts?: RpcOpts): Promise<void> {
|
||||
return client.wshRpcCall("dismisswshfail", data, opts);
|
||||
}
|
||||
|
||||
// command "dispose" [call]
|
||||
DisposeCommand(client: WshClient, data: CommandDisposeData, opts?: RpcOpts): Promise<void> {
|
||||
return client.wshRpcCall("dispose", data, opts);
|
||||
@ -262,6 +267,11 @@ class RpcApiType {
|
||||
return client.wshRpcCall("setconfig", data, opts);
|
||||
}
|
||||
|
||||
// command "setconnectionsconfig" [call]
|
||||
SetConnectionsConfigCommand(client: WshClient, data: ConnConfigRequest, opts?: RpcOpts): Promise<void> {
|
||||
return client.wshRpcCall("setconnectionsconfig", data, opts);
|
||||
}
|
||||
|
||||
// command "setmeta" [call]
|
||||
SetMetaCommand(client: WshClient, data: CommandSetMetaData, opts?: RpcOpts): Promise<void> {
|
||||
return client.wshRpcCall("setmeta", data, opts);
|
||||
|
7
frontend/types/gotypes.d.ts
vendored
7
frontend/types/gotypes.d.ts
vendored
@ -278,6 +278,12 @@ declare global {
|
||||
err: string;
|
||||
};
|
||||
|
||||
// wshrpc.ConnConfigRequest
|
||||
type ConnConfigRequest = {
|
||||
host: string;
|
||||
metamaptype: MetaType;
|
||||
};
|
||||
|
||||
// wshrpc.ConnKeywords
|
||||
type ConnKeywords = {
|
||||
"conn:wshenabled"?: boolean;
|
||||
@ -319,6 +325,7 @@ declare global {
|
||||
hasconnected: boolean;
|
||||
activeconnnum: number;
|
||||
error?: string;
|
||||
wsherror?: string;
|
||||
};
|
||||
|
||||
// wshrpc.CpuDataRequest
|
||||
|
@ -354,7 +354,26 @@ func (bc *BlockController) DoRunShellCommand(rc *RunShellOpts, blockMeta waveobj
|
||||
}
|
||||
cmdOpts.Env[wshutil.WaveJwtTokenVarName] = jwtStr
|
||||
}
|
||||
if !conn.WshEnabled.Load() {
|
||||
shellProc, err = shellexec.StartRemoteShellProcNoWsh(rc.TermSize, cmdStr, cmdOpts, conn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
shellProc, err = shellexec.StartRemoteShellProc(rc.TermSize, cmdStr, cmdOpts, conn)
|
||||
if err != nil {
|
||||
conn.WithLock(func() {
|
||||
conn.WshError = err.Error()
|
||||
})
|
||||
conn.WshEnabled.Store(false)
|
||||
log.Printf("error starting remote shell proc with wsh: %v", err)
|
||||
log.Print("attempting install without wsh")
|
||||
shellProc, err = shellexec.StartRemoteShellProcNoWsh(rc.TermSize, cmdStr, cmdOpts, conn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -59,6 +59,7 @@ type SSHConn struct {
|
||||
DomainSockListener net.Listener
|
||||
ConnController *ssh.Session
|
||||
Error string
|
||||
WshError string
|
||||
HasWaiter *atomic.Bool
|
||||
LastConnectTime int64
|
||||
ActiveConnNum int
|
||||
@ -94,10 +95,12 @@ func (conn *SSHConn) DeriveConnStatus() wshrpc.ConnStatus {
|
||||
return wshrpc.ConnStatus{
|
||||
Status: conn.Status,
|
||||
Connected: conn.Status == Status_Connected,
|
||||
WshEnabled: conn.WshEnabled.Load(),
|
||||
Connection: conn.Opts.String(),
|
||||
HasConnected: (conn.LastConnectTime > 0),
|
||||
ActiveConnNum: conn.ActiveConnNum,
|
||||
Error: conn.Error,
|
||||
WshError: conn.WshError,
|
||||
}
|
||||
}
|
||||
|
||||
@ -532,7 +535,11 @@ func (conn *SSHConn) connectInternal(ctx context.Context, connFlags *wshrpc.Conn
|
||||
})
|
||||
} else if installErr != nil {
|
||||
log.Printf("error: unable to install wsh shell extensions for %s: %v\n", conn.GetName(), err)
|
||||
return fmt.Errorf("conncontroller %s wsh install error: %v", conn.GetName(), installErr)
|
||||
log.Print("attempting to run with nowsh instead")
|
||||
conn.WithLock(func() {
|
||||
conn.WshError = installErr.Error()
|
||||
})
|
||||
conn.WshEnabled.Store(false)
|
||||
} else {
|
||||
conn.WshEnabled.Store(true)
|
||||
}
|
||||
@ -541,7 +548,12 @@ func (conn *SSHConn) connectInternal(ctx context.Context, connFlags *wshrpc.Conn
|
||||
csErr := conn.StartConnServer()
|
||||
if csErr != nil {
|
||||
log.Printf("error: unable to start conn server for %s: %v\n", conn.GetName(), csErr)
|
||||
return fmt.Errorf("conncontroller %s start wsh connserver error: %v", conn.GetName(), csErr)
|
||||
log.Print("attempting to run with nowsh instead")
|
||||
conn.WithLock(func() {
|
||||
conn.WshError = csErr.Error()
|
||||
})
|
||||
conn.WshEnabled.Store(false)
|
||||
//return fmt.Errorf("conncontroller %s start wsh connserver error: %v", conn.GetName(), csErr)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -236,10 +236,8 @@ func StartWslShellProc(ctx context.Context, termSize waveobj.TermSize, cmdStr st
|
||||
return &ShellProc{Cmd: cmdWrap, ConnName: conn.GetName(), CloseOnce: &sync.Once{}, DoneCh: make(chan any)}, nil
|
||||
}
|
||||
|
||||
func StartRemoteShellProc(termSize waveobj.TermSize, cmdStr string, cmdOpts CommandOptsType, conn *conncontroller.SSHConn) (*ShellProc, error) {
|
||||
func StartRemoteShellProcNoWsh(termSize waveobj.TermSize, cmdStr string, cmdOpts CommandOptsType, conn *conncontroller.SSHConn) (*ShellProc, error) {
|
||||
client := conn.GetClient()
|
||||
if !conn.WshEnabled.Load() {
|
||||
// no wsh code
|
||||
session, err := client.NewSession()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -278,7 +276,10 @@ func StartRemoteShellProc(termSize waveobj.TermSize, cmdStr string, cmdOpts Comm
|
||||
return nil, err
|
||||
}
|
||||
return &ShellProc{Cmd: sessionWrap, ConnName: conn.GetName(), CloseOnce: &sync.Once{}, DoneCh: make(chan any)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func StartRemoteShellProc(termSize waveobj.TermSize, cmdStr string, cmdOpts CommandOptsType, conn *conncontroller.SSHConn) (*ShellProc, error) {
|
||||
client := conn.GetClient()
|
||||
shellPath := cmdOpts.ShellPath
|
||||
if shellPath == "" {
|
||||
remoteShellPath, err := remote.DetectShell(client)
|
||||
|
@ -115,6 +115,12 @@ func DeleteSubBlockCommand(w *wshutil.WshRpc, data wshrpc.CommandDeleteBlockData
|
||||
return err
|
||||
}
|
||||
|
||||
// command "dismisswshfail", wshserver.DismissWshFailCommand
|
||||
func DismissWshFailCommand(w *wshutil.WshRpc, data string, opts *wshrpc.RpcOpts) error {
|
||||
_, err := sendRpcRequestCallHelper[any](w, "dismisswshfail", data, opts)
|
||||
return err
|
||||
}
|
||||
|
||||
// command "dispose", wshserver.DisposeCommand
|
||||
func DisposeCommand(w *wshutil.WshRpc, data wshrpc.CommandDisposeData, opts *wshrpc.RpcOpts) error {
|
||||
_, err := sendRpcRequestCallHelper[any](w, "dispose", data, opts)
|
||||
@ -317,6 +323,12 @@ func SetConfigCommand(w *wshutil.WshRpc, data wshrpc.MetaSettingsType, opts *wsh
|
||||
return err
|
||||
}
|
||||
|
||||
// command "setconnectionsconfig", wshserver.SetConnectionsConfigCommand
|
||||
func SetConnectionsConfigCommand(w *wshutil.WshRpc, data wshrpc.ConnConfigRequest, opts *wshrpc.RpcOpts) error {
|
||||
_, err := sendRpcRequestCallHelper[any](w, "setconnectionsconfig", data, opts)
|
||||
return err
|
||||
}
|
||||
|
||||
// command "setmeta", wshserver.SetMetaCommand
|
||||
func SetMetaCommand(w *wshutil.WshRpc, data wshrpc.CommandSetMetaData, opts *wshrpc.RpcOpts) error {
|
||||
_, err := sendRpcRequestCallHelper[any](w, "setmeta", data, opts)
|
||||
|
@ -59,6 +59,8 @@ const (
|
||||
Command_StreamWaveAi = "streamwaveai"
|
||||
Command_StreamCpuData = "streamcpudata"
|
||||
Command_Test = "test"
|
||||
Command_SetConfig = "setconfig"
|
||||
Command_SetConnectionsConfig = "connectionsconfig"
|
||||
Command_RemoteStreamFile = "remotestreamfile"
|
||||
Command_RemoteFileInfo = "remotefileinfo"
|
||||
Command_RemoteFileTouch = "remotefiletouch"
|
||||
@ -81,6 +83,7 @@ const (
|
||||
Command_ConnList = "connlist"
|
||||
Command_WslList = "wsllist"
|
||||
Command_WslDefaultDistro = "wsldefaultdistro"
|
||||
Command_DismissWshFail = "dismisswshfail"
|
||||
|
||||
Command_WorkspaceList = "workspacelist"
|
||||
|
||||
@ -139,6 +142,7 @@ type WshRpcInterface interface {
|
||||
StreamCpuDataCommand(ctx context.Context, request CpuDataRequest) chan RespOrErrorUnion[TimeSeriesData]
|
||||
TestCommand(ctx context.Context, data string) error
|
||||
SetConfigCommand(ctx context.Context, data MetaSettingsType) error
|
||||
SetConnectionsConfigCommand(ctx context.Context, data ConnConfigRequest) error
|
||||
BlockInfoCommand(ctx context.Context, blockId string) (*BlockInfoData, error)
|
||||
WaveInfoCommand(ctx context.Context) (*WaveInfoData, error)
|
||||
WshActivityCommand(ct context.Context, data map[string]int) error
|
||||
@ -156,6 +160,7 @@ type WshRpcInterface interface {
|
||||
ConnListCommand(ctx context.Context) ([]string, error)
|
||||
WslListCommand(ctx context.Context) ([]string, error)
|
||||
WslDefaultDistroCommand(ctx context.Context) (string, error)
|
||||
DismissWshFailCommand(ctx context.Context, connName string) error
|
||||
|
||||
// eventrecv is special, it's handled internally by WshRpc with EventListener
|
||||
EventRecvCommand(ctx context.Context, data wps.WaveEvent) error
|
||||
@ -512,6 +517,11 @@ func (m MetaSettingsType) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(m.MetaMapType)
|
||||
}
|
||||
|
||||
type ConnConfigRequest struct {
|
||||
Host string `json:"host"`
|
||||
MetaMapType waveobj.MetaMapType `json:"metamaptype"`
|
||||
}
|
||||
|
||||
type ConnStatus struct {
|
||||
Status string `json:"status"`
|
||||
WshEnabled bool `json:"wshenabled"`
|
||||
@ -520,6 +530,7 @@ type ConnStatus struct {
|
||||
HasConnected bool `json:"hasconnected"` // true if it has *ever* connected successfully
|
||||
ActiveConnNum int `json:"activeconnnum"`
|
||||
Error string `json:"error,omitempty"`
|
||||
WshError string `json:"wsherror,omitempty"`
|
||||
}
|
||||
|
||||
type WebSelectorOpts struct {
|
||||
|
@ -575,6 +575,11 @@ func (ws *WshServer) SetConfigCommand(ctx context.Context, data wshrpc.MetaSetti
|
||||
return wconfig.SetBaseConfigValue(data.MetaMapType)
|
||||
}
|
||||
|
||||
func (ws *WshServer) SetConnectionsConfigCommand(ctx context.Context, data wshrpc.ConnConfigRequest) error {
|
||||
log.Printf("SET CONNECTIONS CONFIG: %v\n", data)
|
||||
return wconfig.SetConnectionsConfigValue(data.Host, data.MetaMapType)
|
||||
}
|
||||
|
||||
func (ws *WshServer) ConnStatusCommand(ctx context.Context) ([]wshrpc.ConnStatus, error) {
|
||||
rtn := conncontroller.GetAllConnStatus()
|
||||
return rtn, nil
|
||||
@ -685,6 +690,25 @@ func (ws *WshServer) WslDefaultDistroCommand(ctx context.Context) (string, error
|
||||
return distro.Name(), nil
|
||||
}
|
||||
|
||||
/**
|
||||
* Dismisses the WshFail Command in runtime memory on the backend
|
||||
*/
|
||||
func (ws *WshServer) DismissWshFailCommand(ctx context.Context, connName string) error {
|
||||
opts, err := remote.ParseOpts(connName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
conn := conncontroller.GetConn(ctx, opts, false, nil)
|
||||
if conn == nil {
|
||||
return fmt.Errorf("connection %s not found", connName)
|
||||
}
|
||||
conn.WithLock(func() {
|
||||
conn.WshError = ""
|
||||
})
|
||||
conn.FireConnChangeEvent()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ws *WshServer) BlockInfoCommand(ctx context.Context, blockId string) (*wshrpc.BlockInfoData, error) {
|
||||
blockData, err := wstore.DBMustGet[*waveobj.Block](ctx, blockId)
|
||||
if err != nil {
|
||||
|
Loading…
Reference in New Issue
Block a user