mirror of
https://github.com/wavetermdev/backup.git
synced 2026-04-22 15:26:58 -07:00
Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a2a5e5afdb | |||
| 9014660dc3 | |||
| 0d10632c62 | |||
| 8f7f259132 | |||
| 33f2532ae8 | |||
| 0505205df5 | |||
| 894e6c152e | |||
| 2c3dab6ee9 | |||
| 595102fefa | |||
| 63526fbe8a | |||
| f2faa6b735 | |||
| b0f8de5331 | |||
| 3c81eae8af | |||
| f8269b7495 | |||
| fbf4915210 | |||
| d0c0a7fd96 | |||
| 3d72e97831 | |||
| d54a61658f | |||
| e38c5bf16a | |||
| 56f44151df | |||
| c46c2a926c | |||
| a3de9768f2 | |||
| cd871a0c6c |
@@ -16,6 +16,10 @@ jobs:
|
||||
arch: "amd64"
|
||||
runner: "ubuntu-latest"
|
||||
scripthaus: "build-package-linux"
|
||||
- platform: "linux"
|
||||
arch: "arm64"
|
||||
runner: ubuntu-24.04-arm64-16core
|
||||
scripthaus: "build-package-linux"
|
||||
runs-on: ${{ matrix.runner }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -28,6 +32,8 @@ jobs:
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install --no-install-recommends -y libarchive-tools libopenjp2-tools rpm
|
||||
- name: Install FPM # The version of FPM that comes bundled with electron-builder doesn't include a Linux ARM target. Installing Gems onto the runner is super quick so we'll just do this for all targets.
|
||||
run: sudo gem install fpm
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{env.GO_VERSION}}
|
||||
@@ -58,6 +64,7 @@ jobs:
|
||||
run: scripthaus run ${{ matrix.scripthaus }}
|
||||
env:
|
||||
GOARCH: ${{ matrix.arch }}
|
||||
USE_SYSTEM_FPM: true # Ensure that the installed version of FPM is used rather than the bundled one.
|
||||
CSC_LINK: ${{ matrix.platform == 'darwin' && secrets.PROD_MACOS_CERTIFICATE}}
|
||||
CSC_KEY_PASSWORD: ${{ matrix.platform == 'darwin' && secrets.PROD_MACOS_CERTIFICATE_PWD }}
|
||||
APPLE_ID: ${{ matrix.platform == 'darwin' && secrets.PROD_MACOS_NOTARIZATION_APPLE_ID }}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name: TestDriver.ai Regression Testing
|
||||
name: TestDriver.ai Regression Testing - Waveterm
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
@@ -14,53 +14,38 @@ permissions:
|
||||
contents: read # To allow the action to read repository contents
|
||||
pull-requests: write # To allow the action to create/update pull request comments
|
||||
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: TestDriver
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: dashcamio/testdriver@main
|
||||
id: testdriver
|
||||
with:
|
||||
version: v2.12.12
|
||||
prerun: |
|
||||
rm ~/Desktop/WITH-LOVE-FROM-AMERICA.txt
|
||||
cd ~/actions-runner/_work/testdriver/testdriver/
|
||||
brew install go
|
||||
brew tap scripthaus-dev/scripthaus
|
||||
brew install corepack
|
||||
brew install scripthaus
|
||||
corepack enable
|
||||
yarn install
|
||||
scripthaus run build-backend
|
||||
echo "Yarn"
|
||||
yarn
|
||||
echo "Rebuild"
|
||||
scripthaus run electron-rebuild
|
||||
echo "Webpack"
|
||||
scripthaus run webpack-build
|
||||
echo "Starting Electron"
|
||||
scripthaus run electron 1>/dev/null 2>&1 &
|
||||
echo "Electron Done"
|
||||
exit
|
||||
prompt: |
|
||||
1. wait 10 seconds
|
||||
1. click "Get Started"
|
||||
1. validate that overlapping text does not appear in the application
|
||||
1. focus the Wave input with the keyboard shorcut Command + I
|
||||
1. type 'ls' into the input
|
||||
1. press return
|
||||
1. validate Wave shows the result of 'ls'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- uses: peter-evans/create-or-update-comment@v4
|
||||
if: ${{always()}}
|
||||
with:
|
||||
issue-number: ${{ github.event.pull_request.number }}
|
||||
body: |
|
||||
## TestDriver Summary
|
||||
${{ steps.testdriver.outputs.markdown }}
|
||||
${{ steps.testdriver.outputs.summary }}
|
||||
reactions: |
|
||||
+1
|
||||
-1
|
||||
test:
|
||||
name: "TestDriver"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: dashcamio/testdriver@main
|
||||
id: testdriver
|
||||
with:
|
||||
version: v3.9.0
|
||||
key: ${{secrets.DASHCAM_API}}
|
||||
os: mac
|
||||
prerun: |
|
||||
cd ~/actions-runner/_work/testdriver/testdriver/
|
||||
brew install go
|
||||
brew tap scripthaus-dev/scripthaus
|
||||
brew install corepack
|
||||
brew install scripthaus
|
||||
corepack enable
|
||||
yarn install
|
||||
scripthaus run build-backend
|
||||
echo "Yarn"
|
||||
yarn
|
||||
echo "Rebuild"
|
||||
scripthaus run electron-rebuild
|
||||
echo "Webpack"
|
||||
scripthaus run webpack-build
|
||||
echo "Starting Electron"
|
||||
scripthaus run electron 1>/dev/null 2>&1 &
|
||||
echo "Electron Done"
|
||||
cd /Users/ec2-user/Downloads/td/
|
||||
npm rebuild
|
||||
exit
|
||||
prompt: |
|
||||
1. /run /Users/ec2-user/actions-runner/_work/testdriver/testdriver/.testdriver/wave1.yml
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
rm ~/Desktop/WITH-LOVE-FROM-AMERICA.txt
|
||||
cd ~/actions-runner/_work/testdriver/testdriver/
|
||||
brew install go
|
||||
brew tap scripthaus-dev/scripthaus
|
||||
brew install scripthaus
|
||||
corepack enable
|
||||
yarn install
|
||||
scripthaus run build-backend
|
||||
echo "Yarn"
|
||||
yarn
|
||||
echo "Rebuild"
|
||||
scripthaus run electron-rebuild
|
||||
echo "Webpack"
|
||||
scripthaus run webpack-build
|
||||
echo "Starting Electron"
|
||||
scripthaus run electron 1>/dev/null 2>&1 &
|
||||
echo "Electron Done"
|
||||
exit
|
||||
@@ -0,0 +1,37 @@
|
||||
version: 3.8.0
|
||||
steps:
|
||||
- prompt: "Focus electron"
|
||||
commands:
|
||||
- command: focus-application
|
||||
name: Electron
|
||||
- command: hover-text
|
||||
description: Get started CTA
|
||||
text: Get Started
|
||||
action: click
|
||||
- command: hover-text
|
||||
description: Settings button
|
||||
text: Settings
|
||||
action: click
|
||||
- command: hover-text
|
||||
description: font size 13
|
||||
text: 13px
|
||||
action: click
|
||||
- command: hover-text
|
||||
description: font size 12
|
||||
text: 12px
|
||||
action: click
|
||||
- command: hover-text
|
||||
description: theme selector
|
||||
text: Dark
|
||||
action: click
|
||||
- command: hover-text
|
||||
description: theme color white
|
||||
text: Light
|
||||
action: click
|
||||
- command: hover-text
|
||||
description: workspace
|
||||
text: workspace-1
|
||||
action: click
|
||||
- command: assert
|
||||
expect: the terminal is white
|
||||
|
||||
+13
-10
@@ -34,20 +34,23 @@ You'll now have to move the built `scripthaus` binary to a directory in your pat
|
||||
sudo cp scripthaus /usr/local/bin
|
||||
```
|
||||
|
||||
## Install nodejs, npm, and yarn
|
||||
## Install nodejs 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):
|
||||
You also need a relatively modern nodejs with npm and yarn installed.
|
||||
|
||||
```
|
||||
wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash
|
||||
source ~/.bashrc
|
||||
nvm install v20.8.1
|
||||
```
|
||||
Node can be installed from [https://nodejs.org](https://nodejs.org).
|
||||
|
||||
Now we can install yarn:
|
||||
We use Yarn Modern to manage our packages. The recommended way to install Yarn Modern is using Corepack, a new utility shipped by NodeJS that lets you manage your package manager versioning as you would any packages.
|
||||
|
||||
```
|
||||
npm install -g yarn
|
||||
If you installed NodeJS from the official feed (via the website or using NVM), this should come preinstalled. If you use Homebrew or some other feed, you may need to manually install Corepack using `npm install -g corepack`.
|
||||
|
||||
For more information on Corepack, check out [this link](https://yarnpkg.com/corepack).
|
||||
|
||||
Once you've verified that you have Corepack installed, run the following script to set up Yarn for the repository:
|
||||
|
||||
```sh
|
||||
corepack enable
|
||||
yarn install
|
||||
```
|
||||
|
||||
## Clone the Wave Repo
|
||||
|
||||
+14
@@ -134,14 +134,28 @@ github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaD
|
||||
gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b/go.mod h1:T3BPAOm2cqquPa0MKWeNkmOM5RQsRhkrwMWonFMN7fE=
|
||||
go.mongodb.org/mongo-driver v1.7.5/go.mod h1:VXEWRZ6URJIkUq2SCAyapmhH0ZLRBP+FT4xhp5Zvxng=
|
||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/exp v0.0.0-20230315142452-642cacee5cc0/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/oauth2 v0.1.0/go.mod h1:G9FE4dLTsbXUu90h/Pf85g4w1D+SSAgR+q46nJZ8M4A=
|
||||
golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
||||
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||
golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
|
||||
google.golang.org/api v0.106.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY=
|
||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
|
||||
+52
-52
@@ -18,95 +18,95 @@
|
||||
"appId": "dev.commandline.waveterm"
|
||||
},
|
||||
"dependencies": {
|
||||
"@lexical/react": "^0.14.3",
|
||||
"@monaco-editor/react": "^4.5.1",
|
||||
"@lexical/react": "^0.17.0",
|
||||
"@monaco-editor/react": "^4.6.0",
|
||||
"@table-nav/core": "^0.0.7",
|
||||
"@table-nav/react": "^0.0.7",
|
||||
"@tanstack/match-sorter-utils": "^8.8.4",
|
||||
"@tanstack/react-table": "^8.10.3",
|
||||
"@withfig/autocomplete": "^2.652.3",
|
||||
"@tanstack/match-sorter-utils": "^8.19.4",
|
||||
"@tanstack/react-table": "^8.20.1",
|
||||
"@withfig/autocomplete": "^2.672.0",
|
||||
"autobind-decorator": "^2.4.0",
|
||||
"base64-js": "^1.5.1",
|
||||
"clsx": "^2.1.1",
|
||||
"dayjs": "^1.11.3",
|
||||
"dompurify": "^3.0.2",
|
||||
"electron-squirrel-startup": "^1.0.0",
|
||||
"electron-updater": "^6.1.8",
|
||||
"framer-motion": "^10.16.16",
|
||||
"dayjs": "^1.11.12",
|
||||
"dompurify": "^3.1.6",
|
||||
"electron-squirrel-startup": "^1.0.1",
|
||||
"electron-updater": "^6.3.2",
|
||||
"framer-motion": "^10.18.0",
|
||||
"lexical": "0.14.5",
|
||||
"mobx": "6.12",
|
||||
"mobx-react": "^7.5.0",
|
||||
"mobx": "6.12.5",
|
||||
"mobx-react": "^7.6.0",
|
||||
"monaco-editor": "0.48.0",
|
||||
"mustache": "^4.2.0",
|
||||
"node-fetch": "^3.2.10",
|
||||
"overlayscrollbars": "^2.6.1",
|
||||
"overlayscrollbars-react": "^0.5.5",
|
||||
"node-fetch": "^3.3.2",
|
||||
"overlayscrollbars": "^2.10.0",
|
||||
"overlayscrollbars-react": "^0.5.6",
|
||||
"papaparse": "^5.4.1",
|
||||
"react": "^18.1.0",
|
||||
"react-dom": "^18.1.0",
|
||||
"react-markdown": "^9.0.0",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-markdown": "^9.0.1",
|
||||
"remark": "^15.0.1",
|
||||
"remark-gfm": "^4.0.0",
|
||||
"sprintf-js": "^1.1.2",
|
||||
"throttle-debounce": "^5.0.0",
|
||||
"sprintf-js": "^1.1.3",
|
||||
"throttle-debounce": "^5.0.2",
|
||||
"tinycolor2": "^1.6.0",
|
||||
"tsx-control-statements": "^5.1.1",
|
||||
"uuid": "^9.0.0",
|
||||
"winston": "^3.8.2",
|
||||
"uuid": "^9.0.1",
|
||||
"winston": "^3.13.1",
|
||||
"xterm": "^5.3.0",
|
||||
"xterm-addon-serialize": "^0.11.0",
|
||||
"xterm-addon-web-links": "^0.9.0",
|
||||
"xterm-addon-webgl": "^0.16.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.17.10",
|
||||
"@babel/core": "^7.18.2",
|
||||
"@babel/cli": "^7.24.8",
|
||||
"@babel/core": "^7.25.2",
|
||||
"@babel/plugin-proposal-class-properties": "^7.18.6",
|
||||
"@babel/plugin-proposal-decorators": "^7.18.2",
|
||||
"@babel/plugin-proposal-decorators": "^7.24.7",
|
||||
"@babel/plugin-proposal-private-methods": "^7.18.6",
|
||||
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
|
||||
"@babel/plugin-transform-react-jsx": "^7.17.12",
|
||||
"@babel/plugin-transform-runtime": "^7.23.4",
|
||||
"@babel/preset-env": "^7.18.2",
|
||||
"@babel/preset-react": "^7.23.3",
|
||||
"@babel/preset-typescript": "^7.17.12",
|
||||
"@babel/plugin-transform-react-jsx": "^7.25.2",
|
||||
"@babel/plugin-transform-runtime": "^7.24.7",
|
||||
"@babel/preset-env": "^7.25.3",
|
||||
"@babel/preset-react": "^7.24.7",
|
||||
"@babel/preset-typescript": "^7.24.7",
|
||||
"@electron/rebuild": "^3.6.0",
|
||||
"@svgr/webpack": "^8.1.0",
|
||||
"@types/electron": "^1.6.10",
|
||||
"@types/node": "^20.11.0",
|
||||
"@types/papaparse": "^5.3.10",
|
||||
"@types/react": "^18.0.12",
|
||||
"@types/semver": "^7.5.6",
|
||||
"@types/sprintf-js": "^1.1.3",
|
||||
"@types/throttle-debounce": "^5.0.1",
|
||||
"@types/tinycolor2": "^1",
|
||||
"@types/uuid": "^9.0.7",
|
||||
"@types/webpack-env": "^1.18.3",
|
||||
"@withfig/autocomplete-types": "^1.30.0",
|
||||
"@types/node": "^22.1.0",
|
||||
"@types/papaparse": "^5.3.14",
|
||||
"@types/react": "^18.3.3",
|
||||
"@types/semver": "^7.5.8",
|
||||
"@types/sprintf-js": "^1.1.4",
|
||||
"@types/throttle-debounce": "^5.0.2",
|
||||
"@types/tinycolor2": "^1.4.6",
|
||||
"@types/uuid": "^9.0.8",
|
||||
"@types/webpack-env": "^1.18.5",
|
||||
"@withfig/autocomplete-types": "^1.31.0",
|
||||
"babel-loader": "^9.1.3",
|
||||
"babel-plugin-jsx-control-statements": "^4.1.2",
|
||||
"copy-webpack-plugin": "^12.0.0",
|
||||
"css-loader": "^7.1.0",
|
||||
"electron": "^30.0.8",
|
||||
"copy-webpack-plugin": "^12.0.2",
|
||||
"css-loader": "^7.1.2",
|
||||
"electron": "^31.3.1",
|
||||
"electron-builder": "^24.13.3",
|
||||
"electron-builder-squirrel-windows": "^24.13.3",
|
||||
"electron-builder-squirrel-windows": "25.0.0-alpha.10",
|
||||
"file-loader": "^6.2.0",
|
||||
"http-server": "^14.1.1",
|
||||
"less": "^4.1.2",
|
||||
"less-loader": "^12.0.0",
|
||||
"less": "^4.2.0",
|
||||
"less-loader": "^12.2.0",
|
||||
"lodash-webpack-plugin": "^0.11.6",
|
||||
"mini-css-extract-plugin": "^2.6.0",
|
||||
"mini-css-extract-plugin": "^2.9.0",
|
||||
"prettier": "^2.8.8",
|
||||
"raw-loader": "^4.0.2",
|
||||
"react-split-it": "^2.0.0",
|
||||
"style-loader": "4.0.0",
|
||||
"typescript": "^5.0.0",
|
||||
"webpack": "^5.73.0",
|
||||
"webpack-bundle-analyzer": "^4.10.1",
|
||||
"typescript": "^5.5.4",
|
||||
"webpack": "^5.94.0",
|
||||
"webpack-bundle-analyzer": "^4.10.2",
|
||||
"webpack-cli": "^5.1.4",
|
||||
"webpack-dev-server": "^5.0.4",
|
||||
"webpack-merge": "^5.8.0",
|
||||
"yaml": "^2.4.0"
|
||||
"webpack-merge": "^5.10.0",
|
||||
"yaml": "^2.5.0"
|
||||
},
|
||||
"scripts": {
|
||||
"postinstall": "electron-builder install-app-deps"
|
||||
|
||||
+2
-2
@@ -76,7 +76,7 @@ function buildWaveShell {
|
||||
}
|
||||
function buildWaveSrv {
|
||||
# adds -extldflags=-static, *only* on linux (macos does not support fully static binaries) to avoid a glibc dependency
|
||||
(cd wavesrv; CGO_ENABLED=1 GOARCH=$1 go build -tags "osusergo,netgo,sqlite_omit_load_extension" -ldflags "-linkmode 'external' -extldflags=-static $GO_LDFLAGS -X main.WaveVersion=$WAVESRV_VERSION" -o ../bin/wavesrv.$1 ./cmd)
|
||||
(cd wavesrv; CGO_ENABLED=1 GOARCH=$1 go build -tags "osusergo,netcgo,sqlite_omit_load_extension" -ldflags "-linkmode 'external' -extldflags=-static $GO_LDFLAGS -X main.WaveVersion=$WAVESRV_VERSION" -o ../bin/wavesrv.$1 ./cmd)
|
||||
}
|
||||
buildWaveShell darwin amd64
|
||||
buildWaveShell darwin arm64
|
||||
@@ -90,7 +90,7 @@ yarn run electron-builder -c electron-builder.config.js -l -p never
|
||||
# @scripthaus command build-wavesrv
|
||||
WAVESRV_VERSION=$(node -e 'console.log(require("./version.js"))')
|
||||
cd wavesrv
|
||||
CGO_ENABLED=1 go build -tags "osusergo,netgo,sqlite_omit_load_extension" -ldflags "-X main.BuildTime=$(date +'%Y%m%d%H%M') -X main.WaveVersion=$WAVESRV_VERSION" -o ../bin/wavesrv ./cmd
|
||||
CGO_ENABLED=1 go build -tags "osusergo,netcgo,sqlite_omit_load_extension" -ldflags "-X main.BuildTime=$(date +'%Y%m%d%H%M') -X main.WaveVersion=$WAVESRV_VERSION" -o ../bin/wavesrv ./cmd
|
||||
```
|
||||
|
||||
```bash
|
||||
|
||||
@@ -178,7 +178,7 @@ class LineActions extends React.Component<{ screen: LineContainerType; line: Lin
|
||||
<div className="line-actions">
|
||||
<Choose>
|
||||
<When condition={containerType == appconst.LineContainer_Main}>
|
||||
<div key="chat" title="Restart Command" className="line-icon" onClick={this.clickChat}>
|
||||
<div key="chat" title="Ask Wave AI" className="line-icon" onClick={this.clickChat}>
|
||||
<i className="fa-sharp fa-regular fa-sparkles fa-fw" />
|
||||
</div>
|
||||
<div key="restart" title="Restart Command" className="line-icon" onClick={this.clickRestart}>
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"unicode/utf8"
|
||||
@@ -673,3 +674,26 @@ func GetFirstLine(s string) string {
|
||||
}
|
||||
return s[0:idx]
|
||||
}
|
||||
|
||||
func TrimQuotes(s string) (string, bool) {
|
||||
if len(s) > 2 && s[0] == '"' {
|
||||
trimmed, err := strconv.Unquote(s)
|
||||
if err != nil {
|
||||
return s, false
|
||||
}
|
||||
return trimmed, true
|
||||
}
|
||||
return s, false
|
||||
}
|
||||
|
||||
func TryTrimQuotes(s string) string {
|
||||
trimmed, _ := TrimQuotes(s)
|
||||
return trimmed
|
||||
}
|
||||
|
||||
func ReplaceQuotes(s string, shouldReplace bool) string {
|
||||
if shouldReplace {
|
||||
return strconv.Quote(s)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
+4
-3
@@ -17,12 +17,12 @@ require (
|
||||
github.com/jmoiron/sqlx v1.3.5
|
||||
github.com/kevinburke/ssh_config v1.2.0
|
||||
github.com/mattn/go-sqlite3 v1.14.16
|
||||
github.com/sashabaranov/go-openai v1.9.0
|
||||
github.com/sashabaranov/go-openai v1.30.0
|
||||
github.com/sawka/txwrap v0.1.2
|
||||
github.com/wavetermdev/waveterm/waveshell v0.0.0
|
||||
golang.org/x/crypto v0.17.0
|
||||
golang.org/x/crypto v0.24.0
|
||||
golang.org/x/mod v0.10.0
|
||||
golang.org/x/sys v0.15.0
|
||||
golang.org/x/sys v0.21.0
|
||||
mvdan.cc/sh/v3 v3.7.0
|
||||
)
|
||||
|
||||
@@ -30,6 +30,7 @@ require (
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/skeema/knownhosts v1.3.0 // indirect
|
||||
go.uber.org/atomic v1.7.0 // indirect
|
||||
)
|
||||
|
||||
|
||||
@@ -51,8 +51,12 @@ github.com/rogpeppe/go-internal v1.10.1-0.20230524175051-ec119421bb97 h1:3RPlVWz
|
||||
github.com/rogpeppe/go-internal v1.10.1-0.20230524175051-ec119421bb97/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
||||
github.com/sashabaranov/go-openai v1.9.0 h1:NoiO++IISxxJ1pRc0n7uZvMGMake0G+FJ1XPwXtprsA=
|
||||
github.com/sashabaranov/go-openai v1.9.0/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg=
|
||||
github.com/sashabaranov/go-openai v1.30.0 h1:fHv9urGxABfm885xGWsXFSk5cksa+8dJ4jGli/UQQcI=
|
||||
github.com/sashabaranov/go-openai v1.30.0/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg=
|
||||
github.com/sawka/txwrap v0.1.2 h1:v8xS0Z1LE7/6vMZA81PYihI+0TSR6Zm1MalzzBIuXKc=
|
||||
github.com/sawka/txwrap v0.1.2/go.mod h1:T3nlw2gVpuolo6/XEetvBbk1oMXnY978YmBFy1UyHvw=
|
||||
github.com/skeema/knownhosts v1.3.0 h1:AM+y0rI04VksttfwjkSTNQorvGqmwATnvnAHpSgc0LY=
|
||||
github.com/skeema/knownhosts v1.3.0/go.mod h1:sPINvnADmT/qYH1kfv+ePMmOBTH6Tbl7b5LvTDjFK7M=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
@@ -63,11 +67,15 @@ go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
|
||||
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
|
||||
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
|
||||
golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk=
|
||||
golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
|
||||
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
|
||||
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
||||
@@ -132,7 +132,7 @@ var SetVarScopes = []SetVarScope{
|
||||
{ScopeName: "remote", VarNames: []string{}},
|
||||
}
|
||||
|
||||
var userHostRe = regexp.MustCompile(`^(sudo@)?([a-zA-Z0-9][a-zA-Z0-9._@\\-]*@)?([a-z0-9][a-z0-9.-]*)(?::([0-9]+))?$`)
|
||||
var userHostRe = regexp.MustCompile(`^(sudo@)?([a-zA-Z0-9][a-zA-Z0-9._@:\\-]*@)?([a-z0-9][a-z0-9.-]*)(?::([0-9]+))?$`)
|
||||
var remoteAliasRe = regexp.MustCompile("^[a-zA-Z0-9][a-zA-Z0-9._-]*$")
|
||||
var genericNameRe = regexp.MustCompile("^[a-zA-Z][a-zA-Z0-9_ .()<>,/\"'\\[\\]{}=+$@!*-]*$")
|
||||
var rendererRe = regexp.MustCompile("^[a-zA-Z][a-zA-Z0-9_.:-]*$")
|
||||
|
||||
@@ -190,7 +190,7 @@ func RunCompletionStream(ctx context.Context, opts *sstore.OpenAIOptsType, promp
|
||||
pk := packet.MakeOpenAIPacket()
|
||||
pk.Index = choice.Index
|
||||
pk.Text = choice.Delta.Content
|
||||
pk.FinishReason = choice.FinishReason
|
||||
pk.FinishReason = string(choice.FinishReason)
|
||||
rtn <- pk
|
||||
}
|
||||
}
|
||||
@@ -209,7 +209,7 @@ func marshalResponse(resp openaiapi.ChatCompletionResponse) []*packet.OpenAIPack
|
||||
choicePk := packet.MakeOpenAIPacket()
|
||||
choicePk.Index = choice.Index
|
||||
choicePk.Text = choice.Message.Content
|
||||
choicePk.FinishReason = choice.FinishReason
|
||||
choicePk.FinishReason = string(choice.FinishReason)
|
||||
rtn = append(rtn, choicePk)
|
||||
}
|
||||
return rtn
|
||||
|
||||
@@ -1314,14 +1314,15 @@ func (wsh *WaveshellProc) RunInstall(autoInstall bool) {
|
||||
wsh.WriteToPtyBuffer("*error: cannot install on a local remote\n")
|
||||
return
|
||||
}
|
||||
_, err = shellapi.MakeShellApi(packet.ShellType_bash)
|
||||
sapi, err := shellapi.MakeShellApi(wsh.GetShellType())
|
||||
if err != nil {
|
||||
wsh.WriteToPtyBuffer("*error: %v\n", err)
|
||||
return
|
||||
}
|
||||
if wsh.Client == nil {
|
||||
remoteDisplayName := fmt.Sprintf("%s [%s]", remoteCopy.RemoteAlias, remoteCopy.RemoteCanonicalName)
|
||||
client, err := ConnectToClient(makeClientCtx, remoteCopy.SSHOpts, remoteDisplayName)
|
||||
sshAuthSock, _ := exec.CommandContext(makeClientCtx, sapi.GetLocalShellPath(), "-c", "echo \"${SSH_AUTH_SOCK}\"").CombinedOutput()
|
||||
client, err := ConnectToClient(makeClientCtx, remoteCopy.SSHOpts, remoteDisplayName, strings.TrimSpace(string(sshAuthSock)))
|
||||
if err != nil {
|
||||
statusErr := fmt.Errorf("ssh cannot connect to client: %w", err)
|
||||
wsh.setInstallErrorStatus(statusErr)
|
||||
@@ -1614,7 +1615,8 @@ func (wsh *WaveshellProc) createWaveshellSession(clientCtx context.Context, remo
|
||||
wsSession = shexec.CmdWrap{Cmd: ecmd}
|
||||
} else if wsh.Client == nil {
|
||||
remoteDisplayName := fmt.Sprintf("%s [%s]", remoteCopy.RemoteAlias, remoteCopy.RemoteCanonicalName)
|
||||
client, err := ConnectToClient(clientCtx, remoteCopy.SSHOpts, remoteDisplayName)
|
||||
sshAuthSock, _ := exec.CommandContext(clientCtx, sapi.GetLocalShellPath(), "-c", "echo \"${SSH_AUTH_SOCK}\"").CombinedOutput()
|
||||
client, err := ConnectToClient(clientCtx, remoteCopy.SSHOpts, remoteDisplayName, strings.TrimSpace(string(sshAuthSock)))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ssh cannot connect to client: %w", err)
|
||||
}
|
||||
|
||||
+124
-30
@@ -22,18 +22,23 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/kevinburke/ssh_config"
|
||||
"github.com/skeema/knownhosts"
|
||||
"github.com/wavetermdev/waveterm/waveshell/pkg/base"
|
||||
"github.com/wavetermdev/waveterm/waveshell/pkg/utilfn"
|
||||
"github.com/wavetermdev/waveterm/wavesrv/pkg/scbus"
|
||||
"github.com/wavetermdev/waveterm/wavesrv/pkg/sstore"
|
||||
"github.com/wavetermdev/waveterm/wavesrv/pkg/userinput"
|
||||
"golang.org/x/crypto/ssh"
|
||||
"golang.org/x/crypto/ssh/knownhosts"
|
||||
"golang.org/x/crypto/ssh/agent"
|
||||
xknownhosts "golang.org/x/crypto/ssh/knownhosts"
|
||||
)
|
||||
|
||||
type UserInputCancelError struct {
|
||||
Err error
|
||||
}
|
||||
|
||||
type HostKeyAlgorithms = func(hostWithPort string) (algos []string)
|
||||
|
||||
func (uice UserInputCancelError) Error() string {
|
||||
return uice.Err.Error()
|
||||
}
|
||||
@@ -65,7 +70,7 @@ func createDummySigner() ([]ssh.Signer, error) {
|
||||
// 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(connCtx context.Context, sshKeywords *SshKeywords, passphrase string) func() ([]ssh.Signer, error) {
|
||||
func createPublicKeyCallback(connCtx context.Context, sshKeywords *SshKeywords, passphrase string, authSockSignersExt []ssh.Signer, agentClient agent.ExtendedAgent) func() ([]ssh.Signer, error) {
|
||||
var identityFiles []string
|
||||
existingKeys := make(map[string][]byte)
|
||||
|
||||
@@ -83,7 +88,19 @@ func createPublicKeyCallback(connCtx context.Context, sshKeywords *SshKeywords,
|
||||
// require pointer to modify list in closure
|
||||
identityFilesPtr := &identityFiles
|
||||
|
||||
var authSockSigners []ssh.Signer
|
||||
authSockSigners = append(authSockSigners, authSockSignersExt...)
|
||||
authSockSignersPtr := &authSockSigners
|
||||
|
||||
return func() ([]ssh.Signer, error) {
|
||||
// try auth sock
|
||||
if len(*authSockSignersPtr) != 0 {
|
||||
authSockSigner := (*authSockSignersPtr)[0]
|
||||
*authSockSignersPtr = (*authSockSignersPtr)[1:]
|
||||
return []ssh.Signer{authSockSigner}, nil
|
||||
}
|
||||
|
||||
// try manual identity files
|
||||
if len(*identityFilesPtr) == 0 {
|
||||
return nil, fmt.Errorf("no identity files remaining")
|
||||
}
|
||||
@@ -95,6 +112,24 @@ func createPublicKeyCallback(connCtx context.Context, sshKeywords *SshKeywords,
|
||||
// skip this key and try with the next
|
||||
return createDummySigner()
|
||||
}
|
||||
|
||||
unencryptedPrivateKey, err := ssh.ParseRawPrivateKey(privateKey)
|
||||
if err == nil {
|
||||
signer, err := ssh.NewSignerFromKey(unencryptedPrivateKey)
|
||||
if err == nil {
|
||||
if sshKeywords.AddKeysToAgent && agentClient != nil {
|
||||
agentClient.Add(agent.AddedKey{
|
||||
PrivateKey: unencryptedPrivateKey,
|
||||
})
|
||||
}
|
||||
return []ssh.Signer{signer}, err
|
||||
}
|
||||
}
|
||||
if _, ok := err.(*ssh.PassphraseMissingError); !ok {
|
||||
// skip this key and try with the next
|
||||
return createDummySigner()
|
||||
}
|
||||
|
||||
signer, err := ssh.ParsePrivateKey(privateKey)
|
||||
if err == nil {
|
||||
return []ssh.Signer{signer}, err
|
||||
@@ -104,9 +139,17 @@ func createPublicKeyCallback(connCtx context.Context, sshKeywords *SshKeywords,
|
||||
return createDummySigner()
|
||||
}
|
||||
|
||||
signer, err = ssh.ParsePrivateKeyWithPassphrase(privateKey, []byte(passphrase))
|
||||
unencryptedPrivateKey, err = ssh.ParseRawPrivateKeyWithPassphrase(privateKey, []byte(passphrase))
|
||||
if err == nil {
|
||||
return []ssh.Signer{signer}, err
|
||||
signer, err := ssh.NewSignerFromKey(unencryptedPrivateKey)
|
||||
if err == nil {
|
||||
if sshKeywords.AddKeysToAgent && agentClient != nil {
|
||||
agentClient.Add(agent.AddedKey{
|
||||
PrivateKey: unencryptedPrivateKey,
|
||||
})
|
||||
}
|
||||
return []ssh.Signer{signer}, err
|
||||
}
|
||||
}
|
||||
if err != x509.IncorrectPasswordError && err.Error() != "bcrypt_pbkdf: empty password" {
|
||||
// skip this key and try with the next
|
||||
@@ -132,11 +175,22 @@ func createPublicKeyCallback(connCtx context.Context, sshKeywords *SshKeywords,
|
||||
// trying keys
|
||||
return nil, UserInputCancelError{Err: err}
|
||||
}
|
||||
signer, err = ssh.ParsePrivateKeyWithPassphrase(privateKey, []byte(response.Text))
|
||||
|
||||
unencryptedPrivateKey, err = ssh.ParseRawPrivateKeyWithPassphrase(privateKey, []byte([]byte(response.Text)))
|
||||
if err != nil {
|
||||
// skip this key and try with the next
|
||||
return createDummySigner()
|
||||
}
|
||||
signer, err = ssh.NewSignerFromKey(unencryptedPrivateKey)
|
||||
if err != nil {
|
||||
// skip this key and try with the next
|
||||
return createDummySigner()
|
||||
}
|
||||
if sshKeywords.AddKeysToAgent && agentClient != nil {
|
||||
agentClient.Add(agent.AddedKey{
|
||||
PrivateKey: unencryptedPrivateKey,
|
||||
})
|
||||
}
|
||||
return []ssh.Signer{signer}, err
|
||||
}
|
||||
}
|
||||
@@ -356,7 +410,7 @@ func lineContainsMatch(line []byte, matches [][]byte) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func createHostKeyCallback(opts *sstore.SSHOpts) (ssh.HostKeyCallback, error) {
|
||||
func createHostKeyCallback(opts *sstore.SSHOpts) (ssh.HostKeyCallback, HostKeyAlgorithms, error) {
|
||||
ssh_config.ReloadConfigs()
|
||||
rawUserKnownHostsFiles, _ := ssh_config.GetStrict(opts.SSHHost, "UserKnownHostsFile")
|
||||
userKnownHostsFiles := strings.Fields(rawUserKnownHostsFiles) // TODO - smarter splitting escaped spaces and quotes
|
||||
@@ -365,7 +419,7 @@ func createHostKeyCallback(opts *sstore.SSHOpts) (ssh.HostKeyCallback, error) {
|
||||
|
||||
osUser, err := user.Current()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
var unexpandedKnownHostsFiles []string
|
||||
if osUser.Username == "root" {
|
||||
@@ -381,7 +435,7 @@ func createHostKeyCallback(opts *sstore.SSHOpts) (ssh.HostKeyCallback, error) {
|
||||
|
||||
// there are no good known hosts files
|
||||
if len(knownHostsFiles) == 0 {
|
||||
return nil, fmt.Errorf("no known_hosts files provided by ssh. defaults are overridden")
|
||||
return nil, nil, fmt.Errorf("no known_hosts files provided by ssh. defaults are overridden")
|
||||
}
|
||||
|
||||
var unreadableFiles []string
|
||||
@@ -390,9 +444,10 @@ func createHostKeyCallback(opts *sstore.SSHOpts) (ssh.HostKeyCallback, error) {
|
||||
// incorrectly. if a problem file is found, it is removed from our list
|
||||
// and we try again
|
||||
var basicCallback ssh.HostKeyCallback
|
||||
var hostKeyAlgorithms HostKeyAlgorithms
|
||||
for basicCallback == nil && len(knownHostsFiles) > 0 {
|
||||
var err error
|
||||
basicCallback, err = knownhosts.New(knownHostsFiles...)
|
||||
keyDb, err := knownhosts.NewDB(knownHostsFiles...)
|
||||
if serr, ok := err.(*os.PathError); ok {
|
||||
badFile := serr.Path
|
||||
unreadableFiles = append(unreadableFiles, badFile)
|
||||
@@ -403,18 +458,26 @@ func createHostKeyCallback(opts *sstore.SSHOpts) (ssh.HostKeyCallback, error) {
|
||||
}
|
||||
}
|
||||
if len(okFiles) >= len(knownHostsFiles) {
|
||||
return nil, fmt.Errorf("problem file (%s) doesn't exist. this should not be possible", badFile)
|
||||
return nil, nil, fmt.Errorf("problem file (%s) doesn't exist. this should not be possible", badFile)
|
||||
}
|
||||
knownHostsFiles = okFiles
|
||||
} else if err != nil {
|
||||
// TODO handle obscure problems if possible
|
||||
return nil, fmt.Errorf("known_hosts formatting error: %+v", err)
|
||||
return nil, nil, fmt.Errorf("known_hosts formatting error: %+v", err)
|
||||
} else {
|
||||
basicCallback = keyDb.HostKeyCallback()
|
||||
hostKeyAlgorithms = keyDb.HostKeyAlgorithms
|
||||
}
|
||||
}
|
||||
|
||||
if basicCallback == nil {
|
||||
basicCallback = func(hostname string, remote net.Addr, key ssh.PublicKey) error {
|
||||
return &knownhosts.KeyError{}
|
||||
return &xknownhosts.KeyError{}
|
||||
}
|
||||
// need to return nil here to avoid null pointer from attempting to call
|
||||
// the one provided by the db if nothing was found
|
||||
hostKeyAlgorithms = func(hostWithPort string) (algos []string) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -423,21 +486,21 @@ func createHostKeyCallback(opts *sstore.SSHOpts) (ssh.HostKeyCallback, error) {
|
||||
if err == nil {
|
||||
// success
|
||||
return nil
|
||||
} else if _, ok := err.(*knownhosts.RevokedError); ok {
|
||||
} else if _, ok := err.(*xknownhosts.RevokedError); ok {
|
||||
// revoked credentials are refused outright
|
||||
return err
|
||||
} else if _, ok := err.(*knownhosts.KeyError); !ok {
|
||||
} else if _, ok := err.(*xknownhosts.KeyError); !ok {
|
||||
// this is an unknown error (note the !ok is opposite of usual)
|
||||
return err
|
||||
}
|
||||
serr, _ := err.(*knownhosts.KeyError)
|
||||
serr, _ := err.(*xknownhosts.KeyError)
|
||||
if len(serr.Want) == 0 {
|
||||
// the key was not found
|
||||
|
||||
// try to write to a file that could be read
|
||||
err := fmt.Errorf("placeholder, should not be returned") // a null value here can cause problems with empty slice
|
||||
for _, filename := range knownHostsFiles {
|
||||
newLine := knownhosts.Line([]string{knownhosts.Normalize(hostname)}, key)
|
||||
newLine := xknownhosts.Line([]string{xknownhosts.Normalize(hostname)}, key)
|
||||
getUserVerification := createUnknownKeyVerifier(filename, hostname, remote.String(), key)
|
||||
err = writeToKnownHosts(filename, newLine, getUserVerification)
|
||||
if err == nil {
|
||||
@@ -452,7 +515,7 @@ func createHostKeyCallback(opts *sstore.SSHOpts) (ssh.HostKeyCallback, error) {
|
||||
// should catch cases where there is no known_hosts file
|
||||
if err != nil {
|
||||
for _, filename := range unreadableFiles {
|
||||
newLine := knownhosts.Line([]string{knownhosts.Normalize(hostname)}, key)
|
||||
newLine := xknownhosts.Line([]string{xknownhosts.Normalize(hostname)}, key)
|
||||
getUserVerification := createMissingKnownHostsVerifier(filename, hostname, remote.String(), key)
|
||||
err = writeToKnownHosts(filename, newLine, getUserVerification)
|
||||
if err == nil {
|
||||
@@ -502,7 +565,7 @@ func createHostKeyCallback(opts *sstore.SSHOpts) (ssh.HostKeyCallback, error) {
|
||||
return fmt.Errorf("remote host identification has changed")
|
||||
}
|
||||
|
||||
updatedCallback, err := knownhosts.New(knownHostsFiles...)
|
||||
updatedCallback, err := xknownhosts.New(knownHostsFiles...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -510,7 +573,7 @@ func createHostKeyCallback(opts *sstore.SSHOpts) (ssh.HostKeyCallback, error) {
|
||||
return updatedCallback(hostname, remote, key)
|
||||
}
|
||||
|
||||
return waveHostKeyCallback, nil
|
||||
return waveHostKeyCallback, hostKeyAlgorithms, nil
|
||||
}
|
||||
|
||||
func DialContext(ctx context.Context, network string, addr string, config *ssh.ClientConfig) (*ssh.Client, error) {
|
||||
@@ -526,8 +589,8 @@ func DialContext(ctx context.Context, network string, addr string, config *ssh.C
|
||||
return ssh.NewClient(c, chans, reqs), nil
|
||||
}
|
||||
|
||||
func ConnectToClient(connCtx context.Context, opts *sstore.SSHOpts, remoteDisplayName string) (*ssh.Client, error) {
|
||||
sshConfigKeywords, err := findSshConfigKeywords(opts.SSHHost)
|
||||
func ConnectToClient(connCtx context.Context, opts *sstore.SSHOpts, remoteDisplayName string, sshAuthSock string) (*ssh.Client, error) {
|
||||
sshConfigKeywords, err := findSshConfigKeywords(opts.SSHHost, sshAuthSock)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -537,7 +600,17 @@ func ConnectToClient(connCtx context.Context, opts *sstore.SSHOpts, remoteDispla
|
||||
return nil, err
|
||||
}
|
||||
|
||||
publicKeyCallback := ssh.PublicKeysCallback(createPublicKeyCallback(connCtx, sshKeywords, opts.SSHPassword))
|
||||
conn, err := net.Dial("unix", sshKeywords.IdentityAgent)
|
||||
var authSockSigners []ssh.Signer
|
||||
var agentClient agent.ExtendedAgent
|
||||
if err != nil {
|
||||
log.Printf("Failed to open Identity Agent Socket: %v", err)
|
||||
} else {
|
||||
agentClient = agent.NewClient(conn)
|
||||
authSockSigners, _ = agentClient.Signers()
|
||||
}
|
||||
|
||||
publicKeyCallback := ssh.PublicKeysCallback(createPublicKeyCallback(connCtx, sshKeywords, opts.SSHPassword, authSockSigners, agentClient))
|
||||
keyboardInteractive := ssh.KeyboardInteractive(createCombinedKbdInteractiveChallenge(connCtx, opts.SSHPassword, remoteDisplayName))
|
||||
passwordCallback := ssh.PasswordCallback(createCombinedPasswordCallbackPrompt(connCtx, opts.SSHPassword, remoteDisplayName))
|
||||
|
||||
@@ -552,7 +625,7 @@ func ConnectToClient(connCtx context.Context, opts *sstore.SSHOpts, remoteDispla
|
||||
|
||||
// exclude gssapi-with-mic and hostbased until implemented
|
||||
authMethodMap := map[string]ssh.AuthMethod{
|
||||
"publickey": ssh.RetryableAuthMethod(publicKeyCallback, len(sshKeywords.IdentityFile)),
|
||||
"publickey": ssh.RetryableAuthMethod(publicKeyCallback, len(sshKeywords.IdentityFile)+len(authSockSigners)),
|
||||
"keyboard-interactive": ssh.RetryableAuthMethod(keyboardInteractive, attemptsAllowed),
|
||||
"password": ssh.RetryableAuthMethod(passwordCallback, attemptsAllowed),
|
||||
}
|
||||
@@ -576,17 +649,18 @@ func ConnectToClient(connCtx context.Context, opts *sstore.SSHOpts, remoteDispla
|
||||
authMethods = append(authMethods, authMethod)
|
||||
}
|
||||
|
||||
hostKeyCallback, err := createHostKeyCallback(opts)
|
||||
hostKeyCallback, hostKeyAlgorithms, err := createHostKeyCallback(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
clientConfig := &ssh.ClientConfig{
|
||||
User: sshKeywords.User,
|
||||
Auth: authMethods,
|
||||
HostKeyCallback: hostKeyCallback,
|
||||
}
|
||||
networkAddr := sshKeywords.HostName + ":" + sshKeywords.Port
|
||||
clientConfig := &ssh.ClientConfig{
|
||||
User: sshKeywords.User,
|
||||
Auth: authMethods,
|
||||
HostKeyCallback: hostKeyCallback,
|
||||
HostKeyAlgorithms: hostKeyAlgorithms(networkAddr),
|
||||
}
|
||||
return DialContext(connCtx, "tcp", networkAddr, clientConfig)
|
||||
}
|
||||
|
||||
@@ -600,6 +674,8 @@ type SshKeywords struct {
|
||||
PasswordAuthentication bool
|
||||
KbdInteractiveAuthentication bool
|
||||
PreferredAuthentications []string
|
||||
AddKeysToAgent bool
|
||||
IdentityAgent string
|
||||
}
|
||||
|
||||
func combineSshKeywords(opts *sstore.SSHOpts, configKeywords *SshKeywords) (*SshKeywords, error) {
|
||||
@@ -649,6 +725,8 @@ func combineSshKeywords(opts *sstore.SSHOpts, configKeywords *SshKeywords) (*Ssh
|
||||
sshKeywords.PasswordAuthentication = configKeywords.PasswordAuthentication
|
||||
sshKeywords.KbdInteractiveAuthentication = configKeywords.KbdInteractiveAuthentication
|
||||
sshKeywords.PreferredAuthentications = configKeywords.PreferredAuthentications
|
||||
sshKeywords.AddKeysToAgent = configKeywords.AddKeysToAgent
|
||||
sshKeywords.IdentityAgent = configKeywords.IdentityAgent
|
||||
|
||||
return sshKeywords, nil
|
||||
}
|
||||
@@ -656,7 +734,7 @@ func combineSshKeywords(opts *sstore.SSHOpts, configKeywords *SshKeywords) (*Ssh
|
||||
// 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) {
|
||||
func findSshConfigKeywords(hostPattern string, sshAuthSock string) (*SshKeywords, error) {
|
||||
ssh_config.ReloadConfigs()
|
||||
sshKeywords := &SshKeywords{}
|
||||
var err error
|
||||
@@ -711,5 +789,21 @@ func findSshConfigKeywords(hostPattern string) (*SshKeywords, error) {
|
||||
}
|
||||
sshKeywords.PreferredAuthentications = strings.Split(preferredAuthenticationsRaw, ",")
|
||||
|
||||
addKeysToAgentRaw, err := ssh_config.GetStrict(hostPattern, "AddKeysToAgent")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sshKeywords.AddKeysToAgent = (strings.ToLower(addKeysToAgentRaw) == "yes")
|
||||
|
||||
identityAgentRaw, err := ssh_config.GetStrict(hostPattern, "IdentityAgent")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if identityAgentRaw == "" {
|
||||
sshKeywords.IdentityAgent = base.ExpandHomeDir(utilfn.TryTrimQuotes(strings.TrimSpace(string(sshAuthSock))))
|
||||
} else {
|
||||
sshKeywords.IdentityAgent = base.ExpandHomeDir(utilfn.TryTrimQuotes(identityAgentRaw))
|
||||
}
|
||||
|
||||
return sshKeywords, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user