Skip to content

Conversation

@leggetter
Copy link
Collaborator

This pull request introduces server health monitoring to the CLI proxy, allowing users to be notified when their local server becomes unreachable or recovers. The implementation includes a health check utility, periodic monitoring, and renderer updates for both interactive and simple output modes. It also includes dependency updates and minor improvements to output messages.

Server Health Monitoring

  • Added a health check utility (pkg/listen/healthcheck/healthcheck.go) that performs TCP connection checks and formats user-friendly health status messages. Includes comprehensive unit tests (pkg/listen/healthcheck/healthcheck_test.go). [1] [2]
  • Integrated initial health check and warning message before starting the proxy in pkg/listen/listen.go, using a 3-second timeout for local development.
  • Implemented periodic health checks in the proxy (pkg/listen/proxy/proxy.go), with adaptive intervals based on server status, and renderer notifications on state changes. [1] [2] [3] [4] [5] [6] [7] [8] [9]
  • Updated the TUI model and update logic to track and display server health status. [1] [2] [3]

Dependency Updates

  • Updated dependencies in go.mod: github.com/BurntSushi/toml to v1.6.0, github.com/spf13/cobra to v1.10.2, golang.org/x/sys to v0.40.0, and golang.org/x/term to v0.38.0. [1] [2]

Output Message Improvements

  • Clarified the description of the --output flag in pkg/cmd/listen.go to specify that "quiet" mode now shows errors and warnings only.
  • Updated warning icons and messaging for connection load and server health status in simple renderer.

These changes collectively improve user experience by providing real-time feedback on server availability and making CLI output clearer and more actionable.

