Skip to content

Conversation

@gmegidish
Copy link
Member

@gmegidish gmegidish commented Jan 12, 2026

Summary by CodeRabbit

  • Bug Fixes

    • Ensures the network tunnel is started before rebooting, launching WDA, or installing/uninstalling apps; treats an already-running tunnel as non-fatal and adds clearer wrapped errors and logs.
  • New Features

    • Adds JSON-RPC methods to list and terminate apps remotely.
  • Improvements

    • Auto-selection now prefers and caches a single online device; clearer handling when none or multiple online devices exist.
  • Documentation

    • Documents the new JSON-RPC methods with examples.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Jan 12, 2026

Warning

Rate limit exceeded

@gmegidish has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 4 minutes and 25 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between b9d3c07 and 79fd63a.

📒 Files selected for processing (2)
  • commands/commands.go
  • devices/ios.go

Walkthrough

Ensure the iOS tunnel is started before device operations by calling startTunnel() from Reboot, LaunchWda, ListApps, InstallApp, and UninstallApp. StartTunnel() now delegates to TunnelManager.StartTunnel(), treats an "already running" sentinel error as non-fatal (logged), and wraps other failures.

Changes

Cohort / File(s) Change Summary
iOS device operations
devices/ios.go
Reboot(), LaunchWda(), ListApps(), InstallApp(), UninstallApp() now call startTunnel() and propagate errors; logging adjusted around tunnel startup.
Tunnel manager
devices/ios/tunnel-manager.go
Introduces exported sentinel error ErrTunnelAlreadyRunning (errors.New("tunnel is already running")) and returns it from start routines instead of a formatted error; refactors StartTunnelWithCallback to use this sentinel.
Server RPC, docs, and command selection
server/server.go, docs/jsonrpc_README.md, commands/commands.go
Adds JSON-RPC methods apps_terminate and apps_list with param structs and handlers; documents apps_terminate/apps_list in JSON-RPC README; FindDeviceOrAutoSelect now prefers cached online device and restricts auto-selection to devices in state "online".

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the primary fix: ensuring the tunnel is properly initialized when listing apps on iOS, which is reflected across multiple files in the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @devices/ios.go:
- Around line 228-241: The code currently detects the "already running" tunnel
by comparing err.Error() to a string; create a sentinel error (e.g., var
ErrTunnelAlreadyRunning = errors.New("tunnel is already running")) in
tunnel-manager.go and return that sentinel from StartTunnel() when appropriate,
then update the check in devices/ios.go to use errors.Is(err,
ErrTunnelAlreadyRunning) (or errors.As if you prefer a typed error) instead of
string comparison; this replaces brittle string matching with a stable sentinel
and ensures proper error detection across packages.
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 33e663d and 0be259a.

📒 Files selected for processing (1)
  • devices/ios.go
🧰 Additional context used
🧬 Code graph analysis (1)
devices/ios.go (1)
utils/logger.go (1)
  • Verbose (19-23)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: lint
  • GitHub Check: Analyze (go)
🔇 Additional comments (5)
devices/ios.go (5)

116-121: LGTM!

The tunnel initialization for Reboot() follows the consistent pattern used across other device operations, with proper error handling and context.


375-380: LGTM!

Adding tunnel initialization here ensures LaunchWda works correctly when called directly (not through StartAgent). The redundant call when invoked via StartAgent is handled gracefully by the "already running" check in startTunnel().


600-605: Core fix for the PR objective.

This addresses the missing tunnel initialization that was causing ListApps to fail on iOS 17+ devices. The pattern is consistent with other device operations.


695-700: LGTM!

Tunnel initialization for InstallApp follows the consistent pattern and ensures iOS 17+ compatibility.


723-728: LGTM!

Tunnel initialization for UninstallApp completes the consistent pattern across all device operations requiring tunnel connectivity.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
devices/ios.go (1)

223-244: Fix build: import errors and qualify ios.ErrTunnelAlreadyRunning.
Current code won’t compile (undefined: errors / undefined: ErrTunnelAlreadyRunning).

