mirror of
https://github.com/wavetermdev/waveterm.git
synced 2024-12-21 16:38:23 +01:00
New Connections Configs (#1383)
This adds the following connections changes: - connections can be hidden from the dropdown in our internal connections.json config - `wsh ssh` -i will write identity files to the internal connections.json config for that connection - the internal connections.json config will also be used to get identity files when connecting - the internal connections.json config allows setting theme, fontsize, and font for specific connections - successful connections (including those using wsh ssh) are saved to the internal connections.json config - the connections.json config will be used to help pre-populate the dropdown list - adds an item to the dropdown to edit the connections config in an ephemeral block --------- Co-authored-by: Evan Simkowitz <esimkowitz@users.noreply.github.com>
This commit is contained in:
parent
5c315779ba
commit
b4b0222c9d
@ -15,6 +15,8 @@ import { TypeAheadModal } from "@/app/modals/typeaheadmodal";
|
||||
import { ContextMenuModel } from "@/app/store/contextmenu";
|
||||
import {
|
||||
atoms,
|
||||
createBlock,
|
||||
getApi,
|
||||
getBlockComponentModel,
|
||||
getConnStatusAtom,
|
||||
getHostName,
|
||||
@ -182,6 +184,9 @@ const BlockFrame_Header = ({
|
||||
const prevMagifiedState = React.useRef(magnified);
|
||||
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;
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!magnified || preview || prevMagifiedState.current) {
|
||||
@ -239,6 +244,11 @@ const BlockFrame_Header = ({
|
||||
</div>
|
||||
);
|
||||
}
|
||||
const wshInstallButton: IconButtonDecl = {
|
||||
elemtype: "iconbutton",
|
||||
icon: "link-slash",
|
||||
title: "wsh is not installed for this connection",
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="block-frame-default-header" ref={dragHandleRef} onContextMenu={onContextMenu}>
|
||||
@ -256,6 +266,9 @@ const BlockFrame_Header = ({
|
||||
changeConnModalAtom={changeConnModalAtom}
|
||||
/>
|
||||
)}
|
||||
{manageConnection && !wshEnabled && (
|
||||
<IconButton decl={wshInstallButton} className="block-frame-header-iconbutton" />
|
||||
)}
|
||||
<div className="block-frame-textelems-wrapper">{headerTextElems}</div>
|
||||
<div className="block-frame-end-icons">{endIconsElem}</div>
|
||||
</div>
|
||||
@ -568,6 +581,10 @@ const ChangeConnectionBlockModal = React.memo(
|
||||
const allConnStatus = jotai.useAtomValue(atoms.allConnStatus);
|
||||
const [rowIndex, setRowIndex] = React.useState(0);
|
||||
const connStatusMap = new Map<string, ConnStatus>();
|
||||
const fullConfig = jotai.useAtomValue(atoms.fullConfigAtom);
|
||||
const connectionsConfig = fullConfig.connections;
|
||||
let filterOutNowsh = util.useAtomValueSafe(viewModel.filterOutNowsh) || true;
|
||||
|
||||
let maxActiveConnNum = 1;
|
||||
for (const conn of allConnStatus) {
|
||||
if (conn.activeconnnum > maxActiveConnNum) {
|
||||
@ -638,7 +655,12 @@ const ChangeConnectionBlockModal = React.memo(
|
||||
if (conn === connSelected) {
|
||||
createNew = false;
|
||||
}
|
||||
if (conn.includes(connSelected)) {
|
||||
if (
|
||||
conn.includes(connSelected) &&
|
||||
connectionsConfig[conn]?.["display:hidden"] != true &&
|
||||
(connectionsConfig[conn]?.["conn:wshenabled"] != false || !filterOutNowsh)
|
||||
// != false is necessary because of defaults
|
||||
) {
|
||||
filteredList.push(conn);
|
||||
}
|
||||
}
|
||||
@ -647,7 +669,12 @@ const ChangeConnectionBlockModal = React.memo(
|
||||
if (conn === connSelected) {
|
||||
createNew = false;
|
||||
}
|
||||
if (conn.includes(connSelected)) {
|
||||
if (
|
||||
conn.includes(connSelected) &&
|
||||
connectionsConfig[conn]?.["display:hidden"] != true &&
|
||||
(connectionsConfig[conn]?.["conn:wshenabled"] != false || !filterOutNowsh)
|
||||
// != false is necessary because of defaults
|
||||
) {
|
||||
filteredWslList.push(conn);
|
||||
}
|
||||
}
|
||||
@ -734,9 +761,38 @@ const ChangeConnectionBlockModal = React.memo(
|
||||
};
|
||||
return item;
|
||||
});
|
||||
const connectionsEditItem: SuggestionConnectionItem = {
|
||||
status: "disconnected",
|
||||
icon: "gear",
|
||||
iconColor: "var(--grey-text-color",
|
||||
value: "Edit Connections",
|
||||
label: "Edit Connections",
|
||||
onSelect: () => {
|
||||
util.fireAndForget(async () => {
|
||||
globalStore.set(changeConnModalAtom, false);
|
||||
const path = `${getApi().getConfigDir()}/connections.json`;
|
||||
const blockDef: BlockDef = {
|
||||
meta: {
|
||||
view: "preview",
|
||||
file: path,
|
||||
},
|
||||
};
|
||||
await createBlock(blockDef, false, true);
|
||||
});
|
||||
},
|
||||
};
|
||||
const sortedRemoteItems = remoteItems.sort(
|
||||
(itemA: SuggestionConnectionItem, itemB: SuggestionConnectionItem) => {
|
||||
const connNameA = itemA.value;
|
||||
const connNameB = itemB.value;
|
||||
const valueA = connectionsConfig[connNameA]?.["display:order"] ?? 0;
|
||||
const valueB = connectionsConfig[connNameB]?.["display:order"] ?? 0;
|
||||
return valueA - valueB;
|
||||
}
|
||||
);
|
||||
const remoteSuggestions: SuggestionConnectionScope = {
|
||||
headerText: "Remote",
|
||||
items: remoteItems,
|
||||
items: [...sortedRemoteItems, connectionsEditItem],
|
||||
};
|
||||
|
||||
let suggestions: Array<SuggestionsType> = [];
|
||||
|
@ -246,6 +246,21 @@ function useBlockMetaKeyAtom<T extends keyof MetaType>(blockId: string, key: T):
|
||||
return useAtomValue(getBlockMetaKeyAtom(blockId, key));
|
||||
}
|
||||
|
||||
function getConnConfigKeyAtom<T extends keyof ConnKeywords>(connName: string, key: T): Atom<ConnKeywords[T]> {
|
||||
let connCache = getSingleConnAtomCache(connName);
|
||||
const keyAtomName = "#conn-" + key;
|
||||
let keyAtom = connCache.get(keyAtomName);
|
||||
if (keyAtom != null) {
|
||||
return keyAtom;
|
||||
}
|
||||
keyAtom = atom((get) => {
|
||||
let fullConfig = get(atoms.fullConfigAtom);
|
||||
return fullConfig.connections[connName]?.[key];
|
||||
});
|
||||
connCache.set(keyAtomName, keyAtom);
|
||||
return keyAtom;
|
||||
}
|
||||
|
||||
const settingsAtomCache = new Map<string, Atom<any>>();
|
||||
|
||||
function getOverrideConfigAtom<T extends keyof SettingsType>(blockId: string, key: T): Atom<SettingsType[T]> {
|
||||
@ -261,6 +276,13 @@ function getOverrideConfigAtom<T extends keyof SettingsType>(blockId: string, ke
|
||||
if (metaKeyVal != null) {
|
||||
return metaKeyVal;
|
||||
}
|
||||
const connNameAtom = getBlockMetaKeyAtom(blockId, "connection");
|
||||
const connName = get(connNameAtom);
|
||||
const connConfigKeyAtom = getConnConfigKeyAtom(connName, key as any);
|
||||
const connConfigKeyVal = get(connConfigKeyAtom);
|
||||
if (connConfigKeyVal != null) {
|
||||
return connConfigKeyVal;
|
||||
}
|
||||
const settingsKeyAtom = getSettingsKeyAtom(key);
|
||||
const settingsVal = get(settingsKeyAtom);
|
||||
if (settingsVal != null) {
|
||||
@ -322,6 +344,15 @@ function getSingleBlockAtomCache(blockId: string): Map<string, Atom<any>> {
|
||||
return blockCache;
|
||||
}
|
||||
|
||||
function getSingleConnAtomCache(connName: string): Map<string, Atom<any>> {
|
||||
let blockCache = blockAtomCache.get(connName);
|
||||
if (blockCache == null) {
|
||||
blockCache = new Map<string, Atom<any>>();
|
||||
blockAtomCache.set(connName, blockCache);
|
||||
}
|
||||
return blockCache;
|
||||
}
|
||||
|
||||
function useBlockAtom<T>(blockId: string, name: string, makeFn: () => Atom<T>): Atom<T> {
|
||||
const blockCache = getSingleBlockAtomCache(blockId);
|
||||
let atom = blockCache.get(name);
|
||||
|
@ -121,6 +121,7 @@ export class PreviewModel implements ViewModel {
|
||||
loadableSpecializedView: Atom<Loadable<{ specializedView?: string; errorStr?: string }>>;
|
||||
manageConnection: Atom<boolean>;
|
||||
connStatus: Atom<ConnStatus>;
|
||||
filterOutNowsh?: Atom<boolean>;
|
||||
|
||||
metaFilePath: Atom<string>;
|
||||
statFilePath: Atom<Promise<string>>;
|
||||
@ -164,6 +165,7 @@ export class PreviewModel implements ViewModel {
|
||||
this.manageConnection = atom(true);
|
||||
this.blockAtom = WOS.getWaveObjectAtom<Block>(`block:${blockId}`);
|
||||
this.markdownShowToc = atom(false);
|
||||
this.filterOutNowsh = atom(true);
|
||||
this.monacoRef = createRef();
|
||||
this.viewIcon = atom((get) => {
|
||||
const blockData = get(this.blockAtom);
|
||||
|
@ -91,7 +91,7 @@ function convertWaveEventToDataItem(event: WaveEvent): DataItem {
|
||||
return dataItem;
|
||||
}
|
||||
|
||||
class SysinfoViewModel {
|
||||
class SysinfoViewModel implements ViewModel {
|
||||
viewType: string;
|
||||
blockAtom: jotai.Atom<Block>;
|
||||
termMode: jotai.Atom<string>;
|
||||
@ -109,6 +109,7 @@ class SysinfoViewModel {
|
||||
metrics: jotai.Atom<string[]>;
|
||||
connection: jotai.Atom<string>;
|
||||
manageConnection: jotai.Atom<boolean>;
|
||||
filterOutNowsh: jotai.Atom<boolean>;
|
||||
connStatus: jotai.Atom<ConnStatus>;
|
||||
plotMetaAtom: jotai.PrimitiveAtom<Map<string, TimeSeriesMeta>>;
|
||||
endIconButtons: jotai.Atom<IconButtonDecl[]>;
|
||||
@ -176,6 +177,7 @@ class SysinfoViewModel {
|
||||
});
|
||||
this.plotMetaAtom = jotai.atom(new Map(Object.entries(DefaultPlotMeta)));
|
||||
this.manageConnection = jotai.atom(true);
|
||||
this.filterOutNowsh = jotai.atom(true);
|
||||
this.loadingAtom = jotai.atom(true);
|
||||
this.numPoints = jotai.atom((get) => {
|
||||
const blockData = get(this.blockAtom);
|
||||
|
@ -41,7 +41,7 @@ type InitialLoadDataType = {
|
||||
heldData: Uint8Array[];
|
||||
};
|
||||
|
||||
class TermViewModel {
|
||||
class TermViewModel implements ViewModel {
|
||||
viewType: string;
|
||||
nodeModel: BlockNodeModel;
|
||||
connected: boolean;
|
||||
@ -54,6 +54,7 @@ class TermViewModel {
|
||||
viewText: jotai.Atom<HeaderElem[]>;
|
||||
blockBg: jotai.Atom<MetaType>;
|
||||
manageConnection: jotai.Atom<boolean>;
|
||||
filterOutNowsh?: jotai.Atom<boolean>;
|
||||
connStatus: jotai.Atom<ConnStatus>;
|
||||
termWshClient: TermWshClient;
|
||||
vdomBlockId: jotai.Atom<string>;
|
||||
@ -196,6 +197,7 @@ class TermViewModel {
|
||||
}
|
||||
return true;
|
||||
});
|
||||
this.filterOutNowsh = jotai.atom(false);
|
||||
this.termThemeNameAtom = useBlockAtom(blockId, "termthemeatom", () => {
|
||||
return jotai.atom<string>((get) => {
|
||||
return get(getOverrideConfigAtom(this.blockId, "term:theme")) ?? DefaultTermTheme;
|
||||
@ -221,7 +223,10 @@ class TermViewModel {
|
||||
const blockData = get(this.blockAtom);
|
||||
const fsSettingsAtom = getSettingsKeyAtom("term:fontsize");
|
||||
const settingsFontSize = get(fsSettingsAtom);
|
||||
const rtnFontSize = blockData?.meta?.["term:fontsize"] ?? settingsFontSize ?? 12;
|
||||
const connName = blockData?.meta?.connection;
|
||||
const fullConfig = get(atoms.fullConfigAtom);
|
||||
const connFontSize = fullConfig?.connections?.[connName]?.["term:fontsize"];
|
||||
const rtnFontSize = blockData?.meta?.["term:fontsize"] ?? connFontSize ?? settingsFontSize ?? 12;
|
||||
if (typeof rtnFontSize != "number" || isNaN(rtnFontSize) || rtnFontSize < 4 || rtnFontSize > 64) {
|
||||
return 12;
|
||||
}
|
||||
@ -725,6 +730,8 @@ const TerminalView = ({ blockId, model }: TerminalViewProps) => {
|
||||
const termModeRef = React.useRef(termMode);
|
||||
|
||||
const termFontSize = jotai.useAtomValue(model.fontSizeAtom);
|
||||
const fullConfig = globalStore.get(atoms.fullConfigAtom);
|
||||
const connFontFamily = fullConfig.connections?.[blockData?.meta?.connection]?.["term:fontfamily"];
|
||||
|
||||
React.useEffect(() => {
|
||||
const fullConfig = globalStore.get(atoms.fullConfigAtom);
|
||||
@ -750,7 +757,7 @@ const TerminalView = ({ blockId, model }: TerminalViewProps) => {
|
||||
{
|
||||
theme: termTheme,
|
||||
fontSize: termFontSize,
|
||||
fontFamily: termSettings?.["term:fontfamily"] ?? "Hack",
|
||||
fontFamily: termSettings?.["term:fontfamily"] ?? connFontFamily ?? "Hack",
|
||||
drawBoldTextInBrightColors: false,
|
||||
fontWeight: "normal",
|
||||
fontWeightBold: "bold",
|
||||
@ -784,7 +791,7 @@ const TerminalView = ({ blockId, model }: TerminalViewProps) => {
|
||||
termWrap.dispose();
|
||||
rszObs.disconnect();
|
||||
};
|
||||
}, [blockId, termSettings, termFontSize]);
|
||||
}, [blockId, termSettings, termFontSize, connFontFamily]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (termModeRef.current == "vdom" && termMode == "term") {
|
||||
|
1
frontend/types/custom.d.ts
vendored
1
frontend/types/custom.d.ts
vendored
@ -237,6 +237,7 @@ declare global {
|
||||
blockBg?: jotai.Atom<MetaType>;
|
||||
manageConnection?: jotai.Atom<boolean>;
|
||||
noPadding?: jotai.Atom<boolean>;
|
||||
filterOutNowsh?: jotai.Atom<boolean>;
|
||||
|
||||
onBack?: () => void;
|
||||
onForward?: () => void;
|
||||
|
10
frontend/types/gotypes.d.ts
vendored
10
frontend/types/gotypes.d.ts
vendored
@ -277,8 +277,14 @@ declare global {
|
||||
|
||||
// wshrpc.ConnKeywords
|
||||
type ConnKeywords = {
|
||||
wshenabled?: boolean;
|
||||
askbeforewshinstall?: boolean;
|
||||
"conn:wshenabled"?: boolean;
|
||||
"conn:askbeforewshinstall"?: boolean;
|
||||
"display:hidden"?: boolean;
|
||||
"display:order"?: number;
|
||||
"term:*"?: boolean;
|
||||
"term:fontsize"?: number;
|
||||
"term:fontfamily"?: string;
|
||||
"term:theme"?: string;
|
||||
"ssh:user"?: string;
|
||||
"ssh:hostname"?: string;
|
||||
"ssh:port"?: string;
|
||||
|
@ -342,7 +342,7 @@ func (conn *SSHConn) CheckAndInstallWsh(ctx context.Context, clientDisplayName s
|
||||
}
|
||||
if !response.Confirm {
|
||||
meta := make(map[string]any)
|
||||
meta["wshenabled"] = false
|
||||
meta["conn:wshenabled"] = false
|
||||
err = wconfig.SetConnectionsConfigValue(conn.GetName(), meta)
|
||||
if err != nil {
|
||||
log.Printf("warning: error writing to connections file: %v", err)
|
||||
@ -454,9 +454,41 @@ func (conn *SSHConn) Connect(ctx context.Context, connFlags *wshrpc.ConnKeywords
|
||||
}
|
||||
})
|
||||
conn.FireConnChangeEvent()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// logic for saving connection and potential flags (we only save once a connection has been made successfully)
|
||||
// at the moment, identity files is the only saved flag
|
||||
var identityFiles []string
|
||||
existingConfig := wconfig.ReadFullConfig()
|
||||
existingConnection, ok := existingConfig.Connections[conn.GetName()]
|
||||
if ok {
|
||||
identityFiles = existingConnection.SshIdentityFile
|
||||
}
|
||||
if err != nil {
|
||||
// i do not consider this a critical failure
|
||||
log.Printf("config read error: unable to save connection %s: %v", conn.GetName(), err)
|
||||
}
|
||||
|
||||
meta := make(map[string]any)
|
||||
if connFlags.SshIdentityFile != nil {
|
||||
for _, identityFile := range connFlags.SshIdentityFile {
|
||||
if utilfn.ContainsStr(identityFiles, identityFile) {
|
||||
continue
|
||||
}
|
||||
identityFiles = append(identityFiles, connFlags.SshIdentityFile...)
|
||||
}
|
||||
meta["ssh:identityfile"] = identityFiles
|
||||
}
|
||||
err = wconfig.SetConnectionsConfigValue(conn.GetName(), meta)
|
||||
if err != nil {
|
||||
// i do not consider this a critical failure
|
||||
log.Printf("config write error: unable to save connection %s: %v", conn.GetName(), err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (conn *SSHConn) WithLock(fn func()) {
|
||||
conn.Lock.Lock()
|
||||
defer conn.Lock.Unlock()
|
||||
@ -484,11 +516,11 @@ func (conn *SSHConn) connectInternal(ctx context.Context, connFlags *wshrpc.Conn
|
||||
askBeforeInstall := config.Settings.ConnAskBeforeWshInstall
|
||||
connSettings, ok := config.Connections[conn.GetName()]
|
||||
if ok {
|
||||
if connSettings.WshEnabled != nil {
|
||||
enableWsh = *connSettings.WshEnabled
|
||||
if connSettings.ConnWshEnabled != nil {
|
||||
enableWsh = *connSettings.ConnWshEnabled
|
||||
}
|
||||
if connSettings.AskBeforeWshInstall != nil {
|
||||
askBeforeInstall = *connSettings.AskBeforeWshInstall
|
||||
if connSettings.ConnAskBeforeWshInstall != nil {
|
||||
askBeforeInstall = *connSettings.ConnAskBeforeWshInstall
|
||||
}
|
||||
}
|
||||
if enableWsh {
|
||||
@ -661,6 +693,9 @@ func GetConnectionsList() ([]string, error) {
|
||||
hasConnected = append(hasConnected, stat.Connection)
|
||||
}
|
||||
}
|
||||
|
||||
fromInternal := GetConnectionsFromInternalConfig()
|
||||
|
||||
fromConfig, err := GetConnectionsFromConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -670,7 +705,7 @@ func GetConnectionsList() ([]string, error) {
|
||||
alreadyUsed := make(map[string]struct{})
|
||||
var connList []string
|
||||
|
||||
for _, subList := range [][]string{currentlyRunning, hasConnected, fromConfig} {
|
||||
for _, subList := range [][]string{currentlyRunning, hasConnected, fromInternal, fromConfig} {
|
||||
for _, pattern := range subList {
|
||||
if _, used := alreadyUsed[pattern]; !used {
|
||||
connList = append(connList, pattern)
|
||||
@ -682,6 +717,15 @@ func GetConnectionsList() ([]string, error) {
|
||||
return connList, nil
|
||||
}
|
||||
|
||||
func GetConnectionsFromInternalConfig() []string {
|
||||
var internalNames []string
|
||||
config := wconfig.ReadFullConfig()
|
||||
for internalName := range config.Connections {
|
||||
internalNames = append(internalNames, internalName)
|
||||
}
|
||||
return internalNames
|
||||
}
|
||||
|
||||
func GetConnectionsFromConfig() ([]string, error) {
|
||||
home := wavebase.GetHomeDir()
|
||||
localConfig := filepath.Join(home, ".ssh", "config")
|
||||
|
@ -27,6 +27,7 @@ import (
|
||||
"github.com/wavetermdev/waveterm/pkg/userinput"
|
||||
"github.com/wavetermdev/waveterm/pkg/util/shellutil"
|
||||
"github.com/wavetermdev/waveterm/pkg/wavebase"
|
||||
"github.com/wavetermdev/waveterm/pkg/wconfig"
|
||||
"github.com/wavetermdev/waveterm/pkg/wshrpc"
|
||||
"golang.org/x/crypto/ssh"
|
||||
"golang.org/x/crypto/ssh/agent"
|
||||
@ -649,7 +650,13 @@ func ConnectToClient(connCtx context.Context, opts *SSHOpts, currentClient *ssh.
|
||||
connFlags.SshHostName = opts.SSHHost
|
||||
connFlags.SshPort = fmt.Sprintf("%d", opts.SSHPort)
|
||||
|
||||
sshKeywords, err := combineSshKeywords(connFlags, sshConfigKeywords)
|
||||
rawName := opts.String()
|
||||
savedKeywords, ok := wconfig.ReadFullConfig().Connections[rawName]
|
||||
if !ok {
|
||||
savedKeywords = wshrpc.ConnKeywords{}
|
||||
}
|
||||
|
||||
sshKeywords, err := combineSshKeywords(connFlags, sshConfigKeywords, &savedKeywords)
|
||||
if err != nil {
|
||||
return nil, debugInfo.JumpNum, ConnectionError{ConnectionDebugInfo: debugInfo, Err: err}
|
||||
}
|
||||
@ -685,7 +692,7 @@ func ConnectToClient(connCtx context.Context, opts *SSHOpts, currentClient *ssh.
|
||||
return client, debugInfo.JumpNum, nil
|
||||
}
|
||||
|
||||
func combineSshKeywords(userProvidedOpts *wshrpc.ConnKeywords, configKeywords *wshrpc.ConnKeywords) (*wshrpc.ConnKeywords, error) {
|
||||
func combineSshKeywords(userProvidedOpts *wshrpc.ConnKeywords, configKeywords *wshrpc.ConnKeywords, savedKeywords *wshrpc.ConnKeywords) (*wshrpc.ConnKeywords, error) {
|
||||
sshKeywords := &wshrpc.ConnKeywords{}
|
||||
|
||||
if userProvidedOpts.SshUser != "" {
|
||||
@ -716,7 +723,13 @@ func combineSshKeywords(userProvidedOpts *wshrpc.ConnKeywords, configKeywords *w
|
||||
sshKeywords.SshPort = "22"
|
||||
}
|
||||
|
||||
sshKeywords.SshIdentityFile = append(userProvidedOpts.SshIdentityFile, configKeywords.SshIdentityFile...)
|
||||
// use internal config ones
|
||||
if savedKeywords != nil {
|
||||
sshKeywords.SshIdentityFile = append(sshKeywords.SshIdentityFile, savedKeywords.SshIdentityFile...)
|
||||
}
|
||||
|
||||
sshKeywords.SshIdentityFile = append(sshKeywords.SshIdentityFile, userProvidedOpts.SshIdentityFile...)
|
||||
sshKeywords.SshIdentityFile = append(sshKeywords.SshIdentityFile, configKeywords.SshIdentityFile...)
|
||||
|
||||
// these are not officially supported in the waveterm frontend but can be configured
|
||||
// in ssh config files
|
||||
|
@ -1,3 +0,0 @@
|
||||
{
|
||||
"askbeforewshinstall": true
|
||||
}
|
@ -452,8 +452,16 @@ type CommandRemoteWriteFileData struct {
|
||||
}
|
||||
|
||||
type ConnKeywords struct {
|
||||
WshEnabled *bool `json:"wshenabled,omitempty"`
|
||||
AskBeforeWshInstall *bool `json:"askbeforewshinstall,omitempty"`
|
||||
ConnWshEnabled *bool `json:"conn:wshenabled,omitempty"`
|
||||
ConnAskBeforeWshInstall *bool `json:"conn:askbeforewshinstall,omitempty"`
|
||||
|
||||
DisplayHidden *bool `json:"display:hidden,omitempty"`
|
||||
DisplayOrder float32 `json:"display:order,omitempty"`
|
||||
|
||||
TermClear bool `json:"term:*,omitempty"`
|
||||
TermFontSize float64 `json:"term:fontsize,omitempty"`
|
||||
TermFontFamily string `json:"term:fontfamily,omitempty"`
|
||||
TermTheme string `json:"term:theme,omitempty"`
|
||||
|
||||
SshUser string `json:"ssh:user,omitempty"`
|
||||
SshHostName string `json:"ssh:hostname,omitempty"`
|
||||
|
Loading…
Reference in New Issue
Block a user