Compare commits

...

8 Commits

Author SHA1 Message Date
Evan Simkowitz 523eafd85a save work 2024-02-20 14:50:50 -08:00
Evan Simkowitz fe3ffd1545 Use IsEmpty rather than nullcheck for scbus types
* Use IsEmpty rather than nullcheck for scbus types
2024-02-16 12:14:05 -08:00
Mike Sawka e2e71898c1 new wlog package to do distributed logging from waveshell back to wavesrv (#295) 2024-02-15 17:42:43 -08:00
Evan Simkowitz 07fa5bf9cb Remove verbose log from scbus (#296) 2024-02-15 16:50:03 -08:00
Evan Simkowitz 8acda3525b Break model update code out of sstore (#290)
* Break update code out of sstore

* add license disclaimers

* missed one

* add another

* fix regression in openai updates, remove unnecessary functions

* another copyright

* update casts

* fix issue with variadic updates

* remove logs

* remove log

* remove unnecessary log

* save work

* moved a bunch of stuff to scbus

* make modelupdate an object

* fix new screen not updating active screen

* add comment

* make updates into packet types

* different cast

* update comments, remove unused methods

* add one more comment

* add an IsEmpty() on model updates to prevent sending empty updates to client
2024-02-15 16:45:47 -08:00
Sylvie Crowe 158378a7ad Ssh Fixes and Improvements (#293)
* 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.

* chore: remove unnecessary print in updatebus.go

* chore: remove unnecessary print in sshclient.go

* chore: remove old publicKey auth check

With the new callback in place, we no longer need this, so it has been
removed.

* 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.

* 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.

* 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.

* 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.

* 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.

* 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.

* 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.

* fix: change size of dummy key to 2048

This fixes the CodeQL scan issue for using a weak key.
2024-02-15 15:58:50 -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
36 changed files with 1717 additions and 985 deletions
+16 -11
View File
@@ -1,28 +1,31 @@
# Build Instructions for Wave Terminal
These instructions are for setting up the build on MacOS.
These instructions are for setting up the build on MacOS.
If you're developing on Linux please use the [Linux Build Instructions](./build-linux.md).
## 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
Download and install Go (must be at least go 1.18):
```
brew install go
```
Download and install ScriptHaus (to run the build commands):
```
brew tap scripthaus-dev/scripthaus
brew install scripthaus
```
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
@@ -40,16 +43,17 @@ git clone git@github.com:wavetermdev/waveterm.git
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.
## One-Time Setup
Install modules (we use yarn):
```
yarn
```
Electron also requires specific builds of node_modules to work (because Electron embeds a specific node.js version that might not match your development node.js version). We use a special electron command to cross-compile those modules:
Electron also requires specific builds of node_modules to work (because Electron embeds a specific node.js version that might not match your development node.js version). We use a special electron command to cross-compile those modules:
```
scripthaus run electron-rebuild
@@ -57,7 +61,7 @@ scripthaus run electron-rebuild
## 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:
```
scripthaus run webpack-watch
@@ -65,15 +69,16 @@ scripthaus run webpack-watch
## 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
```
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
You can use the regular Chrome DevTools to debug the frontend application. You can open the DevTools using the keyboard shortcut `Cmd-Option-I`.
You can use the regular Chrome DevTools to debug the frontend application. You can open the DevTools using the keyboard shortcut `Cmd-Option-I`.
+24 -24
View File
@@ -1,24 +1,25 @@
<p align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="./src/app/assets/wave-logo_horizontal-coloronblack.svg">
<source media="(prefers-color-scheme: light)" srcset="./src/app/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%;">
<source media="(prefers-color-scheme: dark)" srcset="./assets/wave-logo_horizontal-coloronblack.svg">
<source media="(prefers-color-scheme: light)" srcset="./assets/wave-logo_horizontal-coloronwhite.svg">
<img alt="Wave Terminal Logo" src="./assets/wave-logo_horizontal-coloronwhite.svg" width="352" height="59" style="max-width: 100%;">
</picture>
<br/>
</p>
# Wave Terminal
# Wave Terminal
A open-source, cross-platform, AI-integrated, modern terminal for seamless workflows.
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.
* Persistent sessions that can restore state across network disconnections and reboots
* Searchable contextual command history across all remote sessions (saved locally)
* 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
- 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
- Searchable contextual command history across all remote sessions (saved locally)
- 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
![WaveTerm Screenshot](./assets/wave-screenshot.jpeg)
## Installation
@@ -27,31 +28,30 @@ Wave Terminal works with MacOS and Linux (preliminary).
Install Wave Terminal from: [www.waveterm.dev/download](https://www.waveterm.dev/download)
Also available as a homebrew cask for MacOS:
```
brew install --cask wave
```
## Links
* Homepage &mdash; https://www.waveterm.dev
* Download Page &mdash; https://www.waveterm.dev/download
* Documentation &mdash; https://docs.waveterm.dev/
* Blog &mdash; https://blog.waveterm.dev/
* Quick Start Guide &mdash; https://docs.waveterm.dev/quickstart/
* Discord Community &mdash; https://discord.gg/XfvZ334gwU
- Homepage &mdash; https://www.waveterm.dev
- Download Page &mdash; https://www.waveterm.dev/download
- Documentation &mdash; https://docs.waveterm.dev/
- Blog &mdash; https://blog.waveterm.dev/
- Quick Start Guide &mdash; https://docs.waveterm.dev/quickstart/
- Discord Community &mdash; https://discord.gg/XfvZ334gwU
## Building from Source
* [MacOS Build Instructions](./BUILD.md)
* [Linux Build Instructions](./build-linux.md)
- [MacOS Build Instructions](./BUILD.md)
- [Linux Build Instructions](./build-linux.md)
## Contributing
Wave uses Github Project for tracking issues.
Wave uses Github Project for tracking issues.
Find more information in our [Contributions Guide](CONTRIBUTING.md), which includes:
* [Ways to contribute](CONTRIBUTING.md#contributing-to-wave-terminal)
* [Contribution guidelines](CONTRIBUTING.md#before-you-start)
- [Ways to contribute](CONTRIBUTING.md#contributing-to-wave-terminal)
- [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

+14 -14
View File
@@ -1,16 +1,16 @@
# Build Instructions for Wave Terminal on Linux
These instructions are for setting up the build on Linux (Ubuntu).
These instructions are for setting up the build on Linux (Ubuntu).
If you're developing on MacOS please use the [MacOS Build Instructions](./BUILD.md).
If you are working on a different Linux distribution, you may need to adapt some of these instructions to fit your environment.
## 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
Download and install Go (must be at least go 1.18). We also need gcc installed to run a CGO build (for Golang).
Download and install Go (must be at least go 1.18). We also need gcc installed to run a CGO build (for Golang).
zip is required to build linux deployment packages (not required for running and debugging dev builds).
```
@@ -36,7 +36,7 @@ sudo cp scripthaus /usr/local/bin
## Install nodejs, npm, and yarn
We use [nvm](https://github.com/nvm-sh/nvm) to install nodejs on Linux (you can use an alternate installer if you wish). You must have a relatively recent version of node in order to build the terminal. Different distributions and shells will require different setup instructions. These instructions work for Ubuntu 22 using bash (will install node v20.8.1):
We use [nvm](https://github.com/nvm-sh/nvm) to install nodejs on Linux (you can use an alternate installer if you wish). You must have a relatively recent version of node in order to build the terminal. Different distributions and shells will require different setup instructions. These instructions work for Ubuntu 22 using bash (will install node v20.8.1):
```
wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash
@@ -50,10 +50,9 @@ Now we can install yarn:
npm install -g yarn
```
## 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.
```
git clone git@github.com:wavetermdev/waveterm.git
@@ -62,11 +61,12 @@ git clone git@github.com:wavetermdev/waveterm.git
## One-Time Setup
Install Wave modules (we use yarn):
```
yarn
```
Electron also requires specific builds of node_modules to work (because Electron embeds a specific node.js version that might not match your development node.js version). We use a special electron command to cross-compile those modules:
Electron also requires specific builds of node_modules to work (because Electron embeds a specific node.js version that might not match your development node.js version). We use a special electron command to cross-compile those modules:
```
scripthaus run electron-rebuild
@@ -81,12 +81,11 @@ cd waveterm
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
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:
```
scripthaus run webpack-watch
@@ -94,15 +93,16 @@ scripthaus run webpack-watch
## 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
```
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).
## Debugging the Dev Client
You can use the regular Chrome DevTools to debug the frontend application. You can open the DevTools using the keyboard shortcut `Cmd-Option-I`.
You can use the regular Chrome DevTools to debug the frontend application. You can open the DevTools using the keyboard shortcut `Cmd-Option-I`.
@@ -259,8 +259,10 @@ class ViewRemoteConnDetailModal extends React.Component<{}, {}> {
message = "Connected and ready to run commands.";
} else if (remote.status == "connecting") {
message = remote.waitingforpassword ? "Connecting, waiting for user-input..." : "Connecting...";
let connectTimeout = remote.connecttimeout ?? 0;
message = message + " (" + connectTimeout + "s)";
if (remote.countdownactive) {
let connectTimeout = remote.connecttimeout ?? 0;
message = message + " (" + connectTimeout + "s)";
}
} else if (remote.status == "disconnected") {
message = "Disconnected";
} else if (remote.status == "error") {
+2 -2
View File
@@ -176,7 +176,7 @@ function readAuthKey() {
}
return authKeyStr.trim();
}
const reloadAcceleratorKey = unamePlatform == "darwin" ? "Option+R" : "Super+R";
let menuTemplate: Electron.MenuItemConstructorOptions[] = [
{
role: "appMenu",
@@ -202,7 +202,7 @@ let menuTemplate: Electron.MenuItemConstructorOptions[] = [
{
role: "viewMenu",
submenu: [
{ role: "reload", accelerator: "Option+R" },
{ role: "reload", accelerator: reloadAcceleratorKey },
{ role: "toggleDevTools" },
{ type: "separator" },
{ role: "resetZoom" },
+137 -129
View File
@@ -715,7 +715,7 @@ class Model {
return this.ws.open.get();
}
runUpdate(genUpdate: UpdateMessage, interactive: boolean) {
runUpdate(genUpdate: UpdatePacket, interactive: boolean) {
mobx.action(() => {
const oldContext = this.getUIContext();
try {
@@ -727,8 +727,9 @@ class Model {
const newContext = this.getUIContext();
if (oldContext.sessionid != newContext.sessionid || oldContext.screenid != newContext.screenid) {
this.inputModel.resetInput();
if (!("ptydata64" in genUpdate)) {
const reversedGenUpdate = genUpdate.slice().reverse();
if (genUpdate.type == "model") {
const modelUpdate = genUpdate as ModelUpdatePacket;
const reversedGenUpdate = modelUpdate.data.slice().reverse();
const lastCmdLine = reversedGenUpdate.find((update) => "cmdline" in update);
if (lastCmdLine) {
// TODO a bit of a hack since this update gets applied in runUpdate_internal.
@@ -768,20 +769,12 @@ class Model {
}
updateActiveSession(sessionId: string): void {
const [oldActiveSessionId, oldActiveScreenId] = this.getActiveIds();
if (sessionId != null) {
const newSessionId = sessionId;
if (this.activeSessionId.get() != newSessionId) {
this.activeSessionId.set(newSessionId);
}
}
const [newActiveSessionId, newActiveScreenId] = this.getActiveIds();
if (oldActiveSessionId != newActiveSessionId || oldActiveScreenId != newActiveScreenId) {
this.activeMainView.set("session");
this.deactivateScreenLines();
this.ws.watchScreen(newActiveSessionId, newActiveScreenId);
}
}
updateScreenNumRunningCommands(numRunningCommandUpdates: ScreenNumRunningCommandsUpdateType[]) {
@@ -796,9 +789,9 @@ class Model {
}
}
runUpdate_internal(genUpdate: UpdateMessage, uiContext: UIContextType, interactive: boolean) {
if ("ptydata64" in genUpdate) {
const ptyMsg: PtyDataUpdateType = genUpdate;
runUpdate_internal(genUpdate: UpdatePacket, uiContext: UIContextType, interactive: boolean) {
if (genUpdate.type == "pty") {
const ptyMsg = genUpdate.data as PtyDataUpdateType;
if (isBlank(ptyMsg.remoteid)) {
// regular update
this.updatePtyData(ptyMsg);
@@ -807,125 +800,138 @@ class Model {
const ptyData = base64ToArray(ptyMsg.ptydata64);
this.remotesModel.receiveData(ptyMsg.remoteid, ptyMsg.ptypos, ptyData);
}
return;
}
let showedRemotesModal = false;
genUpdate.forEach((update) => {
if (update.connect != null) {
if (update.connect.screens != null) {
this.screenMap.clear();
this.updateScreens(update.connect.screens);
}
if (update.connect.sessions != null) {
this.sessionList.clear();
this.updateSessions(update.connect.sessions);
}
if (update.connect.remotes != null) {
this.remotes.clear();
this.updateRemotes(update.connect.remotes);
}
if (update.connect.activesessionid != null) {
this.updateActiveSession(update.connect.activesessionid);
}
if (update.connect.screennumrunningcommands != null) {
this.updateScreenNumRunningCommands(update.connect.screennumrunningcommands);
}
if (update.connect.screenstatusindicators != null) {
this.updateScreenStatusIndicators(update.connect.screenstatusindicators);
}
} else if (genUpdate.type == "model") {
const modelUpdateItems = genUpdate.data as ModelUpdateItemType[];
this.sessionListLoaded.set(true);
this.remotesLoaded.set(true);
} else if (update.screen != null) {
this.updateScreens([update.screen]);
} else if (update.session != null) {
this.updateSessions([update.session]);
} else if (update.activesessionid != null) {
this.updateActiveSession(update.activesessionid);
} else if (update.line != null) {
this.addLineCmd(update.line.line, update.line.cmd, interactive);
} else if (update.cmd != null) {
this.updateCmd(update.cmd);
} else if (update.screenlines != null) {
this.updateScreenLines(update.screenlines, false);
} else if (update.remote != null) {
this.updateRemotes([update.remote]);
// This code's purpose is to show view remote connection modal when a new connection is added
if (!showedRemotesModal && this.remotesModel.recentConnAddedState.get()) {
showedRemotesModal = true;
this.remotesModel.openReadModal(update.remote.remoteid);
}
} else if (update.mainview != null) {
switch (update.mainview.mainview) {
case "session":
this.activeMainView.set("session");
break;
case "history":
if (update.mainview.historyview != null) {
this.historyViewModel.showHistoryView(update.mainview.historyview);
} else {
console.warn("invalid historyview in update:", update.mainview);
}
break;
case "bookmarks":
if (update.mainview.bookmarksview != null) {
this.bookmarksModel.showBookmarksView(
update.mainview.bookmarksview?.bookmarks ?? [],
update.mainview.bookmarksview?.selectedbookmark
);
} else {
console.warn("invalid bookmarksview in update:", update.mainview);
}
break;
case "plugins":
this.pluginsModel.showPluginsView();
break;
default:
console.warn("invalid mainview in update:", update.mainview);
}
} else if (update.bookmarks != null) {
if (update.bookmarks.bookmarks != null) {
this.bookmarksModel.mergeBookmarks(update.bookmarks.bookmarks);
}
} else if (update.clientdata != null) {
this.setClientData(update.clientdata);
} else if (update.cmdline != null) {
this.inputModel.updateCmdLine(update.cmdline);
} else if (update.openaicmdinfochat != null) {
this.inputModel.setOpenAICmdInfoChat(update.openaicmdinfochat);
} else if (update.screenstatusindicator != null) {
this.updateScreenStatusIndicators([update.screenstatusindicator]);
} else if (update.screennumrunningcommands != null) {
this.updateScreenNumRunningCommands([update.screennumrunningcommands]);
} else if (update.userinputrequest != null) {
let userInputRequest: UserInputRequest = update.userinputrequest;
this.modalsModel.pushModal(appconst.USER_INPUT, userInputRequest);
} else if (interactive) {
if (update.info != null) {
const info: InfoType = update.info;
this.inputModel.flashInfoMsg(info, info.timeoutms);
} else if (update.remoteview != null) {
const rview: RemoteViewType = update.remoteview;
if (rview.remoteedit != null) {
this.remotesModel.openEditModal({ ...rview.remoteedit });
let showedRemotesModal = false;
const [oldActiveSessionId, oldActiveScreenId] = this.getActiveIds();
modelUpdateItems.forEach((update) => {
if (update.connect != null) {
if (update.connect.screens != null) {
this.screenMap.clear();
this.updateScreens(update.connect.screens);
}
} else if (update.alertmessage != null) {
const alertMessage: AlertMessageType = update.alertmessage;
this.showAlert(alertMessage);
} else if (update.history != null) {
if (
uiContext.sessionid == update.history.sessionid &&
uiContext.screenid == update.history.screenid
) {
this.inputModel.setHistoryInfo(update.history);
if (update.connect.sessions != null) {
this.sessionList.clear();
this.updateSessions(update.connect.sessions);
}
if (update.connect.remotes != null) {
this.remotes.clear();
this.updateRemotes(update.connect.remotes);
}
if (update.connect.activesessionid != null) {
this.updateActiveSession(update.connect.activesessionid);
}
if (update.connect.screennumrunningcommands != null) {
this.updateScreenNumRunningCommands(update.connect.screennumrunningcommands);
}
if (update.connect.screenstatusindicators != null) {
this.updateScreenStatusIndicators(update.connect.screenstatusindicators);
}
this.sessionListLoaded.set(true);
this.remotesLoaded.set(true);
} else if (update.screen != null) {
this.updateScreens([update.screen]);
} else if (update.session != null) {
this.updateSessions([update.session]);
} else if (update.activesessionid != null) {
this.updateActiveSession(update.activesessionid);
} else if (update.line != null) {
this.addLineCmd(update.line.line, update.line.cmd, interactive);
} else if (update.cmd != null) {
this.updateCmd(update.cmd);
} else if (update.screenlines != null) {
this.updateScreenLines(update.screenlines, false);
} else if (update.remote != null) {
this.updateRemotes([update.remote]);
// This code's purpose is to show view remote connection modal when a new connection is added
if (!showedRemotesModal && this.remotesModel.recentConnAddedState.get()) {
showedRemotesModal = true;
this.remotesModel.openReadModal(update.remote.remoteid);
}
} else if (update.mainview != null) {
switch (update.mainview.mainview) {
case "session":
this.activeMainView.set("session");
break;
case "history":
if (update.mainview.historyview != null) {
this.historyViewModel.showHistoryView(update.mainview.historyview);
} else {
console.warn("invalid historyview in update:", update.mainview);
}
break;
case "bookmarks":
if (update.mainview.bookmarksview != null) {
this.bookmarksModel.showBookmarksView(
update.mainview.bookmarksview?.bookmarks ?? [],
update.mainview.bookmarksview?.selectedbookmark
);
} else {
console.warn("invalid bookmarksview in update:", update.mainview);
}
break;
case "plugins":
this.pluginsModel.showPluginsView();
break;
default:
console.warn("invalid mainview in update:", update.mainview);
}
} else if (update.bookmarks != null) {
if (update.bookmarks.bookmarks != null) {
this.bookmarksModel.mergeBookmarks(update.bookmarks.bookmarks);
}
} else if (update.clientdata != null) {
this.setClientData(update.clientdata);
} else if (update.cmdline != null) {
this.inputModel.updateCmdLine(update.cmdline);
} else if (update.openaicmdinfochat != null) {
this.inputModel.setOpenAICmdInfoChat(update.openaicmdinfochat);
} else if (update.screenstatusindicator != null) {
this.updateScreenStatusIndicators([update.screenstatusindicator]);
} else if (update.screennumrunningcommands != null) {
this.updateScreenNumRunningCommands([update.screennumrunningcommands]);
} else if (update.userinputrequest != null) {
const userInputRequest: UserInputRequest = update.userinputrequest;
this.modalsModel.pushModal(appconst.USER_INPUT, userInputRequest);
} else if (interactive) {
if (update.info != null) {
const info: InfoType = update.info;
this.inputModel.flashInfoMsg(info, info.timeoutms);
} else if (update.remoteview != null) {
const rview: RemoteViewType = update.remoteview;
if (rview.remoteedit != null) {
this.remotesModel.openEditModal({ ...rview.remoteedit });
}
} else if (update.alertmessage != null) {
const alertMessage: AlertMessageType = update.alertmessage;
this.showAlert(alertMessage);
} else if (update.history != null) {
if (
uiContext.sessionid == update.history.sessionid &&
uiContext.screenid == update.history.screenid
) {
this.inputModel.setHistoryInfo(update.history);
}
} else if (this.isDev) {
console.log("did not match update", update);
}
} else if (this.isDev) {
console.log("did not match update", update);
}
} else if (this.isDev) {
console.log("did not match update", update);
});
// Check if the active session or screen has changed, and if so, watch the new screen
const [newActiveSessionId, newActiveScreenId] = this.getActiveIds();
if (oldActiveSessionId != newActiveSessionId || oldActiveScreenId != newActiveScreenId) {
this.activeMainView.set("session");
this.deactivateScreenLines();
this.ws.watchScreen(newActiveSessionId, newActiveScreenId);
}
});
} else {
console.warn("unknown update", genUpdate);
}
}
updateRemotes(remotes: RemoteType[]): void {
@@ -1064,11 +1070,13 @@ class Model {
this.handleCmdRestart(cmd);
}
isInfoUpdate(update: UpdateMessage): boolean {
if (update == null || "ptydata64" in update) {
isInfoUpdate(update: UpdatePacket): boolean {
if (update.type == "model") {
const modelUpdate = update as ModelUpdatePacket;
return modelUpdate.data.some((u) => u.info != null || u.history != null);
} else {
return false;
}
return update.some((u) => u.info != null || u.history != null);
}
getClientDataLoop(loopNum: number): void {
+15 -1
View File
@@ -338,6 +338,10 @@ declare global {
};
type ModelUpdateType = {
items?: ModelUpdateItemType[];
};
type ModelUpdateItemType = {
interactive: boolean;
session?: SessionDataType;
activesessionid?: string;
@@ -440,7 +444,17 @@ declare global {
showCut?: boolean;
};
type UpdateMessage = PtyDataUpdateType | ModelUpdateType[];
type ModelUpdatePacket = {
type: "model";
data: ModelUpdateItemType[];
};
type PtyDataUpdatePacket = {
type: "pty";
data: PtyDataUpdateType;
};
type UpdatePacket = ModelUpdatePacket | PtyDataUpdatePacket;
type RendererContext = {
screenId: string;
+4
View File
@@ -14,6 +14,7 @@ import (
"github.com/wavetermdev/waveterm/waveshell/pkg/packet"
"github.com/wavetermdev/waveterm/waveshell/pkg/server"
"github.com/wavetermdev/waveterm/waveshell/pkg/shexec"
"github.com/wavetermdev/waveterm/waveshell/pkg/wlog"
)
var BuildTime = "0"
@@ -39,6 +40,7 @@ func handleSingle() {
sender.Close()
sender.WaitForDone()
}()
wlog.LogConsumer = sender.SendLogPacket
initPacket := shexec.MakeInitPacket()
sender.SendPacket(initPacket)
if len(os.Args) >= 3 && os.Args[2] == "--version" {
@@ -133,11 +135,13 @@ func main() {
return
} else if firstArg == "--single" || firstArg == "--single-from-server" {
base.ProcessType = base.ProcessType_WaveShellSingle
wlog.GlobalSubsystem = base.ProcessType_WaveShellSingle
base.InitDebugLog("single")
handleSingle()
return
} else if firstArg == "--server" {
base.ProcessType = base.ProcessType_WaveShellServer
wlog.GlobalSubsystem = base.ProcessType_WaveShellServer
base.InitDebugLog("server")
rtnCode, err := server.RunServer()
if err != nil {
+2 -2
View File
@@ -42,8 +42,8 @@ const LogRcFileName = "debug.rcfile"
const (
ProcessType_Unknown = "unknown"
ProcessType_WaveSrv = "wavesrv"
ProcessType_WaveShellSingle = "waveshell-single"
ProcessType_WaveShellServer = "waveshell-server"
ProcessType_WaveShellSingle = "wsh-1"
ProcessType_WaveShellServer = "wsh-s"
)
// keys are sessionids (also the key RcFilesDirBaseName)
+14 -8
View File
@@ -14,9 +14,9 @@ import (
"os"
"reflect"
"sync"
"time"
"github.com/wavetermdev/waveterm/waveshell/pkg/base"
"github.com/wavetermdev/waveterm/waveshell/pkg/wlog"
)
// single : <init, >run, >cmddata, >cmddone, <cmdstart, <>data, <>dataack, <cmddone
@@ -556,11 +556,8 @@ func MakeRawPacket(val string) *RawPacketType {
}
type LogPacketType struct {
Type string `json:"type"`
Ts int64 `json:"ts"` // log timestamp
ReqId string `json:"reqid,omitempty"` // if this log line is related to an rpc request
ProcInfo string `json:"procinfo,omitempty"` // server/single
LogLine string `json:"logline"` // the logline data
Type string `json:"type"`
Entry wlog.LogEntry `json:"entry"`
}
func (*LogPacketType) GetType() string {
@@ -571,8 +568,8 @@ func (p *LogPacketType) String() string {
return "log"
}
func MakeLogPacket() *LogPacketType {
return &LogPacketType{Type: LogPacketStr, Ts: time.Now().UnixMilli()}
func MakeLogPacket(entry wlog.LogEntry) *LogPacketType {
return &LogPacketType{Type: LogPacketStr, Entry: entry}
}
type ShellStatePacketType struct {
@@ -975,6 +972,11 @@ type CommandPacketType interface {
GetCK() base.CommandKey
}
type ModelUpdatePacketType struct {
Type string `json:"type"`
Updates []any `json:"updates"`
}
func AsExtType(pk PacketType) string {
if rpcPacket, ok := pk.(RpcPacketType); ok {
return fmt.Sprintf("%s[%s]", rpcPacket.GetType(), rpcPacket.GetReqId())
@@ -1103,6 +1105,10 @@ func MakePacketSender(output io.Writer, errHandler func(*PacketSender, PacketTyp
return sender
}
func (sender *PacketSender) SendLogPacket(entry wlog.LogEntry) {
sender.SendPacket(MakeLogPacket(entry))
}
func (sender *PacketSender) goHandleError(pk PacketType, err error) {
sender.Lock.Lock()
defer sender.Lock.Unlock()
+7
View File
@@ -10,6 +10,8 @@ import (
"strconv"
"strings"
"sync"
"github.com/wavetermdev/waveterm/waveshell/pkg/wlog"
)
type PacketParser struct {
@@ -243,6 +245,11 @@ func MakePacketParser(input io.Reader, opts *PacketParserOpts) *PacketParser {
if pk.GetType() == PingPacketStr {
continue
}
if pk.GetType() == LogPacketStr {
logPk := pk.(*LogPacketType)
wlog.LogLogEntry(logPk.Entry)
continue
}
if parser.RpcHandler {
sent := parser.trySendRpcResponse(pk)
if sent {
+8 -6
View File
@@ -23,6 +23,7 @@ import (
"github.com/wavetermdev/waveterm/waveshell/pkg/shellapi"
"github.com/wavetermdev/waveterm/waveshell/pkg/shexec"
"github.com/wavetermdev/waveterm/waveshell/pkg/utilfn"
"github.com/wavetermdev/waveterm/waveshell/pkg/wlog"
)
const MaxFileDataPacketSize = 16 * 1024
@@ -741,6 +742,13 @@ func RunServer() (int, error) {
WriteErrorChOnce: &sync.Once{},
WriteFileContextMap: make(map[string]*WriteFileContext),
}
if debug {
packet.GlobalDebug = true
}
server.MainInput = packet.MakePacketParser(os.Stdin, nil)
server.Sender = packet.MakePacketSender(os.Stdout, server.packetSenderErrorHandler)
defer server.Close()
wlog.LogConsumer = server.Sender.SendLogPacket
go func() {
for {
if server.checkDone() {
@@ -750,12 +758,6 @@ func RunServer() (int, error) {
server.cleanWriteFileContexts()
}
}()
if debug {
packet.GlobalDebug = true
}
server.MainInput = packet.MakePacketParser(os.Stdin, nil)
server.Sender = packet.MakePacketSender(os.Stdout, server.packetSenderErrorHandler)
defer server.Close()
var err error
initPacket, err := shexec.MakeServerInitPacket()
if err != nil {
-1
View File
@@ -1091,7 +1091,6 @@ func (cmd *ShExecType) DetachedWait(startPacket *packet.CmdStartPacketType) {
cmd.DetachedOutput.SendPacket(donePacket)
<-ptyCopyDone
cmd.Close()
return
}
func RunCommandDetached(pk *packet.RunPacketType, sender *packet.PacketSender) (*ShExecType, *packet.CmdStartPacketType, error) {
+77
View File
@@ -0,0 +1,77 @@
// Copyright 2024, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
// implements distributed logging for waveshell processes
package wlog
import (
"fmt"
"log"
)
// wlog will send logs back to the controlling wavesrv process
// note that these logs end up on your local machine where the main Wave Terminal process is running
// wlog has no ability to send logs to a cloud service or Command Line Inc servers
// this code is written a bit strange (with globals getting set from other packages)
// because we want no dependencies so any package (including base) can use wlog
// this should match base.ProcessType (set by main)
var GlobalSubsystem string
// if not set, Logf is a no-op. will be set by main to hook up to
// the main packet.PacketSender
var LogConsumer func(LogEntry)
type LogEntry struct {
LogLine string `json:"logline"`
ReqId string `json:"reqid"`
SubSystem string `json:"subsystem"`
}
func LogLogEntry(entry LogEntry) {
if LogConsumer == nil {
return
}
LogConsumer(entry)
}
// log with a request id (if related to an rpc request)
func LogfRpc(reqId string, format string, args ...interface{}) {
if LogConsumer == nil {
return
}
logEntry := LogEntry{
LogLine: fmt.Sprintf(format, args...),
ReqId: reqId,
SubSystem: GlobalSubsystem,
}
LogConsumer(logEntry)
}
func LogfSS(subsystem string, format string, args ...interface{}) {
if LogConsumer == nil {
return
}
logEntry := LogEntry{
LogLine: fmt.Sprintf(format, args...),
ReqId: "",
SubSystem: subsystem,
}
LogConsumer(logEntry)
}
func Logf(format string, args ...interface{}) {
LogfSS(GlobalSubsystem, format, args...)
}
func LogWithLogger(entry LogEntry) {
if entry.SubSystem == "" {
entry.SubSystem = "unknown"
}
if entry.ReqId != "" {
log.Printf("[%s] reqid=%s %s", entry.SubSystem, entry.ReqId, entry.LogLine)
} else {
log.Printf("[%s] %s", entry.SubSystem, entry.LogLine)
}
}

Some files were not shown because too many files have changed in this diff Show More