Compare commits

..

17 Commits

Author SHA1 Message Date
Sylvia Crowe 550e93e62a fix: change size of dummy key to 2048
This fixes the CodeQL scan issue for using a weak key.
2024-02-15 12:12:34 -08:00
Sylvia Crowe 42963f4287 merge branch 'main' into ssh--auth-control 2024-02-15 12:09:21 -08:00
sawka 766b7b90ce fix directory names in build-linux.md. also fix reload accelerator for Linux (keybindings) 2024-02-15 10:01:18 -08:00
Mike Sawka b3ada77c39 create assets directory, update README with screenshot (#294) 2024-02-15 09:53:39 -08:00
Sylvia Crowe a356df3396 fix: allow 60 second timeouts for ssh inputs
With the previous change, it is now possible to extend the timeout for
manual inputs. 60 seconds should be a reasonable starting point.
2024-02-13 17:57:23 -08:00
Sylvia Crowe 0eab1e3973 merge branch 'main' into ssh--auth-control 2024-02-13 17:54:30 -08:00
Sylvia Crowe 5abff8075b merge branch 'main' into 'ssh--auth-control'
This was mostly straightforward, but it appears that a previous commit
to main broke the user input modals by deleting a function. This adds
that back in addition to the merge.
2024-02-13 16:30:07 -08:00
Sylvia Crowe 6bd60e8330 deactivate timer while connecting to new ssh
The new ssh setup handles timers differently from the old one due to the
possibility of asking for user input multiple times. This limited the
user input to entirely be done within 15 seconds. This removes that
restriction which will allow those timers to increase. It does not
impact the legacy ssh systems or the local connections on the new
system.
2024-02-13 14:10:07 -08:00
Sylvia Crowe e62540bdbe fix: fix auth ordering and identity files
The last few commits introduced a few bugs that are fixed here. The
first is that the auth ordering is parsed as a single string and not a
list. This is fixed by manually splitting the string into a list. The
second is that the copy of identity files was not long enough to copy
the contents of the original. This is now updated to use the length of
the original in its construction.
2024-02-10 00:55:58 -08:00
Sylvia Crowe 85156bd6c2 feat: add batch mode support
BatchMode turns off user input to enter passwords for ssh. Because we
save passwords, we can still attempt these methods but we disable the
user interactive prompts in this case.
2024-02-09 23:44:53 -08:00
Sylvia Crowe d4a64fa8c2 feat: allow attempting auth methods in any order
While waveterm does not provide the control over which order to attempt
yet, it is possible to provide that information in the ssh_config. This
change allows that order to take precedence in a case where it is set.
2024-02-09 23:35:45 -08:00
Sylvia Crowe 618a08fe54 refactor: move logic for wave and config options
The logic for making decisions between details made available from wave
and details made available from ssh_config was spread out. This change
condenses it into one function for gathering those details and one for
picking between them.

It also adds a few new keywords but the logic for those hasn't been
implemented yet.
2024-02-09 22:44:55 -08:00
Sylvia Crowe b788a5e4af merge branch 'main' into ssh--auth-control
This only has a few bug fixes.
2024-02-09 15:07:32 -08:00
Sylvia Crowe 2e76556cac chore: remove old publicKey auth check
With the new callback in place, we no longer need this, so it has been
removed.
2024-02-09 13:30:07 -08:00
Sylvia Crowe d66287fcc6 chore: remove unnecessary print in sshclient.go 2024-02-08 22:45:52 -08:00
Sylvia Crowe f25892ba40 chore: remove unnecessary print in updatebus.go 2024-02-08 21:49:02 -08:00
Sylvia Crowe 37ff5f8c3e feat: parse multiple identity files in ssh
While this does not make it possible to discover multiple identity files
in every case, it does make it possible to parse them individually and
check for user input if it's required for each one.
2024-02-08 21:47:06 -08:00
16 changed files with 507 additions and 177 deletions
+10 -5
View File
@@ -5,24 +5,27 @@ If you're developing on Linux please use the [Linux Build Instructions](./build-
## Running the Development Version of Wave ## Running the Development Version of Wave
If you install the production version of Wave, you'll see a semi-transparent sidebar, and the data for Wave is stored in the directory ~/.waveterm. The development version has a blue sidebar and stores its data in ~/.waveterm-dev. This allows the production and development versions to be run simultaneously with no conflicts. If the dev database is corrupted by development bugs, or the schema changes in development it will not affect the production copy. If you install the production version of Wave, you'll see a semi-transparent gray sidebar, and the data for Wave is stored in the directory ~/.waveterm. The development version has a blue sidebar and stores its data in ~/.waveterm-dev. This allows the production and development versions to be run simultaneously with no conflicts. If the dev database is corrupted by development bugs, or the schema changes in development it will not affect the production copy.
## Prereqs and Tools ## Prereqs and Tools
Download and install Go (must be at least go 1.18): Download and install Go (must be at least go 1.18):
``` ```
brew install go brew install go
``` ```
Download and install ScriptHaus (to run the build commands): Download and install ScriptHaus (to run the build commands):
``` ```
brew tap scripthaus-dev/scripthaus brew tap scripthaus-dev/scripthaus
brew install scripthaus brew install scripthaus
``` ```
You also need a relatively modern nodejs with npm and yarn installed. You also need a relatively modern nodejs with npm and yarn installed.
* Node can be installed from [https://nodejs.org](https://nodejs.org).
* npm can install yarn using: - Node can be installed from [https://nodejs.org](https://nodejs.org).
- npm can install yarn using:
``` ```
npm install -g yarn npm install -g yarn
@@ -45,6 +48,7 @@ This builds the Golang backends for Wave. The binaries will put in waveshell/bi
## One-Time Setup ## One-Time Setup
Install modules (we use yarn): Install modules (we use yarn):
``` ```
yarn yarn
``` ```
@@ -66,13 +70,14 @@ scripthaus run webpack-watch
## Running the WaveTerm Dev Client ## Running the WaveTerm Dev Client
Now that webpack is running (and watching for file changes) we can finally run the WaveTerm Dev Client! To start the client run: Now that webpack is running (and watching for file changes) we can finally run the WaveTerm Dev Client! To start the client run:
``` ```
scripthaus run electron scripthaus run electron
``` ```
To kill the client, either exit the Electron App normally or just Ctrl-C the ```scripthaus run electron``` command. To kill the client, either exit the Electron App normally or just Ctrl-C the `scripthaus run electron` command.
Because we're running webpack in watch mode, any changes you make to the typescript will be automatically picked up by the client after a refresh. Note that I've disabled hot-reloading in the webpack config, so to pick up new changes you'll have to manually refresh the WaveTerm Client window. To do that use "Command-Shift-R" (Command-R is used internally by WaveTerm and will not force a refresh). Because we're running webpack in watch mode, any changes you make to the typescript will be automatically picked up by the client after a refresh. Note that I've disabled hot-reloading in the webpack config, so to pick up new changes you'll have to manually refresh the WaveTerm Client window. To do that use "Option-R" (Command-R is used internally by WaveTerm and will not force a refresh).
## Debugging the Dev Client ## Debugging the Dev Client
+21 -21
View File
@@ -1,9 +1,8 @@
<p align="center"> <p align="center">
<picture> <picture>
<source media="(prefers-color-scheme: dark)" srcset="./src/app/assets/wave-logo_horizontal-coloronblack.svg"> <source media="(prefers-color-scheme: dark)" srcset="./assets/wave-logo_horizontal-coloronblack.svg">
<source media="(prefers-color-scheme: light)" srcset="./src/app/assets/wave-logo_horizontal-coloronwhite.svg"> <source media="(prefers-color-scheme: light)" srcset="./assets/wave-logo_horizontal-coloronwhite.svg">
<img alt="Wave Terminal Logo" src="./src/app/assets/wave-logo_horizontal-coloronwhite.svg" width="352" height="59" style="max-width: 100%;"> <img alt="Wave Terminal Logo" src="./assets/wave-logo_horizontal-coloronwhite.svg" width="352" height="59" style="max-width: 100%;">
</picture> </picture>
<br/> <br/>
</p> </p>
@@ -14,11 +13,13 @@ A open-source, cross-platform, AI-integrated, modern terminal for seamless workf
Wave isn't just another terminal emulator; it's a rethink on how terminals are built. Wave combines command line with the power of the open web to help veteran CLI users and new developers alike. Wave isn't just another terminal emulator; it's a rethink on how terminals are built. Wave combines command line with the power of the open web to help veteran CLI users and new developers alike.
* Inline renderers to cut down on context switching. Render code, images, markdown, and CSV files without ever leaving the terminal. - Inline renderers to cut down on context switching. Render code, images, markdown, and CSV files without ever leaving the terminal.
* Persistent sessions that can restore state across network disconnections and reboots - Persistent sessions that can restore state across network disconnections and reboots
* Searchable contextual command history across all remote sessions (saved locally) - Searchable contextual command history across all remote sessions (saved locally)
* Workspaces, tabs, and command blocks to keep you organized - Workspaces, tabs, and command blocks to keep you organized
* AI Integration with ChatGPT (or ChatGPT compatible APIs) to help write commands and get answers inline - AI Integration with ChatGPT (or ChatGPT compatible APIs) to help write commands and get answers inline
![WaveTerm Screenshot](./assets/wave-screenshot.jpeg)
## Installation ## Installation
@@ -27,23 +28,24 @@ Wave Terminal works with MacOS and Linux (preliminary).
Install Wave Terminal from: [www.waveterm.dev/download](https://www.waveterm.dev/download) Install Wave Terminal from: [www.waveterm.dev/download](https://www.waveterm.dev/download)
Also available as a homebrew cask for MacOS: Also available as a homebrew cask for MacOS:
``` ```
brew install --cask wave brew install --cask wave
``` ```
## Links ## Links
* Homepage &mdash; https://www.waveterm.dev - Homepage &mdash; https://www.waveterm.dev
* Download Page &mdash; https://www.waveterm.dev/download - Download Page &mdash; https://www.waveterm.dev/download
* Documentation &mdash; https://docs.waveterm.dev/ - Documentation &mdash; https://docs.waveterm.dev/
* Blog &mdash; https://blog.waveterm.dev/ - Blog &mdash; https://blog.waveterm.dev/
* Quick Start Guide &mdash; https://docs.waveterm.dev/quickstart/ - Quick Start Guide &mdash; https://docs.waveterm.dev/quickstart/
* Discord Community &mdash; https://discord.gg/XfvZ334gwU - Discord Community &mdash; https://discord.gg/XfvZ334gwU
## Building from Source ## Building from Source
* [MacOS Build Instructions](./BUILD.md) - [MacOS Build Instructions](./BUILD.md)
* [Linux Build Instructions](./build-linux.md) - [Linux Build Instructions](./build-linux.md)
## Contributing ## Contributing
@@ -51,7 +53,5 @@ Wave uses Github Project for tracking issues.
Find more information in our [Contributions Guide](CONTRIBUTING.md), which includes: Find more information in our [Contributions Guide](CONTRIBUTING.md), which includes:
* [Ways to contribute](CONTRIBUTING.md#contributing-to-wave-terminal) - [Ways to contribute](CONTRIBUTING.md#contributing-to-wave-terminal)
* [Contribution guidelines](CONTRIBUTING.md#before-you-start) - [Contribution guidelines](CONTRIBUTING.md#before-you-start)
@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 27.8.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 1000 175" style="enable-background:new 0 0 1000 175;" xml:space="preserve">
<style type="text/css">
.st0{fill:url(#SVGID_1_);}
.st1{fill:url(#SVGID_00000091720229019135579030000007505974356775393451_);}
.st2{fill:#FFFFFF;}
.st3{fill:#58C142;}
.st4{fill:url(#SVGID_00000057844994756141690520000013392915125647203757_);}
.st5{fill:url(#SVGID_00000122684313424325124480000003010239515924857535_);}
.st6{fill:url(#SVGID_00000075883355893983351890000012882526639983753630_);}
.st7{fill:url(#SVGID_00000000207897583077398290000006853703841187474602_);}
.st8{fill:url(#SVGID_00000008108614519912429220000010116473681835506344_);}
.st9{fill:url(#SVGID_00000132058454267173112380000008245439973034275726_);}
.st10{fill:url(#SVGID_00000034804588790703597750000013460998990473107363_);}
.st11{fill:url(#SVGID_00000131349218605867433520000014539481291819864990_);}
.st12{fill:url(#SVGID_00000003071904852992499350000013371332860686737080_);}
.st13{fill:url(#SVGID_00000072278415138862792700000002042113538205077667_);}
</style>
<g>
<path class="st3" d="M339.7,20.6h26.5l30.9,100.8l33.5-100.8h22l33.5,100.8l31.2-100.8h26.5L497.9,156h-23.8L441.9,55.4L409.1,156
h-23.8L339.7,20.6z"/>
<path class="st3" d="M693.8,20.6V156h-25.9V133c-10.5,16.2-27.2,26.2-47.9,26.2c-35.9,0-62.3-29.3-62.3-71
c0-41.4,26.5-70.7,62.3-70.7c20.7,0,37.5,10,47.9,26.2v-23H693.8z M667.9,88.2c0-28.3-17.3-48.2-41.9-48.2
c-24.6,0-42.2,19.9-42.2,48.2c0,28.3,17.6,48.5,42.2,48.5C650.6,136.6,667.9,116.5,667.9,88.2z"/>
<path class="st3" d="M713.8,20.6h27.8l42.2,105.3l41.4-105.3h28L796,156h-24.4L713.8,20.6z"/>
<path class="st3" d="M970.7,122.2l15.2,16c-11.8,12.8-35.6,21-54.7,21c-37.5,0-69.9-29.6-69.9-71.2c0-40.6,30.6-70.5,68.1-70.5
c41.4,0,65.5,30.9,65.5,79.1H886.6c3.1,23.8,19.9,41.1,44,41.1C945.3,137.7,962.3,131.1,970.7,122.2z M886.9,77.5h82.8
c-2.6-21.7-16.2-38.5-39.8-38.5C908.1,38.9,890.8,54.1,886.9,77.5z"/>
</g>
<g>
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="5.2128" y1="58.9088" x2="240.4477" y2="58.9088">
<stop offset="0.1418" style="stop-color:#1F4D22"/>
<stop offset="0.8656" style="stop-color:#418D31"/>
</linearGradient>
<path class="st0" d="M83.6,80.8c-12.3,0-19.1,8-22.8,25.9l-55.6-8C12,40.1,34.8,11.7,79.9,11.7C113.3,11.7,146,37,162,37
c12.4,0,19.1-8.6,22.8-25.9l55.6,8c-6.2,58.7-29.6,87.1-74.7,87.1C131.8,106.1,100.3,80.8,83.6,80.8z"/>
</g>
<g>
<linearGradient id="SVGID_00000145018229965941010500000002424778669397199792_" gradientUnits="userSpaceOnUse" x1="20.9171" y1="116.0912" x2="256.1519" y2="116.0912">
<stop offset="0.2223" style="stop-color:#418D31"/>
<stop offset="0.7733" style="stop-color:#58C142"/>
</linearGradient>
<path style="fill:url(#SVGID_00000145018229965941010500000002424778669397199792_);" d="M99.3,138c-12.3,0-19.1,8-22.8,25.9
l-55.6-8c6.8-58.6,29.6-87.1,74.7-87.1c33.3,0,66.1,25.3,82.1,25.3c12.4,0,19.1-8.6,22.8-25.9l55.6,8
c-6.2,58.7-29.6,87.1-74.7,87.1C147.5,163.3,116,138,99.3,138z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 27.8.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 1000 175" style="enable-background:new 0 0 1000 175;" xml:space="preserve">
<style type="text/css">
.st0{fill:url(#SVGID_1_);}
.st1{fill:url(#SVGID_00000097467401116752621440000013265923851559160252_);}
.st2{fill:#FFFFFF;}
.st3{fill:#58C142;}
.st4{fill:url(#SVGID_00000026844058065691411450000001084298207851076753_);}
.st5{fill:url(#SVGID_00000140695609015829564530000017877493759276215708_);}
.st6{fill:url(#SVGID_00000172429467579424709090000016892278147863045285_);}
.st7{fill:url(#SVGID_00000111161844774090145740000012271107721736914070_);}
.st8{fill:url(#SVGID_00000159463458951391818050000007934417898368699555_);}
.st9{fill:url(#SVGID_00000114048161574266679140000001630328408055608234_);}
.st10{fill:url(#SVGID_00000181790832707237646570000012870231436607310721_);}
.st11{fill:url(#SVGID_00000182504551275047381220000018143622223532349093_);}
.st12{fill:url(#SVGID_00000182503296231706301150000015159284117431565225_);}
.st13{fill:url(#SVGID_00000157991892590648824540000004483708798912232327_);}
</style>
<g>
<path d="M339.7,20.6h26.5l30.9,100.8l33.5-100.8h22l33.5,100.8l31.2-100.8h26.5L497.9,156h-23.8L441.9,55.4L409.1,156h-23.8
L339.7,20.6z"/>
<path d="M693.8,20.6V156h-25.9V133c-10.5,16.2-27.2,26.2-47.9,26.2c-35.9,0-62.3-29.3-62.3-71c0-41.4,26.5-70.7,62.3-70.7
c20.7,0,37.5,10,47.9,26.2v-23H693.8z M667.9,88.2c0-28.3-17.3-48.2-41.9-48.2c-24.6,0-42.2,19.9-42.2,48.2
c0,28.3,17.6,48.5,42.2,48.5C650.6,136.6,667.9,116.5,667.9,88.2z"/>
<path d="M713.8,20.6h27.8l42.2,105.3l41.4-105.3h28L796,156h-24.4L713.8,20.6z"/>
<path d="M970.7,122.2l15.2,16c-11.8,12.8-35.6,21-54.7,21c-37.5,0-69.9-29.6-69.9-71.2c0-40.6,30.6-70.5,68.1-70.5
c41.4,0,65.5,30.9,65.5,79.1H886.6c3.1,23.8,19.9,41.1,44,41.1C945.3,137.7,962.3,131.1,970.7,122.2z M886.9,77.5h82.8
c-2.6-21.7-16.2-38.5-39.8-38.5C908.1,38.9,890.8,54.1,886.9,77.5z"/>
</g>
<g>
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="5.2128" y1="58.9088" x2="240.4477" y2="58.9088">
<stop offset="0.1418" style="stop-color:#1F4D22"/>
<stop offset="0.8656" style="stop-color:#418D31"/>
</linearGradient>
<path class="st0" d="M83.6,80.8c-12.3,0-19.1,8-22.8,25.9l-55.6-8C12,40.1,34.8,11.7,79.9,11.7C113.3,11.7,146,37,162,37
c12.4,0,19.1-8.6,22.8-25.9l55.6,8c-6.2,58.7-29.6,87.1-74.7,87.1C131.8,106.1,100.3,80.8,83.6,80.8z"/>
</g>
<g>
<linearGradient id="SVGID_00000055697211621202625680000016003686276294146176_" gradientUnits="userSpaceOnUse" x1="20.9171" y1="116.0912" x2="256.1519" y2="116.0912">
<stop offset="0.2223" style="stop-color:#418D31"/>
<stop offset="0.7733" style="stop-color:#58C142"/>
</linearGradient>
<path style="fill:url(#SVGID_00000055697211621202625680000016003686276294146176_);" d="M99.3,138c-12.3,0-19.1,8-22.8,25.9
l-55.6-8c6.8-58.6,29.6-87.1,74.7-87.1c33.3,0,66.1,25.3,82.1,25.3c12.4,0,19.1-8.6,22.8-25.9l55.6,8
c-6.2,58.7-29.6,87.1-74.7,87.1C147.5,163.3,116,138,99.3,138z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 306 KiB

+29
View File
@@ -0,0 +1,29 @@
<svg width="74" height="73" viewBox="0 0 74 73" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Icon Center Image Artwork">
<g id="Guide - Hide">
<rect x="1" width="44.7379" height="44.7379" fill="#EA33EC" fill-opacity="0.1"/>
<rect x="3.18447" y="2.18447" width="40.3689" height="40.3689" stroke="#EA33EC" stroke-opacity="0.09" stroke-width="4.36893"/>
</g>
<g id="wave-logo_appicon 1">
<path id="Vector" d="M73.8156 -0.808594H0.191406V72.8156H73.8156V-0.808594Z" fill="black"/>
<g id="Group">
<g id="Group_2">
<path id="Vector_2" d="M27.2969 34.6218C24.7516 34.6218 23.3523 36.2741 22.5859 39.9726L11.125 38.3132C12.5242 26.2194 17.2352 20.3624 26.5305 20.3624C33.407 20.3624 40.157 25.5866 43.4688 25.5866C46.0141 25.5866 47.4133 23.8007 48.1797 20.2358L59.6406 21.8882C58.368 33.9819 53.5305 39.846 44.2352 39.846C37.225 39.846 30.7352 34.6218 27.2969 34.6218Z" fill="url(#paint0_linear_1373_32879)"/>
</g>
<g id="Group_3">
<path id="Vector_3" d="M30.5312 46.4133C27.9859 46.4133 26.5867 48.0656 25.8203 51.7641L14.3594 50.1047C15.7586 38.0109 20.4695 32.1539 29.7648 32.1539C36.6414 32.1539 43.3914 37.3781 46.7031 37.3781C49.2484 37.3781 50.6477 35.5922 51.4141 32.0273L62.875 33.6797C61.6023 45.7734 56.7648 51.6375 47.4695 51.6375C40.4664 51.6375 33.9695 46.4133 30.5312 46.4133Z" fill="url(#paint1_linear_1373_32879)"/>
</g>
</g>
</g>
</g>
<defs>
<linearGradient id="paint0_linear_1373_32879" x1="11.1241" y1="30.1035" x2="59.6372" y2="30.1035" gradientUnits="userSpaceOnUse">
<stop offset="0.1418" stop-color="#1F4D22"/>
<stop offset="0.8656" stop-color="#418D31"/>
</linearGradient>
<linearGradient id="paint1_linear_1373_32879" x1="14.3628" y1="41.8964" x2="62.8759" y2="41.8964" gradientUnits="userSpaceOnUse">
<stop offset="0.2223" stop-color="#418D31"/>
<stop offset="0.7733" stop-color="#58C142"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

+42
View File
@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 27.8.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 600 400" style="enable-background:new 0 0 600 400;" xml:space="preserve">
<style type="text/css">
.st0{fill:url(#SVGID_1_);}
.st1{fill:url(#SVGID_00000112609801797065440120000009078644614516335013_);}
.st2{fill:#FFFFFF;}
.st3{fill:#58C142;}
.st4{fill:url(#SVGID_00000140705471064485508570000011374545427215048096_);}
.st5{fill:url(#SVGID_00000070096993167771140800000009230862614014512804_);}
.st6{fill:url(#SVGID_00000018927797001490499930000007907141601259141028_);}
.st7{fill:url(#SVGID_00000177467729764420216790000007105154932047940258_);}
.st8{fill:url(#SVGID_00000094609616810712664470000012347852020703354001_);}
.st9{fill:url(#SVGID_00000147904120639373224840000010231811793887454594_);}
.st10{fill:url(#SVGID_00000124156599372995293460000015010576141345595828_);}
.st11{fill:url(#SVGID_00000113335101732495131180000003771446430366466471_);}
.st12{fill:url(#SVGID_00000123421819715807053460000011277887084625979316_);}
.st13{fill:url(#SVGID_00000070797766435397124570000013813146128488798621_);}
</style>
<g>
<g>
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="7.3142" y1="133.3046" x2="556.0522" y2="133.3046">
<stop offset="0.1418" style="stop-color:#1F4D22"/>
<stop offset="0.8656" style="stop-color:#418D31"/>
</linearGradient>
<path class="st0" d="M190.2,184.4c-28.8,0-44.6,18.7-53.3,60.5L7.3,226.2C23.2,89.4,76.4,23.1,181.6,23.1
c77.8,0,154.1,59.1,191.6,59.1c28.8,0,44.7-20.2,53.3-60.5l129.6,18.7c-14.4,136.8-69.1,203.1-174.3,203.1
C302.6,243.5,229.1,184.4,190.2,184.4z"/>
</g>
<g>
<linearGradient id="SVGID_00000085247734223146048360000005883855174484422067_" gradientUnits="userSpaceOnUse" x1="43.9478" y1="266.6954" x2="592.6859" y2="266.6954">
<stop offset="0.2223" style="stop-color:#418D31"/>
<stop offset="0.7733" style="stop-color:#58C142"/>
</linearGradient>
<path style="fill:url(#SVGID_00000085247734223146048360000005883855174484422067_);" d="M226.8,317.8
c-28.8,0-44.6,18.7-53.3,60.5L43.9,359.6c15.8-136.8,69.1-203.1,174.3-203.1c77.8,0,154.1,59.1,191.6,59.1
c28.8,0,44.7-20.2,53.3-60.5l129.6,18.7c-14.4,136.8-69.1,203.1-174.3,203.1C339.2,376.9,265.7,317.8,226.8,317.8z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

+4 -4
View File
@@ -6,7 +6,7 @@ If you are working on a different Linux distribution, you may need to adapt some
## Running the Development Version of Wave ## Running the Development Version of Wave
If you install the production version of Wave, you'll see a semi-transparent sidebar, and the data for Wave is stored in the directory ~/prompt. The development version has a red/brown sidebar and stores its data in ~/prompt-dev. This allows the production and development versions to be run simultaneously with no conflicts. If the dev database is corrupted by development bugs, or the schema changes in development it will not affect the production copy. If you install the production version of Wave, you'll see a semi-transparent gray sidebar, and the data for Wave is stored in the directory ~/.waveterm. The development version has a blue sidebar and stores its data in ~/.waveterm-dev. This allows the production and development versions to be run simultaneously with no conflicts. If the dev database is corrupted by development bugs, or the schema changes in development it will not affect the production copy.
## Prereqs and Tools ## Prereqs and Tools
@@ -50,7 +50,6 @@ Now we can install yarn:
npm install -g yarn npm install -g yarn
``` ```
## Clone the Wave Repo ## Clone the Wave Repo
Move out of the `scripthaus` directory if you're still in it. Clone the wave repository into the directory that you'd like to use for development. Move out of the `scripthaus` directory if you're still in it. Clone the wave repository into the directory that you'd like to use for development.
@@ -62,6 +61,7 @@ git clone git@github.com:wavetermdev/waveterm.git
## One-Time Setup ## One-Time Setup
Install Wave modules (we use yarn): Install Wave modules (we use yarn):
``` ```
yarn yarn
``` ```
@@ -83,7 +83,6 @@ scripthaus run build-backend
This builds the Golang backends for Wave. The binaries will put in waveshell/bin and wavesrv/bin respectively. If you're working on a new plugin or other pure frontend changes to Wave, you won't need to rebuild these unless you pull new code from the Wave Repository. This builds the Golang backends for Wave. The binaries will put in waveshell/bin and wavesrv/bin respectively. If you're working on a new plugin or other pure frontend changes to Wave, you won't need to rebuild these unless you pull new code from the Wave Repository.
## Running WebPack ## Running WebPack
We use webpack to build both the React and Electron App Wrapper code. They are both run together using: We use webpack to build both the React and Electron App Wrapper code. They are both run together using:
@@ -95,11 +94,12 @@ scripthaus run webpack-watch
## Running the WaveTerm Dev Client ## Running the WaveTerm Dev Client
Now that webpack is running (and watching for file changes) we can finally run the WaveTerm Dev Client! To start the client run: Now that webpack is running (and watching for file changes) we can finally run the WaveTerm Dev Client! To start the client run:
``` ```
scripthaus run electron scripthaus run electron
``` ```
To kill the client, either exit the Electron App normally or just Ctrl-C the ```scripthaus run electron``` command. To kill the client, either exit the Electron App normally or just Ctrl-C the `scripthaus run electron` command.
Because we're running webpack in watch mode, any changes you make to the typescript will be automatically picked up by the client after a refresh. Note that I've disabled hot-reloading in the webpack config, so to pick up new changes you'll have to manually refresh the WaveTerm Client window. To do that use "Command-Shift-R" (Command-R is used internally by Wave and will not force a refresh). Because we're running webpack in watch mode, any changes you make to the typescript will be automatically picked up by the client after a refresh. Note that I've disabled hot-reloading in the webpack config, so to pick up new changes you'll have to manually refresh the WaveTerm Client window. To do that use "Command-Shift-R" (Command-R is used internally by Wave and will not force a refresh).
@@ -259,8 +259,10 @@ class ViewRemoteConnDetailModal extends React.Component<{}, {}> {
message = "Connected and ready to run commands."; message = "Connected and ready to run commands.";
} else if (remote.status == "connecting") { } else if (remote.status == "connecting") {
message = remote.waitingforpassword ? "Connecting, waiting for user-input..." : "Connecting..."; message = remote.waitingforpassword ? "Connecting, waiting for user-input..." : "Connecting...";
if (remote.countdownactive) {
let connectTimeout = remote.connecttimeout ?? 0; let connectTimeout = remote.connecttimeout ?? 0;
message = message + " (" + connectTimeout + "s)"; message = message + " (" + connectTimeout + "s)";
}
} else if (remote.status == "disconnected") { } else if (remote.status == "disconnected") {
message = "Disconnected"; message = "Disconnected";
} else if (remote.status == "error") { } else if (remote.status == "error") {
+2 -2
View File
@@ -176,7 +176,7 @@ function readAuthKey() {
} }
return authKeyStr.trim(); return authKeyStr.trim();
} }
const reloadAcceleratorKey = unamePlatform == "darwin" ? "Option+R" : "Super+R";
let menuTemplate: Electron.MenuItemConstructorOptions[] = [ let menuTemplate: Electron.MenuItemConstructorOptions[] = [
{ {
role: "appMenu", role: "appMenu",
@@ -202,7 +202,7 @@ let menuTemplate: Electron.MenuItemConstructorOptions[] = [
{ {
role: "viewMenu", role: "viewMenu",
submenu: [ submenu: [
{ role: "reload", accelerator: "Option+R" }, { role: "reload", accelerator: reloadAcceleratorKey },
{ role: "toggleDevTools" }, { role: "toggleDevTools" },
{ type: "separator" }, { type: "separator" },
{ role: "resetZoom" }, { role: "resetZoom" },
-41
View File
@@ -1,41 +0,0 @@
// Copyright 2024, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
type CmdContextType = {
screenid: string;
lineid: string;
linenum: number;
ts: number;
cmdstr: string;
festate: Record<string, string>;
remoteptr: RemotePtrType;
cmdstatus: string;
exitcode: number;
durationms: number;
};
type DataUpdateType = {
pos: number;
data: Uint8Array;
eof: boolean;
};
type PluginApi = {
loadPtyData(): Promise<ExtFile>;
onDataUpdate(datatype: string, cb: (datatype: string, update: DataUpdateType) => void): void;
onCmdDone(cb: (cmdContext: CmdContextType) => void): void;
releaseFocus(): void;
setLineState(lineState: LineStateType): void;
writeRemoteFile(filePath: string, data: Uint8Array): Promise<void>;
readRemoteFile(filePath: string): ExtFile;
streamRemoteFile(filePath: string, cb: (data: DataUpdateType) => void): void;
};
type PluginProps = {
cmdContext: CmdContextType;
pluginDecl: RendererPluginType;
rendererOpts: RendererOpts;
lineState: LineStateType;
focusState: "none" | "selected" | "focused";
api: PluginApi;
};
+23 -5
View File
@@ -601,6 +601,9 @@ func (msh *MShellProc) GetRemoteRuntimeState() RemoteRuntimeState {
if state.ConnectTimeout < 0 { if state.ConnectTimeout < 0 {
state.ConnectTimeout = 0 state.ConnectTimeout = 0
} }
state.CountdownActive = true
} else {
state.CountdownActive = false
} }
} }
vars := msh.Remote.StateVars vars := msh.Remote.StateVars
@@ -1318,6 +1321,9 @@ func (NewLauncher) Launch(msh *MShellProc, interactive bool) {
if remoteCopy.ConnectMode != sstore.ConnectModeManual && remoteCopy.SSHOpts.SSHPassword == "" && !interactive { if remoteCopy.ConnectMode != sstore.ConnectModeManual && remoteCopy.SSHOpts.SSHPassword == "" && !interactive {
sshOpts.BatchMode = true sshOpts.BatchMode = true
} }
var cproc *shexec.ClientProc
var initPk *packet.InitPacketType
if sshOpts.SSHHost == "" && remoteCopy.Local {
makeClientCtx, makeClientCancelFn := context.WithCancel(context.Background()) makeClientCtx, makeClientCancelFn := context.WithCancel(context.Background())
defer makeClientCancelFn() defer makeClientCancelFn()
msh.WithLock(func() { msh.WithLock(func() {
@@ -1330,11 +1336,7 @@ func (NewLauncher) Launch(msh *MShellProc, interactive bool) {
go msh.NotifyRemoteUpdate() go msh.NotifyRemoteUpdate()
}) })
go msh.watchClientDeadlineTime() go msh.watchClientDeadlineTime()
var cmdStr string cmdStr, err := MakeLocalMShellCommandStr(remoteCopy.IsSudo())
var cproc *shexec.ClientProc
var initPk *packet.InitPacketType
if sshOpts.SSHHost == "" && remoteCopy.Local {
cmdStr, err = MakeLocalMShellCommandStr(remoteCopy.IsSudo())
if err != nil { if err != nil {
msh.WriteToPtyBuffer("*error, cannot find local mshell binary: %v\n", err) msh.WriteToPtyBuffer("*error, cannot find local mshell binary: %v\n", err)
return return
@@ -1359,6 +1361,13 @@ func (NewLauncher) Launch(msh *MShellProc, interactive bool) {
} }
cproc, initPk, err = shexec.MakeClientProc(makeClientCtx, shexec.CmdWrap{Cmd: ecmd}) cproc, initPk, err = shexec.MakeClientProc(makeClientCtx, shexec.CmdWrap{Cmd: ecmd})
} else { } else {
msh.WithLock(func() {
msh.Err = nil
msh.ErrNoInitPk = false
msh.Status = StatusConnecting
msh.MakeClientDeadline = nil
go msh.NotifyRemoteUpdate()
})
var client *ssh.Client var client *ssh.Client
client, err = ConnectToClient(remoteCopy.SSHOpts) client, err = ConnectToClient(remoteCopy.SSHOpts)
if err != nil { if err != nil {
@@ -1375,6 +1384,15 @@ func (NewLauncher) Launch(msh *MShellProc, interactive bool) {
msh.setErrorStatus(statusErr) msh.setErrorStatus(statusErr)
return return
} }
makeClientCtx, makeClientCancelFn := context.WithCancel(context.Background())
defer makeClientCancelFn()
msh.WithLock(func() {
msh.MakeClientCancelFn = makeClientCancelFn
deadlineTime := time.Now().Add(RemoteConnectTimeout)
msh.MakeClientDeadline = &deadlineTime
go msh.NotifyRemoteUpdate()
})
go msh.watchClientDeadlineTime()
cproc, initPk, err = shexec.MakeClientProc(makeClientCtx, shexec.SessionWrap{Session: session, StartCmd: MakeServerRunOnlyCommandStr()}) cproc, initPk, err = shexec.MakeClientProc(makeClientCtx, shexec.SessionWrap{Session: session, StartCmd: MakeServerRunOnlyCommandStr()})
} }
// TODO check if initPk.State is not nil // TODO check if initPk.State is not nil
+224 -49
View File
@@ -6,10 +6,11 @@ package remote
import ( import (
"bytes" "bytes"
"context" "context"
"crypto/rand"
"crypto/rsa"
"crypto/x509" "crypto/x509"
"encoding/base64" "encoding/base64"
"fmt" "fmt"
"log"
"net" "net"
"os" "os"
"os/user" "os/user"
@@ -35,39 +36,94 @@ func (uice UserInputCancelError) Error() string {
return uice.Err.Error() return uice.Err.Error()
} }
func createPublicKeyAuth(identityFile string, passphrase string) (ssh.Signer, error) { // This exists to trick the ssh library into continuing to try
// different public keys even when the current key cannot be
// properly parsed
func createDummySigner() ([]ssh.Signer, error) {
dummyKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return nil, err
}
dummySigner, err := ssh.NewSignerFromKey(dummyKey)
if err != nil {
return nil, err
}
return []ssh.Signer{dummySigner}, nil
}
// This is a workaround to only process one identity file at a time,
// even if they have passphrases. It must be combined with retryable
// authentication to work properly
//
// Despite returning an array of signers, we only ever provide one since
// it allows proper user interaction in between attempts
//
// A significant number of errors end up returning dummy values as if
// they were successes. An error in this function prevents any other
// keys from being attempted. But if there's an error because of a dummy
// file, the library can still try again with a new key.
func createPublicKeyCallback(sshKeywords *SshKeywords, passphrase string) func() ([]ssh.Signer, error) {
identityFiles := make([]string, len(sshKeywords.IdentityFile))
copy(identityFiles, sshKeywords.IdentityFile)
identityFilesPtr := &identityFiles
return func() ([]ssh.Signer, error) {
if len(*identityFilesPtr) == 0 {
// skip this key and try with the next
return createDummySigner()
}
identityFile := (*identityFilesPtr)[0]
*identityFilesPtr = (*identityFilesPtr)[1:]
privateKey, err := os.ReadFile(base.ExpandHomeDir(identityFile)) privateKey, err := os.ReadFile(base.ExpandHomeDir(identityFile))
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to read ssh key file. err: %+v", err) // skip this key and try with the next
return createDummySigner()
} }
signer, err := ssh.ParsePrivateKey(privateKey) signer, err := ssh.ParsePrivateKey(privateKey)
if err == nil { if err == nil {
return signer, err return []ssh.Signer{signer}, err
} }
if _, ok := err.(*ssh.PassphraseMissingError); !ok { if _, ok := err.(*ssh.PassphraseMissingError); !ok {
return nil, fmt.Errorf("failed to parse private ssh key. err: %+v", err) // skip this key and try with the next
return createDummySigner()
} }
signer, err = ssh.ParsePrivateKeyWithPassphrase(privateKey, []byte(passphrase)) signer, err = ssh.ParsePrivateKeyWithPassphrase(privateKey, []byte(passphrase))
if err == nil { if err == nil {
return signer, err return []ssh.Signer{signer}, err
} }
if err != x509.IncorrectPasswordError && err.Error() != "bcrypt_pbkdf: empty password" { if err != x509.IncorrectPasswordError && err.Error() != "bcrypt_pbkdf: empty password" {
log.Printf("qwerty: %+v", err) // skip this key and try with the next
return nil, fmt.Errorf("failed to parse private ssh key. err: %+v", err) return createDummySigner()
} }
// batch mode deactivates user input
if sshKeywords.BatchMode {
// skip this key and try with the next
return createDummySigner()
}
request := &sstore.UserInputRequestType{ request := &sstore.UserInputRequestType{
ResponseType: "text", ResponseType: "text",
QueryText: fmt.Sprintf("Enter passphrase for the SSH key: %s", identityFile), QueryText: fmt.Sprintf("Enter passphrase for the SSH key: %s", identityFile),
Title: "Publickey Auth + Passphrase", Title: "Publickey Auth + Passphrase",
} }
ctx, cancelFn := context.WithTimeout(context.Background(), 15*time.Second) ctx, cancelFn := context.WithTimeout(context.Background(), 60*time.Second)
defer cancelFn() defer cancelFn()
response, err := sstore.MainBus.GetUserInput(ctx, request) response, err := sstore.MainBus.GetUserInput(ctx, request)
if err != nil { if err != nil {
// this is an error where we actually do want to stop
// trying keys
return nil, UserInputCancelError{Err: err} return nil, UserInputCancelError{Err: err}
} }
return ssh.ParsePrivateKeyWithPassphrase(privateKey, []byte(response.Text)) signer, err = ssh.ParsePrivateKeyWithPassphrase(privateKey, []byte(response.Text))
if err != nil {
// skip this key and try with the next
return createDummySigner()
}
return []ssh.Signer{signer}, err
}
} }
func createDefaultPasswordCallbackPrompt(password string) func() (secret string, err error) { func createDefaultPasswordCallbackPrompt(password string) func() (secret string, err error) {
@@ -83,7 +139,7 @@ func createInteractivePasswordCallbackPrompt() func() (secret string, err error)
return func() (secret string, err error) { return func() (secret string, err error) {
// limited to 15 seconds for some reason. this should be investigated more // limited to 15 seconds for some reason. this should be investigated more
// in the future // in the future
ctx, cancelFn := context.WithTimeout(context.Background(), 15*time.Second) ctx, cancelFn := context.WithTimeout(context.Background(), 60*time.Second)
defer cancelFn() defer cancelFn()
request := &sstore.UserInputRequestType{ request := &sstore.UserInputRequestType{
ResponseType: "text", ResponseType: "text",
@@ -143,7 +199,7 @@ func createInteractiveKbdInteractiveChallenge() func(name, instruction string, q
func promptChallengeQuestion(question string, echo bool) (answer string, err error) { func promptChallengeQuestion(question string, echo bool) (answer string, err error) {
// limited to 15 seconds for some reason. this should be investigated more // limited to 15 seconds for some reason. this should be investigated more
// in the future // in the future
ctx, cancelFn := context.WithTimeout(context.Background(), 15*time.Second) ctx, cancelFn := context.WithTimeout(context.Background(), 60*time.Second)
defer cancelFn() defer cancelFn()
request := &sstore.UserInputRequestType{ request := &sstore.UserInputRequestType{
ResponseType: "text", ResponseType: "text",
@@ -410,61 +466,180 @@ func createHostKeyCallback(opts *sstore.SSHOpts) (ssh.HostKeyCallback, error) {
} }
func ConnectToClient(opts *sstore.SSHOpts) (*ssh.Client, error) { func ConnectToClient(opts *sstore.SSHOpts) (*ssh.Client, error) {
ssh_config.ReloadConfigs() sshConfigKeywords, err := findSshConfigKeywords(opts.SSHHost)
configIdentity, _ := ssh_config.GetStrict(opts.SSHHost, "IdentityFile") if err != nil {
var identityFile string return nil, err
if opts.SSHIdentity != "" { }
identityFile = opts.SSHIdentity
sshKeywords, err := combineSshKeywords(opts, sshConfigKeywords)
if err != nil {
return nil, err
}
publicKeyCallback := ssh.PublicKeysCallback(createPublicKeyCallback(sshKeywords, opts.SSHPassword))
keyboardInteractive := ssh.KeyboardInteractive(createCombinedKbdInteractiveChallenge(opts.SSHPassword))
passwordCallback := ssh.PasswordCallback(createCombinedPasswordCallbackPrompt(opts.SSHPassword))
// batch mode turns off interactive input. this means the number of
// attemtps must drop to 1 with this setup
var attemptsAllowed int
if sshKeywords.BatchMode {
attemptsAllowed = 1
} else { } else {
identityFile = configIdentity attemptsAllowed = 2
}
// exclude gssapi-with-mic and hostbased until implemented
authMethodMap := map[string]ssh.AuthMethod{
"publickey": ssh.RetryableAuthMethod(publicKeyCallback, len(sshKeywords.IdentityFile)),
"keyboard-interactive": ssh.RetryableAuthMethod(keyboardInteractive, attemptsAllowed),
"password": ssh.RetryableAuthMethod(passwordCallback, attemptsAllowed),
}
authMethodActiveMap := map[string]bool{
"publickey": sshKeywords.PubkeyAuthentication,
"keyboard-interactive": sshKeywords.KbdInteractiveAuthentication,
"password": sshKeywords.PasswordAuthentication,
}
var authMethods []ssh.AuthMethod
for _, authMethodName := range sshKeywords.PreferredAuthentications {
authMethodActive, ok := authMethodActiveMap[authMethodName]
if !ok || !authMethodActive {
continue
}
authMethod, ok := authMethodMap[authMethodName]
if !ok {
continue
}
authMethods = append(authMethods, authMethod)
} }
hostKeyCallback, err := createHostKeyCallback(opts) hostKeyCallback, err := createHostKeyCallback(opts)
if err != nil { if err != nil {
return nil, err return nil, err
} }
var authMethods []ssh.AuthMethod
publicKeySigner, err := createPublicKeyAuth(identityFile, opts.SSHPassword)
if err == nil {
authMethods = append(authMethods, ssh.PublicKeys(publicKeySigner))
}
authMethods = append(authMethods, ssh.RetryableAuthMethod(ssh.KeyboardInteractive(createCombinedKbdInteractiveChallenge(opts.SSHPassword)), 2))
authMethods = append(authMethods, ssh.RetryableAuthMethod(ssh.PasswordCallback(createCombinedPasswordCallbackPrompt(opts.SSHPassword)), 2))
configUser, _ := ssh_config.GetStrict(opts.SSHHost, "User") clientConfig := &ssh.ClientConfig{
configHostName, _ := ssh_config.GetStrict(opts.SSHHost, "HostName") User: sshKeywords.User,
configPort, _ := ssh_config.GetStrict(opts.SSHHost, "Port") Auth: authMethods,
var username string HostKeyCallback: hostKeyCallback,
}
networkAddr := sshKeywords.HostName + ":" + sshKeywords.Port
return ssh.Dial("tcp", networkAddr, clientConfig)
}
type SshKeywords struct {
User string
HostName string
Port string
IdentityFile []string
BatchMode bool
PubkeyAuthentication bool
PasswordAuthentication bool
KbdInteractiveAuthentication bool
PreferredAuthentications []string
}
func combineSshKeywords(opts *sstore.SSHOpts, configKeywords *SshKeywords) (*SshKeywords, error) {
sshKeywords := &SshKeywords{}
if opts.SSHUser != "" { if opts.SSHUser != "" {
username = opts.SSHUser sshKeywords.User = opts.SSHUser
} else if configUser != "" { } else if configKeywords.User != "" {
username = configUser sshKeywords.User = configKeywords.User
} else { } else {
user, err := user.Current() user, err := user.Current()
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to get user for ssh: %+v", err) return nil, fmt.Errorf("failed to get user for ssh: %+v", err)
} }
username = user.Username sshKeywords.User = user.Username
} }
var hostName string
if configHostName != "" { // we have to check the host value because of the weird way
hostName = configHostName // we store the pattern as the hostname for imported remotes
if configKeywords.HostName != "" {
sshKeywords.HostName = configKeywords.HostName
} else { } else {
hostName = opts.SSHHost sshKeywords.HostName = opts.SSHHost
} }
clientConfig := &ssh.ClientConfig{
User: username,
Auth: authMethods,
HostKeyCallback: hostKeyCallback,
}
var port string
if opts.SSHPort != 0 && opts.SSHPort != 22 { if opts.SSHPort != 0 && opts.SSHPort != 22 {
port = strconv.Itoa(opts.SSHPort) sshKeywords.Port = strconv.Itoa(opts.SSHPort)
} else if configPort != "" && configPort != "22" { } else if configKeywords.Port != "" && configKeywords.Port != "22" {
port = configPort sshKeywords.Port = configKeywords.Port
} else { } else {
port = "22" sshKeywords.Port = "22"
} }
networkAddr := hostName + ":" + port
return ssh.Dial("tcp", networkAddr, clientConfig) sshKeywords.IdentityFile = []string{opts.SSHIdentity}
sshKeywords.IdentityFile = append(sshKeywords.IdentityFile, configKeywords.IdentityFile...)
// these are not officially supported in the waveterm frontend but can be configured
// in ssh config files
sshKeywords.BatchMode = configKeywords.BatchMode
sshKeywords.PubkeyAuthentication = configKeywords.PubkeyAuthentication
sshKeywords.PasswordAuthentication = configKeywords.PasswordAuthentication
sshKeywords.KbdInteractiveAuthentication = configKeywords.KbdInteractiveAuthentication
sshKeywords.PreferredAuthentications = configKeywords.PreferredAuthentications
return sshKeywords, nil
}
// note that a `var == "yes"` will default to false
// but `var != "no"` will default to true
// when given unexpected strings
func findSshConfigKeywords(hostPattern string) (*SshKeywords, error) {
ssh_config.ReloadConfigs()
sshKeywords := &SshKeywords{}
var err error
sshKeywords.User, err = ssh_config.GetStrict(hostPattern, "User")
if err != nil {
return nil, err
}
sshKeywords.HostName, err = ssh_config.GetStrict(hostPattern, "HostName")
if err != nil {
return nil, err
}
sshKeywords.Port, err = ssh_config.GetStrict(hostPattern, "Port")
if err != nil {
return nil, err
}
sshKeywords.IdentityFile = ssh_config.GetAll(hostPattern, "IdentityFile")
batchModeRaw, err := ssh_config.GetStrict(hostPattern, "BatchMode")
if err != nil {
return nil, err
}
sshKeywords.BatchMode = (strings.ToLower(batchModeRaw) == "yes")
// we currently do not support host-bound or unbound but will use yes when they are selected
pubkeyAuthenticationRaw, err := ssh_config.GetStrict(hostPattern, "PubkeyAuthentication")
if err != nil {
return nil, err
}
sshKeywords.PubkeyAuthentication = (strings.ToLower(pubkeyAuthenticationRaw) != "no")
passwordAuthenticationRaw, err := ssh_config.GetStrict(hostPattern, "PasswordAuthentication")
if err != nil {
return nil, err
}
sshKeywords.PasswordAuthentication = (strings.ToLower(passwordAuthenticationRaw) != "no")
kbdInteractiveAuthenticationRaw, err := ssh_config.GetStrict(hostPattern, "KbdInteractiveAuthentication")
if err != nil {
return nil, err
}
sshKeywords.KbdInteractiveAuthentication = (strings.ToLower(kbdInteractiveAuthenticationRaw) != "no")
// these are parsed as a single string and must be separated
// these are case sensitive in openssh so they are here too
preferredAuthenticationsRaw, err := ssh_config.GetStrict(hostPattern, "PreferredAuthentications")
sshKeywords.PreferredAuthentications = strings.Split(preferredAuthenticationsRaw, ",")
return sshKeywords, nil
} }
+1
View File
@@ -1004,6 +1004,7 @@ type RemoteRuntimeState struct {
DefaultFeState map[string]string `json:"defaultfestate"` DefaultFeState map[string]string `json:"defaultfestate"`
Status string `json:"status"` Status string `json:"status"`
ConnectTimeout int `json:"connecttimeout,omitempty"` ConnectTimeout int `json:"connecttimeout,omitempty"`
CountdownActive bool `json:"countdownactive"`
ErrorStr string `json:"errorstr,omitempty"` ErrorStr string `json:"errorstr,omitempty"`
InstallStatus string `json:"installstatus"` InstallStatus string `json:"installstatus"`
InstallErrorStr string `json:"installerrorstr,omitempty"` InstallErrorStr string `json:"installerrorstr,omitempty"`
-1
View File
@@ -228,7 +228,6 @@ func (bus *UpdateBus) GetUserInput(ctx context.Context, userInputRequest *UserIn
update := &ModelUpdate{} update := &ModelUpdate{}
AddUpdate(update, *userInputRequest) AddUpdate(update, *userInputRequest)
bus.SendUpdate(update) bus.SendUpdate(update)
log.Printf("test: %+v", userInputRequest)
var response *scpacket.UserInputResponsePacketType var response *scpacket.UserInputResponsePacketType
var err error var err error