dependabot bot and others added 22 commits December 4, 2025 12:16
Bumps [github.com/spf13/cobra](https://github.com/spf13/cobra) from 1.10.1 to 1.10.2.
- [Release notes](https://github.com/spf13/cobra/releases)
- [Commits](spf13/cobra@v1.10.1...v1.10.2)

---
updated-dependencies:
- dependency-name: github.com/spf13/cobra
  dependency-version: 1.10.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
Bumps [github.com/BurntSushi/toml](https://github.com/BurntSushi/toml) from 1.5.0 to 1.6.0.
- [Release notes](https://github.com/BurntSushi/toml/releases)
- [Commits](BurntSushi/toml@v1.5.0...v1.6.0)

---
updated-dependencies:
- dependency-name: github.com/BurntSushi/toml
  dependency-version: 1.6.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
Add health monitoring to the listen command to verify local servers
are reachable before and during webhook forwarding. This helps users
quickly identify configuration issues without waiting for webhooks.

Features:
- Initial TCP connection check at startup (non-blocking warning)
- Adaptive periodic monitoring (30s healthy, 5s unhealthy)
- State-aware notifications (only on status changes)
- TUI integration with status bar health indicator
- Support for all output modes (interactive, compact, quiet)

Implementation:
- Uses net.DialTimeout for lightweight TCP checks (3s timeout)
- New healthcheck package with comprehensive unit tests
- Renderer interface extended with OnServerHealthChanged callback
- Proxy runs background health monitor goroutine
- TUI displays server status in real-time

All unit tests passing. No breaking changes.
Address all 7 issues from PR feedback:

1. Reduce TUI startup delay from 2s to 500ms
2. Remove unused healthCheckInterval field from Proxy struct
3. Fix race condition by using atomic.Bool for serverHealthy field
4. Remove unnecessary 500ms sleep after starting health monitor
5. Move health monitor start to after initial health check
6. Simplify checkServerHealth function implementation
7. Remove unused HealthUnknown constant

All changes improve code quality, fix concurrency issues, and reduce
unnecessary delays in the CLI startup process.
Address 5 additional issues from PR #186 review:

1. Add defensive nil check in FormatHealthMessage to prevent panic
2. Remove misleading TestCheckServerHealth_Timeout test that didn't
   actually test timeout behavior (TCP handshake succeeded immediately)
3. Fix goroutine leak by ensuring health monitor only starts once,
   not on every websocket reconnection
4. Use atomic.Swap instead of separate Load/Store to prevent race
   condition in state change detection
5. Reorganize into healthcheck subpackage and deduplicate logic
Structural changes:
- Created pkg/listen/healthcheck/ subpackage to avoid import cycles
- Moved health check implementation to subpackage
- Added re-export wrapper in pkg/listen/healthcheck.go for compatibility
- Updated proxy to use healthcheck package function

All tests passing. No API changes.
The lastHealthCheck field was written but never read anywhere in the
codebase. Removing it simplifies the code without any functional impact.
TUI (Interactive Mode):
- Remove server health indicator from bottom status bar
- Add conditional warning in connection header (only when unhealthy)
- Shows: '⚠ {URL} is unreachable. Check the server is running' in yellow
- Silent when server is healthy (no message displayed)

Compact/Quiet Modes:
- Update health messages to use consistent formatting
- Healthy: '✓ {URL} is reachable'
- Unhealthy: '⚠ Warning: {URL} is unreachable'

Health Check:
- Extract health check intervals into named constants
- Healthy server check: 15s (reduced from 30s)
- Unhealthy server check: 5s (unchanged)
Replace warning icon (⚠) with yellow dot (●) across all renderers:
- TUI mode: Flashing yellow dot that pulses like 'Connected' animation
- Simple mode: Yellow '● Warning:' and '● WARNING:' text
- Health check: Yellow '● Warning:' with dot and label styled

Replace checkmark (✓) with arrow (→) for server recovery messages:
- Consistent with existing UI conventions
- Used in both health check and simple renderer
Changes:
- TUI view: Dot flashes (●/○) to draw attention to health issues
- Warning text uses default color, only '● Warning:' is yellow
- Warning now visible in both expanded and collapsed header states
- Health check messages apply yellow color to '● Warning:' prefix
- Recovery messages use → instead of ✓ for consistency
- Updated tests to check for new dot and arrow indicators
Addresses user feedback for improved warning visibility and consistency
Change 'WARNING:' to 'Warning:' to match existing codebase conventions:
- pkg/listen/tui/view.go: Updated both expanded and collapsed headers
- pkg/listen/proxy/renderer_simple.go: Updated simple renderer
- pkg/listen/healthcheck/healthcheck.go: Updated health check messages
- pkg/listen/healthcheck/healthcheck_test.go: Updated test expectations

All warning messages in the codebase use 'Warning:' (capital W, lowercase rest)
Replace ticker.Stop() + time.NewTicker() pattern with ticker.Reset()
to avoid resource leaks when adjusting health check intervals.
The code was creating new tickers after stopping old ones, which
leaks the ticker's internal goroutine. ticker.Reset() properly
reuses the existing ticker and handles the stop internally.
Changes:
- Use ticker.Reset() instead of creating new tickers
- Remove ticker.Stop() call (Reset handles it internally)
- Maintains same behavior: 15s interval when healthy, 5s when unhealthy
Critical fixes:
- Add detailed comment explaining hasConnectedOnce guard prevents goroutine leaks on reconnects
- Verify port detection logic correctly handles URLs with explicit ports

Documentation improvements:
- Document 3-second timeout assumptions in listen.go for local development context
- Document timeout parameter guidance in healthcheck.CheckServerHealth function
- Fix re-export comment from 'backward compatibility' to 'convenience'
Test coverage:
- Add TestCheckServerHealth_PortInURL to verify explicit ports are not overwritten
- Confirms fix for edge case where localhost:8080 would not become localhost:8080:80
All tests passing: go test ./pkg/listen/...
feat: add TCP-based local server health checks to listen command
Fixed three issues in TUI status bar:

1. Duplicate '[q] Quit' on narrow terminals:
   - Added .Width(m.width) constraint to statusBarStyle.Render() calls
   - Prevents text wrapping when terminal width is constrained
   - Status bar now truncates instead of wrapping to new line

2. Missing '[q] Quit' on wide terminals:
   - Added '• [q] Quit' to wide terminal status messages
   - Now consistently shows quit option across all terminal sizes

3. Width threshold adjustment:
   - Adjusted isNarrow threshold from 100 to 108 columns
   - Prevents wrapping when switching from compact to full-text mode
   - Full menu text only appears when there's enough space to fit
Changes in renderStatusBar() function in pkg/listen/tui/view.go
Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.38.0 to 0.40.0.
- [Commits](golang/sys@v0.38.0...v0.40.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sys
  dependency-version: 0.40.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
fix: status bar duplicates and width handling
…m/BurntSushi/toml-1.6.0

chore(deps): bump github.com/BurntSushi/toml from 1.5.0 to 1.6.0
…g/x/sys-0.40.0

chore(deps): bump golang.org/x/sys from 0.38.0 to 0.40.0
…m/spf13/cobra-1.10.2

chore(deps): bump github.com/spf13/cobra from 1.10.1 to 1.10.2
Bumps [golang.org/x/term](https://github.com/golang/term) from 0.37.0 to 0.38.0.
- [Commits](golang/term@v0.37.0...v0.38.0)

---
updated-dependencies:
- dependency-name: golang.org/x/term
  dependency-version: 0.38.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
…g/x/term-0.38.0

chore(deps): bump golang.org/x/term from 0.37.0 to 0.38.0
@leggetter leggetter merged commit bc42dc7 into main Jan 9, 2026
10 checks passed
leggetter added a commit that referenced this pull request Jan 12, 2026
Merge pull request #189 from hookdeck/next
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants