mirror of
https://github.com/wavetermdev/waveterm.git
synced 2025-01-02 18:39:05 +01:00
Connection Typeahead/Suggestions (#332)
Adds a list of potential remotes to add and filters it as you type. It also provides options for reconnecting on a disconnection and specifically connecting to a local connection
This commit is contained in:
parent
1706131a80
commit
fc0b1929ec
@ -448,6 +448,10 @@ const ChangeConnectionBlockModal = React.memo(
|
|||||||
const changeConnModalOpen = jotai.useAtomValue(changeConnModalAtom);
|
const changeConnModalOpen = jotai.useAtomValue(changeConnModalAtom);
|
||||||
const [blockData] = WOS.useWaveObjectValue<Block>(WOS.makeORef("block", blockId));
|
const [blockData] = WOS.useWaveObjectValue<Block>(WOS.makeORef("block", blockId));
|
||||||
const isNodeFocused = jotai.useAtomValue(nodeModel.isFocused);
|
const isNodeFocused = jotai.useAtomValue(nodeModel.isFocused);
|
||||||
|
const connection = blockData?.meta?.connection ?? "local";
|
||||||
|
const connStatusAtom = getConnStatusAtom(connection);
|
||||||
|
const connStatus = jotai.useAtomValue(connStatusAtom);
|
||||||
|
const [suggestions, setSuggestions] = React.useState([]);
|
||||||
const changeConnection = React.useCallback(
|
const changeConnection = React.useCallback(
|
||||||
async (connName: string) => {
|
async (connName: string) => {
|
||||||
if (connName == "") {
|
if (connName == "") {
|
||||||
@ -476,6 +480,109 @@ const ChangeConnectionBlockModal = React.memo(
|
|||||||
},
|
},
|
||||||
[blockId, blockData]
|
[blockId, blockData]
|
||||||
);
|
);
|
||||||
|
React.useEffect(() => {
|
||||||
|
const loadFromBackend = async () => {
|
||||||
|
let connList: Array<string>;
|
||||||
|
|
||||||
|
try {
|
||||||
|
connList = await WshServer.ConnListCommand({ timeout: 2000 });
|
||||||
|
} catch (e) {
|
||||||
|
console.log("unable to load conn list from backend. using blank list: ", e);
|
||||||
|
}
|
||||||
|
let createNew: boolean = true;
|
||||||
|
if (connSelected == "") {
|
||||||
|
createNew = false;
|
||||||
|
}
|
||||||
|
const filteredList: Array<string> = [];
|
||||||
|
for (const conn of connList) {
|
||||||
|
if (conn === connSelected) {
|
||||||
|
createNew = false;
|
||||||
|
}
|
||||||
|
if (conn.includes(connSelected)) {
|
||||||
|
filteredList.push(conn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// priority handles special suggestions when necessary
|
||||||
|
// for instance, when reconnecting
|
||||||
|
const newConnectionSuggestion: SuggestionConnectionItem = {
|
||||||
|
status: "connected",
|
||||||
|
icon: "arrow-right-arrow-left",
|
||||||
|
iconColor: "var(--conn-icon-color)",
|
||||||
|
label: `(+) ${connSelected}`,
|
||||||
|
value: "",
|
||||||
|
onSelect: (_: string) => {
|
||||||
|
changeConnection(connSelected);
|
||||||
|
globalStore.set(changeConnModalAtom, false);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const reconnectSuggestion: SuggestionConnectionItem = {
|
||||||
|
status: "connected",
|
||||||
|
icon: "arrow-right-arrow-left",
|
||||||
|
iconColor: "var(--conn-icon-color)",
|
||||||
|
label: `Reconnect to ${connStatus.connection}`,
|
||||||
|
value: "",
|
||||||
|
onSelect: async (_: string) => {
|
||||||
|
console.log("unimplemented: reconnect");
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const priorityItems: Array<SuggestionConnectionItem> = [];
|
||||||
|
if (createNew) {
|
||||||
|
console.log("added to priority items");
|
||||||
|
priorityItems.push(newConnectionSuggestion);
|
||||||
|
}
|
||||||
|
if (connStatus.status == "disconnected" || connStatus.status == "error") {
|
||||||
|
priorityItems.push(reconnectSuggestion);
|
||||||
|
}
|
||||||
|
const prioritySuggestions: SuggestionConnectionScope = {
|
||||||
|
headerText: "",
|
||||||
|
items: priorityItems,
|
||||||
|
};
|
||||||
|
const localSuggestion: SuggestionConnectionScope = {
|
||||||
|
headerText: "Local",
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
status: "connected",
|
||||||
|
icon: "laptop",
|
||||||
|
iconColor: "var(--grey-text-color)",
|
||||||
|
value: "",
|
||||||
|
label: "Switch to Local Connection",
|
||||||
|
// TODO: need to specify user name and host name
|
||||||
|
onSelect: (_: string) => {
|
||||||
|
changeConnection("");
|
||||||
|
globalStore.set(changeConnModalAtom, false);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
const remoteItems = filteredList.map((connName) => {
|
||||||
|
const item: SuggestionConnectionItem = {
|
||||||
|
status: "connected",
|
||||||
|
icon: "arrow-right-arrow-left",
|
||||||
|
iconColor: "var(--conn-icon-color)",
|
||||||
|
value: connName,
|
||||||
|
label: connName,
|
||||||
|
};
|
||||||
|
return item;
|
||||||
|
});
|
||||||
|
const remoteSuggestions: SuggestionConnectionScope = {
|
||||||
|
headerText: "Remote",
|
||||||
|
items: remoteItems,
|
||||||
|
};
|
||||||
|
|
||||||
|
let out: Array<SuggestionsType> = [];
|
||||||
|
if (prioritySuggestions.items.length > 0) {
|
||||||
|
out.push(prioritySuggestions);
|
||||||
|
}
|
||||||
|
if (localSuggestion.items.length > 0) {
|
||||||
|
out.push(localSuggestion);
|
||||||
|
}
|
||||||
|
if (remoteSuggestions.items.length > 0) {
|
||||||
|
out.push(remoteSuggestions);
|
||||||
|
}
|
||||||
|
setSuggestions(out);
|
||||||
|
};
|
||||||
|
loadFromBackend();
|
||||||
|
}, [connStatus, setSuggestions, connSelected, changeConnection]);
|
||||||
const handleTypeAheadKeyDown = React.useCallback(
|
const handleTypeAheadKeyDown = React.useCallback(
|
||||||
(waveEvent: WaveKeyboardEvent): boolean => {
|
(waveEvent: WaveKeyboardEvent): boolean => {
|
||||||
if (keyutil.checkKeyPressed(waveEvent, "Enter")) {
|
if (keyutil.checkKeyPressed(waveEvent, "Enter")) {
|
||||||
@ -493,6 +600,9 @@ const ChangeConnectionBlockModal = React.memo(
|
|||||||
},
|
},
|
||||||
[changeConnModalAtom, viewModel, blockId, connSelected]
|
[changeConnModalAtom, viewModel, blockId, connSelected]
|
||||||
);
|
);
|
||||||
|
React.useEffect(() => {
|
||||||
|
console.log("connSelected is: ", connSelected);
|
||||||
|
}, [connSelected]);
|
||||||
if (!changeConnModalOpen) {
|
if (!changeConnModalOpen) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -500,7 +610,7 @@ const ChangeConnectionBlockModal = React.memo(
|
|||||||
<TypeAheadModal
|
<TypeAheadModal
|
||||||
blockRef={blockRef}
|
blockRef={blockRef}
|
||||||
anchorRef={connBtnRef}
|
anchorRef={connBtnRef}
|
||||||
// suggestions={[]}
|
suggestions={suggestions}
|
||||||
onSelect={(selected: string) => {
|
onSelect={(selected: string) => {
|
||||||
changeConnection(selected);
|
changeConnection(selected);
|
||||||
globalStore.set(changeConnModalAtom, false);
|
globalStore.set(changeConnModalAtom, false);
|
||||||
@ -509,7 +619,7 @@ const ChangeConnectionBlockModal = React.memo(
|
|||||||
onKeyDown={(e) => keyutil.keydownWrapper(handleTypeAheadKeyDown)(e)}
|
onKeyDown={(e) => keyutil.keydownWrapper(handleTypeAheadKeyDown)(e)}
|
||||||
onChange={(current: string) => setConnSelected(current)}
|
onChange={(current: string) => setConnSelected(current)}
|
||||||
value={connSelected}
|
value={connSelected}
|
||||||
label="Switch connection"
|
label="username@host"
|
||||||
onClickBackdrop={() => globalStore.set(changeConnModalAtom, false)}
|
onClickBackdrop={() => globalStore.set(changeConnModalAtom, false)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -208,7 +208,7 @@ export const ConnectionButton = React.memo(
|
|||||||
const connStatus = jotai.useAtomValue(connStatusAtom);
|
const connStatus = jotai.useAtomValue(connStatusAtom);
|
||||||
let showDisconnectedSlash = false;
|
let showDisconnectedSlash = false;
|
||||||
let connIconElem: React.ReactNode = null;
|
let connIconElem: React.ReactNode = null;
|
||||||
let color = "#53b4ea";
|
let color = "var(--conn-icon-color)";
|
||||||
const clickHandler = function () {
|
const clickHandler = function () {
|
||||||
setConnModalOpen(true);
|
setConnModalOpen(true);
|
||||||
};
|
};
|
||||||
|
@ -8,45 +8,35 @@ import ReactDOM from "react-dom";
|
|||||||
|
|
||||||
import "./typeaheadmodal.less";
|
import "./typeaheadmodal.less";
|
||||||
|
|
||||||
type ConnStatus = "connected" | "connecting" | "disconnected" | "error";
|
|
||||||
|
|
||||||
interface BaseItem {
|
|
||||||
label: string;
|
|
||||||
value: string;
|
|
||||||
icon?: string | React.ReactNode;
|
|
||||||
}
|
|
||||||
interface ConnectionItem extends BaseItem {
|
|
||||||
status: ConnStatus;
|
|
||||||
iconColor: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ConnectionScope {
|
|
||||||
headerText?: string;
|
|
||||||
items: ConnectionItem[];
|
|
||||||
}
|
|
||||||
|
|
||||||
type SuggestionsType = ConnectionItem | ConnectionScope;
|
|
||||||
|
|
||||||
interface SuggestionsProps {
|
interface SuggestionsProps {
|
||||||
suggestions?: SuggestionsType[];
|
suggestions?: SuggestionsType[];
|
||||||
onSelect?: (_: string) => void;
|
onSelect?: (_: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Suggestions = forwardRef<HTMLDivElement, SuggestionsProps>(({ suggestions, onSelect }: SuggestionsProps, ref) => {
|
const Suggestions = forwardRef<HTMLDivElement, SuggestionsProps>(({ suggestions, onSelect }: SuggestionsProps, ref) => {
|
||||||
const renderIcon = (icon: string | React.ReactNode) => {
|
const renderIcon = (icon: string | React.ReactNode, color: string) => {
|
||||||
if (typeof icon === "string") {
|
if (typeof icon === "string") {
|
||||||
return <i className={makeIconClass(icon, false)}></i>;
|
return <i className={makeIconClass(icon, false)} style={{ color: color }}></i>;
|
||||||
}
|
}
|
||||||
return icon;
|
return icon;
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderItem = (item: BaseItem | ConnectionItem, index: number) => (
|
const renderItem = (item: SuggestionBaseItem | SuggestionConnectionItem, index: number) => (
|
||||||
<div key={index} onClick={() => onSelect(item.label)} className="suggestion-item">
|
<div
|
||||||
|
key={index}
|
||||||
|
onClick={() => {
|
||||||
|
if ("onSelect" in item && item.onSelect) {
|
||||||
|
item.onSelect(item.label);
|
||||||
|
} else {
|
||||||
|
onSelect(item.label);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
className="suggestion-item"
|
||||||
|
>
|
||||||
<div className="name">
|
<div className="name">
|
||||||
{item.icon && renderIcon(item.icon)}
|
{item.icon && renderIcon(item.icon, "iconColor" in item && item.iconColor ? item.iconColor : "inherit")}
|
||||||
{item.label}
|
{item.label}
|
||||||
</div>
|
</div>
|
||||||
{"status" in item && item.status == "connected" && <i className={makeIconClass("fa-check", false)}></i>}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -61,7 +51,7 @@ const Suggestions = forwardRef<HTMLDivElement, SuggestionsProps>(({ suggestions,
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return renderItem(item as BaseItem, index);
|
return renderItem(item as SuggestionBaseItem, index);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -249,4 +239,3 @@ const TypeAheadModal = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
export { TypeAheadModal };
|
export { TypeAheadModal };
|
||||||
export type { SuggestionsType };
|
|
||||||
|
@ -32,6 +32,11 @@ class WshServerType {
|
|||||||
return wshServerRpcHelper_call("connensure", data, opts);
|
return wshServerRpcHelper_call("connensure", data, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// command "connlist" [call]
|
||||||
|
ConnListCommand(opts?: RpcOpts): Promise<string[]> {
|
||||||
|
return wshServerRpcHelper_call("connlist", null, opts);
|
||||||
|
}
|
||||||
|
|
||||||
// command "connreinstallwsh" [call]
|
// command "connreinstallwsh" [call]
|
||||||
ConnReinstallWshCommand(data: string, opts?: RpcOpts): Promise<void> {
|
ConnReinstallWshCommand(data: string, opts?: RpcOpts): Promise<void> {
|
||||||
return wshServerRpcHelper_call("connreinstallwsh", data, opts);
|
return wshServerRpcHelper_call("connreinstallwsh", data, opts);
|
||||||
|
@ -91,4 +91,7 @@
|
|||||||
--form-element-primary-color: var(--accent-color);
|
--form-element-primary-color: var(--accent-color);
|
||||||
--form-element-secondary-color: rgba(255, 255, 255, 0.2);
|
--form-element-secondary-color: rgba(255, 255, 255, 0.2);
|
||||||
--form-element-error-color: var(--error-color);
|
--form-element-error-color: var(--error-color);
|
||||||
|
|
||||||
|
/* temporary conn icon color - will be replaced with individual colors */
|
||||||
|
--conn-icon-color: #53b4ea;
|
||||||
}
|
}
|
||||||
|
21
frontend/types/custom.d.ts
vendored
21
frontend/types/custom.d.ts
vendored
@ -245,6 +245,27 @@ declare global {
|
|||||||
openSwitchConnection?: () => void;
|
openSwitchConnection?: () => void;
|
||||||
viewModel: ViewModel;
|
viewModel: ViewModel;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type ConnStatusType = "connected" | "connecting" | "disconnected" | "error" | "init";
|
||||||
|
|
||||||
|
interface SuggestionBaseItem {
|
||||||
|
label: string;
|
||||||
|
value: string;
|
||||||
|
icon?: string | React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SuggestionConnectionItem extends SuggestionBaseItem {
|
||||||
|
status: ConnStatusType;
|
||||||
|
iconColor: string;
|
||||||
|
onSelect?: (_: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SuggestionConnectionScope {
|
||||||
|
headerText?: string;
|
||||||
|
items: SuggestionConnectionItem[];
|
||||||
|
}
|
||||||
|
|
||||||
|
type SuggestionsType = SuggestionConnectionItem | SuggestionConnectionScope;
|
||||||
}
|
}
|
||||||
|
|
||||||
export {};
|
export {};
|
||||||
|
@ -540,16 +540,17 @@ func resolveSshConfigPatterns(configFiles []string) ([]string, error) {
|
|||||||
// for each host, find the first good alias
|
// for each host, find the first good alias
|
||||||
for _, hostPattern := range host.Patterns {
|
for _, hostPattern := range host.Patterns {
|
||||||
hostPatternStr := hostPattern.String()
|
hostPatternStr := hostPattern.String()
|
||||||
if !strings.Contains(hostPatternStr, "*") || alreadyUsed[hostPatternStr] {
|
normalized := remote.NormalizeConfigPattern(hostPatternStr)
|
||||||
discoveredPatterns = append(discoveredPatterns, hostPatternStr)
|
if (!strings.Contains(hostPatternStr, "*") && !strings.Contains(hostPatternStr, "?") && !strings.Contains(hostPatternStr, "!")) || alreadyUsed[normalized] {
|
||||||
alreadyUsed[hostPatternStr] = true
|
discoveredPatterns = append(discoveredPatterns, normalized)
|
||||||
|
alreadyUsed[normalized] = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(errs) == len(configFiles) {
|
if len(errs) == len(configFiles) {
|
||||||
errs = append([]error{fmt.Errorf("no ssh config files could be opened:\n")}, errs...)
|
errs = append([]error{fmt.Errorf("no ssh config files could be opened: ")}, errs...)
|
||||||
return nil, errors.Join(errs...)
|
return nil, errors.Join(errs...)
|
||||||
}
|
}
|
||||||
if len(discoveredPatterns) == 0 {
|
if len(discoveredPatterns) == 0 {
|
||||||
@ -559,6 +560,42 @@ func resolveSshConfigPatterns(configFiles []string) ([]string, error) {
|
|||||||
return discoveredPatterns, nil
|
return discoveredPatterns, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetConnectionsList() ([]string, error) {
|
||||||
|
existing := GetAllConnStatus()
|
||||||
|
var currentlyRunning []string
|
||||||
|
var hasConnected []string
|
||||||
|
|
||||||
|
// populate all lists
|
||||||
|
for _, stat := range existing {
|
||||||
|
if stat.Connected {
|
||||||
|
currentlyRunning = append(currentlyRunning, stat.Connection)
|
||||||
|
}
|
||||||
|
|
||||||
|
if stat.HasConnected {
|
||||||
|
hasConnected = append(hasConnected, stat.Connection)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fromConfig, err := GetConnectionsFromConfig()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// sort into one final list and remove duplicates
|
||||||
|
alreadyUsed := make(map[string]struct{})
|
||||||
|
var connList []string
|
||||||
|
|
||||||
|
for _, subList := range [][]string{currentlyRunning, hasConnected, fromConfig} {
|
||||||
|
for _, pattern := range subList {
|
||||||
|
if _, used := alreadyUsed[pattern]; !used {
|
||||||
|
connList = append(connList, pattern)
|
||||||
|
alreadyUsed[pattern] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return connList, nil
|
||||||
|
}
|
||||||
|
|
||||||
func GetConnectionsFromConfig() ([]string, error) {
|
func GetConnectionsFromConfig() ([]string, error) {
|
||||||
home := wavebase.GetHomeDir()
|
home := wavebase.GetHomeDir()
|
||||||
localConfig := filepath.Join(home, ".ssh", "config")
|
localConfig := filepath.Join(home, ".ssh", "config")
|
||||||
|
@ -7,11 +7,14 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
"os/user"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/kevinburke/ssh_config"
|
||||||
|
"github.com/skeema/knownhosts"
|
||||||
"golang.org/x/crypto/ssh"
|
"golang.org/x/crypto/ssh"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -330,3 +333,27 @@ func IsPowershell(shellPath string) bool {
|
|||||||
shellBase := filepath.Base(shellPath)
|
shellBase := filepath.Base(shellPath)
|
||||||
return strings.Contains(shellBase, "powershell") || strings.Contains(shellBase, "pwsh")
|
return strings.Contains(shellBase, "powershell") || strings.Contains(shellBase, "pwsh")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NormalizeConfigPattern(pattern string) string {
|
||||||
|
userName, err := ssh_config.GetStrict(pattern, "User")
|
||||||
|
if err != nil {
|
||||||
|
localUser, err := user.Current()
|
||||||
|
if err == nil {
|
||||||
|
userName = localUser.Username
|
||||||
|
}
|
||||||
|
}
|
||||||
|
port, err := ssh_config.GetStrict(pattern, "Port")
|
||||||
|
if err != nil {
|
||||||
|
port = "22"
|
||||||
|
}
|
||||||
|
if userName != "" {
|
||||||
|
userName += "@"
|
||||||
|
}
|
||||||
|
if port == "22" {
|
||||||
|
port = ""
|
||||||
|
} else {
|
||||||
|
port = ":" + port
|
||||||
|
}
|
||||||
|
unnormalized := fmt.Sprintf("%s%s%s", userName, pattern, port)
|
||||||
|
return knownhosts.Normalize(unnormalized)
|
||||||
|
}
|
||||||
|
@ -42,6 +42,12 @@ func ConnEnsureCommand(w *wshutil.WshRpc, data string, opts *wshrpc.RpcOpts) err
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// command "connlist", wshserver.ConnListCommand
|
||||||
|
func ConnListCommand(w *wshutil.WshRpc, opts *wshrpc.RpcOpts) ([]string, error) {
|
||||||
|
resp, err := sendRpcRequestCallHelper[[]string](w, "connlist", nil, opts)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
// command "connreinstallwsh", wshserver.ConnReinstallWshCommand
|
// command "connreinstallwsh", wshserver.ConnReinstallWshCommand
|
||||||
func ConnReinstallWshCommand(w *wshutil.WshRpc, data string, opts *wshrpc.RpcOpts) error {
|
func ConnReinstallWshCommand(w *wshutil.WshRpc, data string, opts *wshrpc.RpcOpts) error {
|
||||||
_, err := sendRpcRequestCallHelper[any](w, "connreinstallwsh", data, opts)
|
_, err := sendRpcRequestCallHelper[any](w, "connreinstallwsh", data, opts)
|
||||||
|
@ -70,6 +70,7 @@ const (
|
|||||||
Command_ConnReinstallWsh = "connreinstallwsh"
|
Command_ConnReinstallWsh = "connreinstallwsh"
|
||||||
Command_ConnConnect = "connconnect"
|
Command_ConnConnect = "connconnect"
|
||||||
Command_ConnDisconnect = "conndisconnect"
|
Command_ConnDisconnect = "conndisconnect"
|
||||||
|
Command_ConnList = "connlist"
|
||||||
)
|
)
|
||||||
|
|
||||||
type RespOrErrorUnion[T any] struct {
|
type RespOrErrorUnion[T any] struct {
|
||||||
@ -112,6 +113,7 @@ type WshRpcInterface interface {
|
|||||||
ConnReinstallWshCommand(ctx context.Context, connName string) error
|
ConnReinstallWshCommand(ctx context.Context, connName string) error
|
||||||
ConnConnectCommand(ctx context.Context, connName string) error
|
ConnConnectCommand(ctx context.Context, connName string) error
|
||||||
ConnDisconnectCommand(ctx context.Context, connName string) error
|
ConnDisconnectCommand(ctx context.Context, connName string) error
|
||||||
|
ConnListCommand(ctx context.Context) ([]string, error)
|
||||||
|
|
||||||
// eventrecv is special, it's handled internally by WshRpc with EventListener
|
// eventrecv is special, it's handled internally by WshRpc with EventListener
|
||||||
EventRecvCommand(ctx context.Context, data WaveEvent) error
|
EventRecvCommand(ctx context.Context, data WaveEvent) error
|
||||||
|
@ -522,3 +522,7 @@ func (ws *WshServer) ConnReinstallWshCommand(ctx context.Context, connName strin
|
|||||||
}
|
}
|
||||||
return conn.CheckAndInstallWsh(ctx, connName, &conncontroller.WshInstallOpts{Force: true, NoUserPrompt: true})
|
return conn.CheckAndInstallWsh(ctx, connName, &conncontroller.WshInstallOpts{Force: true, NoUserPrompt: true})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ws *WshServer) ConnListCommand(ctx context.Context) ([]string, error) {
|
||||||
|
return conncontroller.GetConnectionsList()
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user