Proposed fix
 import (
 	"context"
+	"errors"
 	"fmt"
 	"io"
 	"strconv"
 	"strings"
 	"time"
@@
 func (d *IOSDevice) startTunnel() error {
@@
 	err := d.tunnelManager.StartTunnel()
 	if err != nil {
 		// check if it's the "already running" error, which is fine
 
-		if errors.Is(err, ErrTunnelAlreadyRunning) {
+		if errors.Is(err, ios.ErrTunnelAlreadyRunning) {
 			utils.Verbose("Tunnel already running for this device")
 			return nil
 		}
 		return fmt.Errorf("failed to start tunnel: %w", err)
 	}
devices/ios/tunnel-manager.go (1)

60-107: StartTunnelWithCallback returns success before the initial tunnel update completes.

The UpdateTunnels call runs inside a goroutine, so StartTunnelWithCallback() returns nil immediately—before the tunnel has actually started. Callers expect the function name and synchronous return semantics to mean "tunnel is ready after this call returns," but startup failures are only reported asynchronously via the onProcessDied callback. This breaks downstream "ensure tunnel then proceed" flows and hides startup errors from callers that check the return value.

Since UpdateTunnels is a synchronous blocking call that completes when the update finishes, it should be called before the goroutine launches so that StartTunnelWithCallback() only returns nil after the initial tunnel setup succeeds.

🤖 Fix all issues with AI agents
In @devices/ios.go:
- Around line 241-243: Replace the fixed sleep after StartTunnel() with a
polling loop that calls tunnelMgr.FindTunnel(d.Udid) until it returns a valid
tunnel (e.g., tunnelInfo.Udid != "") or a timeout is reached; locate the call to
StartTunnel() and the subsequent time.Sleep(1 * time.Second) and instead
repeatedly call tunnelMgr.FindTunnel(d.Udid) with a short interval (e.g.,
100-200ms) and an overall timeout, returning an error if the tunnel isn't ready
in time and proceeding only when FindTunnel reports the tunnel is available.

In @devices/ios/tunnel-manager.go:
- Line 16: Add the missing import for the standard errors package so
ErrTunnelAlreadyRunning compiles: in devices/ios/tunnel-manager.go add `import
"errors"` (or include "errors" in the existing import block) so the var
ErrTunnelAlreadyRunning = errors.New("tunnel is already running") can reference
errors.
🧹 Nitpick comments (1)
devices/ios.go (1)

113-120: Avoid double-wrapping failed to start tunnel at call sites.
Since startTunnel() already returns fmt.Errorf("failed to start tunnel: %w", err), wrapping again in callers will likely produce duplicated context.

Example adjustment (pattern)
 err := d.startTunnel()
 if err != nil {
-	return fmt.Errorf("failed to start tunnel: %w", err)
+	return err
 }

Also applies to: 368-381, 598-606, 693-701, 721-729

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0be259a and 8956f24.

📒 Files selected for processing (2)
  • devices/ios.go
  • devices/ios/tunnel-manager.go
🧰 Additional context used
🧬 Code graph analysis (1)
devices/ios.go (2)
devices/ios/tunnel-manager.go (1)
  • ErrTunnelAlreadyRunning (16-16)
utils/logger.go (1)
  • Verbose (19-23)
🪛 GitHub Actions: PR #140
devices/ios/tunnel-manager.go

[error] 16-16: Go build failed: undefined: errors in devices/ios/tunnel-manager.go:16

🪛 GitHub Check: security
devices/ios/tunnel-manager.go

[failure] 16-16:
undefined: errors

devices/ios.go

[failure] 234-234:
undefined: ErrTunnelAlreadyRunning


[failure] 234-234:
undefined: errors

🪛 GitHub Check: test
devices/ios/tunnel-manager.go

[failure] 16-16:
undefined: errors

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: lint

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
devices/ios.go (1)

117-121: Receiver type mismatch: value receiver calling pointer method.

Reboot() uses a value receiver (d IOSDevice) (line 114), but startTunnel() is defined with a pointer receiver (d *IOSDevice). While Go allows this call (it takes the address automatically), it's inconsistent with other methods that also call startTunnel() (e.g., LaunchWda at line 369 also uses value receiver). This works but is a code smell—consider using pointer receivers consistently for methods that mutate state or call pointer-receiver methods.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8956f24 and 3ac0509.

📒 Files selected for processing (2)
  • devices/ios.go
  • devices/ios/tunnel-manager.go
🧰 Additional context used
🧬 Code graph analysis (1)
devices/ios.go (2)
devices/ios/tunnel-manager.go (1)
  • ErrTunnelAlreadyRunning (17-17)
utils/logger.go (1)
  • Verbose (19-23)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: lint
🔇 Additional comments (7)
devices/ios/tunnel-manager.go (2)

17-18: LGTM! Well-designed sentinel error.

Using a sentinel error for "tunnel already running" is idiomatic Go and enables callers to use errors.Is() for reliable comparison, which is exactly what startTunnel() in devices/ios.go now does.


61-63: LGTM! Consistent with the new sentinel error pattern.

Returning the sentinel ErrTunnelAlreadyRunning here properly enables the caller to distinguish this non-fatal condition from actual errors.

devices/ios.go (5)

224-245: LGTM! Clean error handling with sentinel error.

The use of errors.Is(err, ios.ErrTunnelAlreadyRunning) is correct and properly treats "already running" as a non-fatal condition. The early return for devices not requiring tunnel (iOS < 17) is efficient.

One minor note: the 1-second sleep (line 243) is a timing workaround. If tunnel readiness becomes unreliable, consider polling for tunnel availability instead.


377-381: LGTM! Tunnel ensured before WDA operations.

The tunnel is properly started before obtaining the enhanced device connection. This aligns with the PR objective.


599-606: Core fix: tunnel now established before listing apps.

This addresses the PR objective—ListApps() was missing the startTunnel() call, causing failures on iOS 17+ devices that require a tunnel for device communication.


697-701: LGTM! Tunnel ensured for app installation.

Consistent with the other device operations that require tunnel for iOS 17+.


725-729: LGTM! Tunnel ensured for app uninstallation.

Consistent with the other device operations that require tunnel for iOS 17+.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @commands/commands.go:
- Around line 70-75: deviceCache is a global map accessed concurrently and
iterated without synchronization, causing a data race and nondeterministic
selection; add a sync.RWMutex (e.g., deviceCacheMu) to protect it, wrap the
iteration in the shown snippet with deviceCacheMu.RLock()/RUnlock(), and change
every location that writes or mutates deviceCache (the places that assign or
delete from deviceCache) to use deviceCacheMu.Lock()/Unlock(); additionally, if
you need a deterministic "first online device" choose a stable selection
strategy (e.g., sort keys or use an explicit priority field) instead of relying
on map iteration order.
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3ac0509 and b9d3c07.

📒 Files selected for processing (3)
  • commands/commands.go
  • docs/jsonrpc_README.md
  • server/server.go
🧰 Additional context used
🧬 Code graph analysis (1)
server/server.go (1)
commands/apps.go (4)
  • AppRequest (8-11)
  • TerminateAppCommand (35-53)
  • ListAppsRequest (56-58)
  • ListAppsCommand (61-73)
🪛 markdownlint-cli2 (0.18.1)
docs/jsonrpc_README.md

148-148: Unordered list indentation
Expected: 2; Actual: 1

(MD007, ul-indent)


148-148: Hard tabs
Column: 1

(MD010, no-hard-tabs)


149-149: Unordered list indentation
Expected: 2; Actual: 1

(MD007, ul-indent)


149-149: Hard tabs
Column: 1

(MD010, no-hard-tabs)


150-150: Unordered list indentation
Expected: 4; Actual: 2

(MD007, ul-indent)


150-150: Hard tabs
Column: 1

(MD010, no-hard-tabs)


151-151: Unordered list indentation
Expected: 4; Actual: 2

(MD007, ul-indent)


151-151: Hard tabs
Column: 1

(MD010, no-hard-tabs)


154-154: Unordered list indentation
Expected: 2; Actual: 1

(MD007, ul-indent)


154-154: Hard tabs
Column: 1

(MD010, no-hard-tabs)


155-155: Unordered list indentation
Expected: 2; Actual: 1

(MD007, ul-indent)


155-155: Hard tabs
Column: 1

(MD010, no-hard-tabs)


156-156: Unordered list indentation
Expected: 4; Actual: 2

(MD007, ul-indent)


156-156: Hard tabs
Column: 1

(MD010, no-hard-tabs)


264-264: Hard tabs
Column: 1

(MD010, no-hard-tabs)


265-265: Hard tabs
Column: 1

(MD010, no-hard-tabs)


272-272: Hard tabs
Column: 1

(MD010, no-hard-tabs)


273-273: Hard tabs
Column: 1

(MD010, no-hard-tabs)

🔇 Additional comments (5)
server/server.go (3)

191-194: LGTM!

New method dispatch entries follow the established pattern and correctly route to the new handlers.


476-483: LGTM!

Parameter structs are consistent with existing definitions like AppsLaunchParams and follow the same JSON tag conventions.


742-783: LGTM!

Both handlers follow established patterns in this file:

  • handleAppsTerminate mirrors handleAppsLaunch with required params validation
  • handleAppsList correctly makes params optional to support device auto-selection via FindDeviceOrAutoSelect
docs/jsonrpc_README.md (2)

147-157: Documentation accurately reflects the new API methods.

The new method entries for apps_terminate and apps_list follow the existing documentation style and correctly describe the parameters. The static analysis warnings about tabs and indentation apply to the entire file's pre-existing style, which you've correctly maintained for consistency.


259-274: LGTM!

Curl examples for the new methods are consistent with existing examples and provide clear usage patterns.

@gmegidish gmegidish merged commit da5f6a8 into main Jan 13, 2026
11 checks passed
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