From 94c016fd46701c8b9fa192db1a47f11db70d1963 Mon Sep 17 00:00:00 2001 From: Qbandev Date: Thu, 12 Feb 2026 09:33:24 +0100 Subject: [PATCH 1/4] fix: add swap token exchange for Wave Terminal v0.14.0+ compatibility MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wave Terminal v0.14.0 changed authentication from direct WAVETERM_JWT injection to a swap token mechanism. Widget cmd controller blocks no longer receive WAVETERM_JWT because they bypass Wave's shell integration files. This adds the token exchange step directly in wave-scratch.sh, mirroring what Wave's own integration scripts do (`wsh token`). The fix is backward compatible — it's a no-op when JWT is already present. Changes: - Add swap token exchange (WAVETERM_SWAPTOKEN → WAVETERM_JWT) to generated wave-scratch.sh - Add new wsh search paths (.config/waveterm/bin, .waveterm/bin) - Add mktemp-based error handling with auth-specific messaging - Sync VERSION across install.sh, uninstall.sh, and VERSION file with x-release-please-version markers - Add VERSION consistency CI check - Add release-please extra-files for uninstall.sh and VERSION - Add troubleshooting section to README - Add 9 new tests (85 total) Co-Authored-By: Claude Opus 4.6 --- .github/workflows/test.yml | 15 +++++++ README.md | 23 +++++++++- VERSION | 2 +- install.sh | 32 +++++++++++--- release-please-config.json | 6 +++ test/install.bats | 86 ++++++++++++++++++++++++++++++++++++++ test/test_helper.bash | 16 +++++++ uninstall.sh | 4 +- 8 files changed, 173 insertions(+), 11 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9ab4819..e1375b1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -60,3 +60,18 @@ jobs: done echo "All lint checks passed!" + + - name: Check VERSION consistency + run: | + FILE_VERSION=$(cat VERSION) + SCRIPT_VERSION=$(grep 'x-release-please-version' install.sh | grep -oE '[0-9]+\.[0-9]+\.[0-9]+') + UNINSTALL_VERSION=$(grep 'x-release-please-version' uninstall.sh | grep -oE '[0-9]+\.[0-9]+\.[0-9]+') + if [ "$FILE_VERSION" != "$SCRIPT_VERSION" ]; then + echo "ERROR: VERSION ($FILE_VERSION) != install.sh ($SCRIPT_VERSION)" + exit 1 + fi + if [ "$FILE_VERSION" != "$UNINSTALL_VERSION" ]; then + echo "ERROR: VERSION ($FILE_VERSION) != uninstall.sh ($UNINSTALL_VERSION)" + exit 1 + fi + echo "VERSION consistency check passed: $FILE_VERSION" diff --git a/README.md b/README.md index 94b5efa..f1b8daf 100644 --- a/README.md +++ b/README.md @@ -97,10 +97,29 @@ mv ~/Documents/WaveNotes ~/Library/Mobile\ Documents/com~apple~CloudDocs/WaveNot ln -s ~/Library/Mobile\ Documents/com~apple~CloudDocs/WaveNotes ~/Documents/WaveNotes ``` +## Troubleshooting + +### Widget fails after upgrading Wave Terminal to v0.14.0+ + +Wave Terminal v0.14.0 changed how authentication works for widget commands. If your note widget stopped working after upgrading Wave Terminal (you may see an authentication error or the editor fails to open), re-run the installer to regenerate the script: + +```bash +wave-notes-setup +``` + +This updates `~/bin/wave-scratch.sh` with the new token exchange mechanism. No data is lost — your existing notes and configuration are preserved. + +If you installed via Homebrew, update first: + +```bash +brew upgrade wave-notes-setup +wave-notes-setup +``` + ## Requirements - macOS -- [Wave Terminal](https://www.waveterm.dev/) +- [Wave Terminal](https://www.waveterm.dev/) (v0.14.0+ supported with automatic swap token exchange) - `jq` (optional, falls back to Python if missing) ## Security @@ -133,7 +152,7 @@ The project includes a comprehensive test suite using [bats](https://github.com/ # Install bats brew install bats-core -# Run all tests (77 tests) +# Run all tests (85 tests) ./test/run_tests.sh # Or run bats directly diff --git a/VERSION b/VERSION index 3eefcb9..38f77a6 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.0.0 +2.0.1 diff --git a/install.sh b/install.sh index 16012d5..2c5fc7e 100755 --- a/install.sh +++ b/install.sh @@ -1,11 +1,11 @@ #!/bin/bash -# wave-notes-setup v1.0.0 +# wave-notes-setup v2.0.1 # Configure Wave Terminal with a Warp-like notes system # https://github.com/qbandev/wave-notes-setup set -euo pipefail -VERSION="1.0.0" +VERSION="2.0.1" # x-release-please-version SCRIPT_NAME="wave-notes-setup" # Colors @@ -316,7 +316,7 @@ install_scratchpad_script() { # Generate the script with injected paths cat > "$script_path" << 'SCRIPT_EOF' #!/bin/bash -# Generated by wave-notes-setup v1.0.0 +# Generated by wave-notes-setup v2.0.1 # Re-run wave-notes-setup to update paths set -euo pipefail @@ -338,6 +338,8 @@ find_wsh() { return fi local paths=( + "$HOME/.config/waveterm/bin/wsh" + "$HOME/.waveterm/bin/wsh" "$HOME/Library/Application Support/waveterm/bin/wsh" "/usr/local/bin/wsh" "/opt/homebrew/bin/wsh" @@ -353,13 +355,31 @@ find_wsh() { WSH_CMD=$(find_wsh) -if [[ -n "$WSH_CMD" ]]; then - "$WSH_CMD" edit "$FILEPATH" -else +if [[ -z "$WSH_CMD" ]]; then echo "Error: 'wsh' command not found. Is Wave Terminal installed and running?" >&2 echo "File created at: $FILEPATH" >&2 exit 1 fi + +# Wave Terminal v0.14.0+ token exchange for cmd controller blocks +if [[ -z "${WAVETERM_JWT:-}" && -n "${WAVETERM_SWAPTOKEN:-}" && -n "$WSH_CMD" ]]; then + eval "$("$WSH_CMD" token "$WAVETERM_SWAPTOKEN" bash 2>/dev/null)" || true +fi + +ERR_FILE=$(mktemp) +if ! "$WSH_CMD" edit "$FILEPATH" 2>"$ERR_FILE"; then + wsh_err=$(cat "$ERR_FILE" 2>/dev/null) + rm -f "$ERR_FILE" + if echo "$wsh_err" | grep -q "WAVETERM"; then + echo "Error: Wave Terminal authentication failed." >&2 + echo "Try: Re-run 'wave-notes-setup' to update configuration." >&2 + else + echo "Error: Failed to open editor: $wsh_err" >&2 + fi + echo "Note created at: $FILEPATH" >&2 + exit 1 +fi +rm -f "$ERR_FILE" SCRIPT_EOF # Replace placeholder with actual path diff --git a/release-please-config.json b/release-please-config.json index b7f6bd0..85af6c4 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -6,10 +6,16 @@ "package-name": "wave-notes-setup", "changelog-path": "CHANGELOG.md", "extra-files": [ + "VERSION", { "type": "generic", "path": "install.sh", "glob": false + }, + { + "type": "generic", + "path": "uninstall.sh", + "glob": false } ] } diff --git a/test/install.bats b/test/install.bats index 2e820e6..a1cf2da 100644 --- a/test/install.bats +++ b/test/install.bats @@ -293,6 +293,92 @@ EOF assert_file_contains "$BIN_DIR/wave-scratch.sh" "find_wsh()" } +@test "install_scratchpad_script: find_wsh includes .config/waveterm/bin/wsh path" { + NOTES_DIR="$TEST_HOME/TestNotes" + BIN_DIR="$TEST_HOME/TestBin" + mkdir -p "$BIN_DIR" + + install_scratchpad_script + + assert_file_contains "$BIN_DIR/wave-scratch.sh" '.config/waveterm/bin/wsh' +} + +@test "install_scratchpad_script: find_wsh includes .waveterm/bin/wsh path" { + NOTES_DIR="$TEST_HOME/TestNotes" + BIN_DIR="$TEST_HOME/TestBin" + mkdir -p "$BIN_DIR" + + install_scratchpad_script + + assert_file_contains "$BIN_DIR/wave-scratch.sh" '.waveterm/bin/wsh' +} + +@test "install_scratchpad_script: find_wsh lists new paths before old paths" { + NOTES_DIR="$TEST_HOME/TestNotes" + BIN_DIR="$TEST_HOME/TestBin" + mkdir -p "$BIN_DIR" + + install_scratchpad_script + + # .config/waveterm/bin/wsh should appear before /usr/local/bin/wsh + local config_line old_line + config_line=$(grep -n '.config/waveterm/bin/wsh' "$BIN_DIR/wave-scratch.sh" | head -1 | cut -d: -f1) + old_line=$(grep -n '/usr/local/bin/wsh' "$BIN_DIR/wave-scratch.sh" | head -1 | cut -d: -f1) + [ "$config_line" -lt "$old_line" ] +} + +@test "install_scratchpad_script: contains swap token exchange logic" { + NOTES_DIR="$TEST_HOME/TestNotes" + BIN_DIR="$TEST_HOME/TestBin" + mkdir -p "$BIN_DIR" + + install_scratchpad_script + + assert_file_contains "$BIN_DIR/wave-scratch.sh" "WAVETERM_SWAPTOKEN" + assert_file_contains "$BIN_DIR/wave-scratch.sh" 'WSH_CMD.*token' +} + +@test "install_scratchpad_script: uses mktemp for error handling" { + NOTES_DIR="$TEST_HOME/TestNotes" + BIN_DIR="$TEST_HOME/TestBin" + mkdir -p "$BIN_DIR" + + install_scratchpad_script + + assert_file_contains "$BIN_DIR/wave-scratch.sh" "mktemp" + assert_file_contains "$BIN_DIR/wave-scratch.sh" "ERR_FILE" +} + +@test "install_scratchpad_script: swap token is backward compatible (no-op if JWT present)" { + NOTES_DIR="$TEST_HOME/TestNotes" + BIN_DIR="$TEST_HOME/TestBin" + mkdir -p "$BIN_DIR" + + install_scratchpad_script + + # The condition checks for empty WAVETERM_JWT, so it's a no-op if JWT is already set + # grep -F for fixed string since -z looks like a grep flag + grep -F 'WAVETERM_JWT' "$BIN_DIR/wave-scratch.sh" | grep -qF 'z "' +} + +@test "VERSION consistency: VERSION file matches install.sh" { + local file_version + file_version=$(cat "$BATS_TEST_DIRNAME/../VERSION") + local script_version + script_version=$(grep 'x-release-please-version' "$BATS_TEST_DIRNAME/../install.sh" | grep -oE '[0-9]+\.[0-9]+\.[0-9]+') + + [ "$file_version" = "$script_version" ] +} + +@test "VERSION consistency: VERSION file matches uninstall.sh" { + local file_version + file_version=$(cat "$BATS_TEST_DIRNAME/../VERSION") + local script_version + script_version=$(grep 'x-release-please-version' "$BATS_TEST_DIRNAME/../uninstall.sh" | grep -oE '[0-9]+\.[0-9]+\.[0-9]+') + + [ "$file_version" = "$script_version" ] +} + # ============================================================================= # install_widgets() tests # ============================================================================= diff --git a/test/test_helper.bash b/test/test_helper.bash index 52b808f..a6e7df1 100644 --- a/test/test_helper.bash +++ b/test/test_helper.bash @@ -42,6 +42,22 @@ EOF export PATH="$TEST_BIN_DIR:$PATH" } +# Create a mock wsh command that supports token exchange +create_mock_wsh_with_token() { + local mock_wsh="$TEST_BIN_DIR/wsh" + cat > "$mock_wsh" << 'EOF' +#!/bin/bash +if [ "$1" = "token" ]; then + echo "export WAVETERM_JWT=mock-jwt-token" + exit 0 +fi +echo "mock wsh called with: $@" +exit 0 +EOF + chmod +x "$mock_wsh" + export PATH="$TEST_BIN_DIR:$PATH" +} + # Create a sample widgets.json with existing widgets create_sample_widgets_json() { local widgets_file="$TEST_WAVETERM_CONFIG/widgets.json" diff --git a/uninstall.sh b/uninstall.sh index 26a1eef..009ef6f 100755 --- a/uninstall.sh +++ b/uninstall.sh @@ -1,10 +1,10 @@ #!/bin/bash -# wave-notes-setup uninstaller v1.0.0 +# wave-notes-setup uninstaller v2.0.1 # https://github.com/qbandev/wave-notes-setup set -euo pipefail -VERSION="1.0.0" +VERSION="2.0.1" # x-release-please-version # Colors RED='\033[0;31m' From 604620a7787c9c0fa6c1b2efd3abc3156eeaa403 Mon Sep 17 00:00:00 2001 From: Qbandev Date: Thu, 12 Feb 2026 09:46:20 +0100 Subject: [PATCH 2/4] fix: replace eval with direct JWT parsing in swap token exchange Address Copilot review feedback: - Replace eval of wsh token output with safe grep+parameter expansion parsing to extract WAVETERM_JWT directly - Remove unused create_mock_wsh_with_token helper from test_helper.bash Co-Authored-By: Claude Opus 4.6 --- install.sh | 9 ++++++++- test/test_helper.bash | 16 ---------------- 2 files changed, 8 insertions(+), 17 deletions(-) diff --git a/install.sh b/install.sh index 2c5fc7e..1ebb159 100755 --- a/install.sh +++ b/install.sh @@ -363,7 +363,14 @@ fi # Wave Terminal v0.14.0+ token exchange for cmd controller blocks if [[ -z "${WAVETERM_JWT:-}" && -n "${WAVETERM_SWAPTOKEN:-}" && -n "$WSH_CMD" ]]; then - eval "$("$WSH_CMD" token "$WAVETERM_SWAPTOKEN" bash 2>/dev/null)" || true + token_output="$("$WSH_CMD" token "$WAVETERM_SWAPTOKEN" bash 2>/dev/null || true)" + jwt_line=$(printf '%s\n' "$token_output" | grep -E '^export WAVETERM_JWT=' 2>/dev/null || true) + if [[ -n "${jwt_line:-}" ]]; then + WAVETERM_JWT=${jwt_line#export WAVETERM_JWT=} + WAVETERM_JWT=${WAVETERM_JWT#\"} + WAVETERM_JWT=${WAVETERM_JWT%\"} + export WAVETERM_JWT + fi fi ERR_FILE=$(mktemp) diff --git a/test/test_helper.bash b/test/test_helper.bash index a6e7df1..52b808f 100644 --- a/test/test_helper.bash +++ b/test/test_helper.bash @@ -42,22 +42,6 @@ EOF export PATH="$TEST_BIN_DIR:$PATH" } -# Create a mock wsh command that supports token exchange -create_mock_wsh_with_token() { - local mock_wsh="$TEST_BIN_DIR/wsh" - cat > "$mock_wsh" << 'EOF' -#!/bin/bash -if [ "$1" = "token" ]; then - echo "export WAVETERM_JWT=mock-jwt-token" - exit 0 -fi -echo "mock wsh called with: $@" -exit 0 -EOF - chmod +x "$mock_wsh" - export PATH="$TEST_BIN_DIR:$PATH" -} - # Create a sample widgets.json with existing widgets create_sample_widgets_json() { local widgets_file="$TEST_WAVETERM_CONFIG/widgets.json" From a5a57fb7cb2e6026e50df78cbe940a6f0a338936 Mon Sep 17 00:00:00 2001 From: Qbandev Date: Thu, 12 Feb 2026 10:06:57 +0100 Subject: [PATCH 3/4] fix: remove redundant WSH_CMD check, use printf over echo for safety Address Copilot review feedback: - Remove redundant -n "$WSH_CMD" in token exchange conditional (already guaranteed non-empty by earlier exit guard) - Replace echo with printf '%s\n' for grep on wsh_err to avoid misinterpreting strings starting with -n/-e as echo flags Co-Authored-By: Claude Opus 4.6 --- install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install.sh b/install.sh index 1ebb159..1c686a0 100755 --- a/install.sh +++ b/install.sh @@ -362,7 +362,7 @@ if [[ -z "$WSH_CMD" ]]; then fi # Wave Terminal v0.14.0+ token exchange for cmd controller blocks -if [[ -z "${WAVETERM_JWT:-}" && -n "${WAVETERM_SWAPTOKEN:-}" && -n "$WSH_CMD" ]]; then +if [[ -z "${WAVETERM_JWT:-}" && -n "${WAVETERM_SWAPTOKEN:-}" ]]; then token_output="$("$WSH_CMD" token "$WAVETERM_SWAPTOKEN" bash 2>/dev/null || true)" jwt_line=$(printf '%s\n' "$token_output" | grep -E '^export WAVETERM_JWT=' 2>/dev/null || true) if [[ -n "${jwt_line:-}" ]]; then @@ -377,7 +377,7 @@ ERR_FILE=$(mktemp) if ! "$WSH_CMD" edit "$FILEPATH" 2>"$ERR_FILE"; then wsh_err=$(cat "$ERR_FILE" 2>/dev/null) rm -f "$ERR_FILE" - if echo "$wsh_err" | grep -q "WAVETERM"; then + if printf '%s\n' "$wsh_err" | grep -q "WAVETERM"; then echo "Error: Wave Terminal authentication failed." >&2 echo "Try: Re-run 'wave-notes-setup' to update configuration." >&2 else From bb9279f126881f0c44db1c6ca2f0b15cd10e1dd1 Mon Sep 17 00:00:00 2001 From: Qbandev Date: Thu, 12 Feb 2026 10:16:30 +0100 Subject: [PATCH 4/4] fix: revert to eval for wsh token exchange The manual JWT parsing from Copilot's suggestion broke authentication because wsh token outputs multiple env vars (not just WAVETERM_JWT) that wsh commands need. Wave's own integration uses eval for this exact reason. Added a comment explaining why eval is required here. Co-Authored-By: Claude Opus 4.6 --- install.sh | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/install.sh b/install.sh index 1c686a0..0735d76 100755 --- a/install.sh +++ b/install.sh @@ -361,16 +361,12 @@ if [[ -z "$WSH_CMD" ]]; then exit 1 fi -# Wave Terminal v0.14.0+ token exchange for cmd controller blocks +# Wave Terminal v0.14.0+ token exchange for cmd controller blocks. +# wsh token outputs shell code setting multiple env vars needed by wsh +# commands (not just WAVETERM_JWT), so eval is required here — matching +# Wave's own integration at ~/Library/Application Support/waveterm/shell/bash/.bashrc if [[ -z "${WAVETERM_JWT:-}" && -n "${WAVETERM_SWAPTOKEN:-}" ]]; then - token_output="$("$WSH_CMD" token "$WAVETERM_SWAPTOKEN" bash 2>/dev/null || true)" - jwt_line=$(printf '%s\n' "$token_output" | grep -E '^export WAVETERM_JWT=' 2>/dev/null || true) - if [[ -n "${jwt_line:-}" ]]; then - WAVETERM_JWT=${jwt_line#export WAVETERM_JWT=} - WAVETERM_JWT=${WAVETERM_JWT#\"} - WAVETERM_JWT=${WAVETERM_JWT%\"} - export WAVETERM_JWT - fi + eval "$("$WSH_CMD" token "$WAVETERM_SWAPTOKEN" bash 2>/dev/null)" || true fi ERR_FILE=$(mktemp)