Skip to content

feat: add device events and file access#5

Open
mozharovsky wants to merge 3 commits into
mainfrom
feat/device-access-0-2
Open

feat: add device events and file access#5
mozharovsky wants to merge 3 commits into
mainfrom
feat/device-access-0-2

Conversation

@mozharovsky
Copy link
Copy Markdown
Member

@mozharovsky mozharovsky commented Jun 3, 2026

Summary

  • add usbmux-backed device attach/detach event streams on USBMuxClient and DeviceClient
  • expand AFC into a general file API for listing, metadata, download, upload, remove, and move operations
  • add HouseArrest container access that vends app Documents/full containers as AFCClient instances
  • add rorkdevice watch and rorkdevice files ... CLI commands
  • update README and roadmap for the 0.2 device events/file access slice

Validation

  • swift test
  • git diff --check
  • swift run rorkdevice --help
  • swift run rorkdevice files list --help

Summary by CodeRabbit

  • New Features

    • Device attach/detach streaming and high-level device event API
    • AFC file operations: list, info, move, read/download, and device-wide AFC access
    • HouseArrest app-container vending for app document/container access
    • CLI: watch command plus files suite (list, info, pull, push, mkdir, rm, mv) and file-access options
  • Documentation

    • Roadmap updated for v0.2.0 and README expanded with CLI file-access usage
  • Tests

    • Added unit/integration tests and enhanced fake daemon for device, usbmux, AFC, and HouseArrest flows

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 3, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

Adds device attach/detach event streaming, high-level AFC filesystem operations and metadata types, HouseArrest app-container vending, CLI watch/files commands, and corresponding tests plus fake-daemon support.

Changes

Device Attachment Events, File Access, and Monitoring

Layer / File(s) Summary
USBMux device event streaming and parsing
Sources/RorkDevice/USBMux/USBMuxClient.swift, Tests/RorkDeviceTests/FakeUSBMuxDaemon.swift, Tests/RorkDeviceTests/USBMuxClientIntegrationTests.swift
USBMuxClient.deviceEvents() streams usbmux Listen attach/detach messages as an AsyncThrowingStream; response decoding, device record parsing, and result validation are centralized into helpers; USBMuxDevice gains a public initializer; fake daemon can emit configured device events and integration tests verify the stream and listen lifecycle.
High-level DeviceEvent and DeviceClient wrapper
Sources/RorkDevice/Public/Device.swift, Sources/RorkDevice/Public/RorkDevice.swift, Tests/RorkDeviceTests/DeviceClientIntegrationTests.swift
Introduces public DeviceEvent (attached/detached) and DeviceClient.deviceEvents() which maps low-level USBMuxDeviceEvent into DeviceEvent and exposes an async stream; integration test asserts mapped attach/detach outputs.
AFC file operations: listing, metadata, move, download
Sources/RorkDevice/AFC/AFCClient.swift, Tests/RorkDeviceTests/AFCClientTests.swift
Adds AFCClient APIs: directoryContents, fileInfo, movePath, contentsOfFile, downloadFile; introduces AFCItemType and AFCFileInfo; adds private helpers for data-path operations and fixed-chunk reads; extends operation codes; tests validate parsing, payloads, streaming reads, and downloads.
HouseArrest container access and DeviceSession integration
Sources/RorkDevice/HouseArrest/HouseArrestClient.swift, Sources/RorkDevice/Public/DeviceSession.swift, Tests/RorkDeviceTests/HouseArrestClientTests.swift, Tests/RorkDeviceTests/DeviceClientIntegrationTests.swift, Tests/RorkDeviceTests/FakeUSBMuxDaemon.swift
Implements HouseArrestClient vend flow with in-flight guards and response validation; adds HouseArrestScope; DeviceSession gains openAFC() and openApplicationContainer() and a raw service-start overload; fake daemon routes/records HouseArrest requests and maps service/port; tests cover vend success, retry, error mapping, and end-to-end container access.
CLI watch and files command suite
Sources/RorkDeviceCLI/RorkDeviceCommand.swift, Tests/RorkDeviceCLITests/RorkDeviceCLITests.swift
Root rorkdevice command registers watch and files; FileAccessOptions selects AFC vs HouseArrest; watch streams device events; files parent with list, info, pull, push, mkdir, rm, mv subcommands perform remote AFC/HouseArrest operations; tests cover help text and argument parsing.
Fake daemon Listen lifecycle & HouseArrest recording
Tests/RorkDeviceTests/FakeUSBMuxDaemon.swift, Tests/RorkDeviceTests/USBMuxClientIntegrationTests.swift
Fake daemon accepts configured device events and listen-response behavior, records listen connection state and house-arrest requests, emits USBMux event dictionaries, and provides hooks used by integration tests for listen completion and cancellation.
Roadmap and README updates
Docs/Roadmap.md, README.md
Adds 0.2.0 roadmap entries for device events, AFC operations, HouseArrest, and CLI watch/files; reshapes future milestones to emphasize pairing/trust flows and discovery; revises compatibility strategy; README documents expanded CLI features and usage examples.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • rorkai/rork-device#1: Directly extends the AFC client foundation and is closely related to the AFC additions in this PR.

Poem

🐰 Devices hop in, devices hop out,
I stream their steps with tiny cheer,
Files scurry through AFC halls about,
HouseArrest opens doors near and clear,
CLI watches, moves, and greets each dear.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.48% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title accurately reflects the main changes: adding device event streaming and file access capabilities through AFC and HouseArrest.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/device-access-0-2

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

@mozharovsky mozharovsky marked this pull request as ready for review June 3, 2026 15:24
@mozharovsky mozharovsky self-assigned this Jun 3, 2026
Copy link
Copy Markdown

@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: 8

Caution

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

⚠️ Outside diff range comments (1)
README.md (1)

53-54: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Remove “device events” from the planned-services list.

This sentence now conflicts with the Features section and CLI docs where watch is already available.

Suggested doc tweak
-and planned services such as pairing creation, device events, syslog, crash
+and planned services such as pairing creation, syslog, crash
 reports, debugserver, developer image mounting, backup, and restore.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@README.md` around lines 53 - 54, Remove the phrase "device events" from the
planned-services list in the README so it no longer conflicts with the existing
Features/CLI docs; locate the sentence containing "and planned services such as
pairing creation, device events, syslog, crash reports, debugserver, developer
image mounting, backup, and restore." and delete only the "device events" item
(and adjust punctuation/commas accordingly) so the list remains grammatically
correct.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Docs/Roadmap.md`:
- Line 45: Update the roadmap wording that currently says "watching devices and
moving files" to a broader, more accurate phrase such as "watching devices and
managing files" to reflect the full file command suite
(list/info/pull/push/mkdir/rm/mv); replace the existing phrase wherever it
appears in the Docs/Roadmap.md content to ensure the scope is correctly
communicated.

In `@README.md`:
- Line 118: The "files" subcommand description is too narrow; update the README
entry for the "files" subcommand to list all supported operations (e.g., browse,
copy, upload, metadata, mkdir, remove, move) and give a short phrase like
"Browse, copy, upload, inspect metadata, create directories, remove and move
files via AFC or HouseArrest" so users see the full capability; edit the line
that currently reads "files                   Browse and copy files through AFC
or HouseArrest." to the expanded description for the "files" subcommand.

In `@Sources/RorkDevice/HouseArrest/HouseArrestClient.swift`:
- Around line 26-37: The openApplicationContainer call vends the underlying
connection to AFC and must make the HouseArrestClient single-use; update
openApplicationContainer (and HouseArrestClient) to record that the connection
has been vended (e.g. set a Bool flag like isVended or nil out the connection)
immediately after validating the response and before returning
AFCClient(connection: connection), ensure subsequent calls to
openApplicationContainer check that flag/connection and throw a clear error
instead of attempting to write a plist into an AFC stream, and make the
flag/connection mutation thread-safe if HouseArrestClient can be used
concurrently.

In `@Sources/RorkDevice/USBMux/USBMuxClient.swift`:
- Around line 123-129: The catch block for read failures currently closes the
connection and always finishes the AsyncStream with an error (via
continuation.finish(throwing: error)), which causes a normal daemon socket close
to be reported as a failure; update the catch to detect the "remote/daemon
closed the listen socket" condition (instead of Task.isCancelled) and call
continuation.finish() for that case: in the catch associated with the read loop
where connection.close() is invoked, check the caught error for the specific
EOF/connection-closed error that usbmux emits and call continuation.finish() for
that error path, otherwise preserve the existing behavior (finish(throwing:
error) or finish() when Task.isCancelled). Ensure you reference the existing
symbols connection.close(), Task.isCancelled, continuation.finish() and
continuation.finish(throwing: error) when making the change.
- Around line 305-308: validateUSBMuxResult currently treats responses with a
missing or non-numeric "Number" field as success; update validateUSBMuxResult(_
response: [String: Any], operation: String) to require that response["Number"]
exists and is an NSNumber and throw a RorkDeviceError.transport when it's
missing or not an NSNumber (e.g. "usbmux \(operation) missing or invalid Number
in response") so that callers like deviceEvents() and connect(...) fail fast on
malformed control replies.

In `@Sources/RorkDeviceCLI/RorkDeviceCommand.swift`:
- Around line 89-105: The CLI currently ignores the --container flag when
bundleIdentifier is nil which can lead to operating on the wrong AFC root;
update RorkDeviceCommand to validate flags (e.g., in afcClient() or a
command-validate step) and reject/throw a user-facing error if container is true
while bundleIdentifier is nil, referencing the bundleIdentifier property and
container Flag so callers must supply --bundle-identifier when using
--container; ensure the error is clear and prevents proceeding (do not silently
fall back to session.openAFC()).

In `@Tests/RorkDeviceTests/AFCClientTests.swift`:
- Around line 173-176: The test currently decodes file bytes with
String(decoding:as:) which tolerates invalid UTF‑8; change the assertion to use
strict UTF‑8 decoding by creating a String via String(data:encoding: .utf8) from
the result of client.contentsOfFile(at:) and fail the test if that returns nil
(e.g., XCTAssertNotNil or guard+XCTFail), then compare the unwrapped string to
"hello world"; keep the existing check of connection.sent mapped with
afcOperation unchanged.

---

Outside diff comments:
In `@README.md`:
- Around line 53-54: Remove the phrase "device events" from the planned-services
list in the README so it no longer conflicts with the existing Features/CLI
docs; locate the sentence containing "and planned services such as pairing
creation, device events, syslog, crash reports, debugserver, developer image
mounting, backup, and restore." and delete only the "device events" item (and
adjust punctuation/commas accordingly) so the list remains grammatically
correct.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 716272a3-26f3-456c-809f-cbaf3baf72e1

📥 Commits

Reviewing files that changed from the base of the PR and between ecb9266 and c368ebe.

📒 Files selected for processing (15)
  • Docs/Roadmap.md
  • README.md
  • Sources/RorkDevice/AFC/AFCClient.swift
  • Sources/RorkDevice/HouseArrest/HouseArrestClient.swift
  • Sources/RorkDevice/Public/Device.swift
  • Sources/RorkDevice/Public/DeviceSession.swift
  • Sources/RorkDevice/Public/RorkDevice.swift
  • Sources/RorkDevice/USBMux/USBMuxClient.swift
  • Sources/RorkDeviceCLI/RorkDeviceCommand.swift
  • Tests/RorkDeviceCLITests/RorkDeviceCLITests.swift
  • Tests/RorkDeviceTests/AFCClientTests.swift
  • Tests/RorkDeviceTests/DeviceClientIntegrationTests.swift
  • Tests/RorkDeviceTests/FakeUSBMuxDaemon.swift
  • Tests/RorkDeviceTests/HouseArrestClientTests.swift
  • Tests/RorkDeviceTests/USBMuxClientIntegrationTests.swift

Comment thread Docs/Roadmap.md Outdated
Comment thread README.md Outdated
Comment thread Sources/RorkDevice/AFC/AFCClient.swift Outdated
Comment thread Sources/RorkDevice/HouseArrest/HouseArrestClient.swift Outdated
Comment thread Sources/RorkDevice/USBMux/USBMuxClient.swift
Comment thread Sources/RorkDevice/USBMux/USBMuxClient.swift
Comment thread Sources/RorkDeviceCLI/RorkDeviceCommand.swift
Comment thread Tests/RorkDeviceTests/AFCClientTests.swift
Copy link
Copy Markdown

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

🧹 Nitpick comments (1)
Tests/RorkDeviceTests/USBMuxClientIntegrationTests.swift (1)

86-88: ⚡ Quick win

Document the intent of the empty catch block.

The empty catch block appears intentional (to suppress cancellation errors during the test), but adding a brief comment would make this clearer to future maintainers.

📝 Suggested documentation
         let task = Task {
             do {
                 for try await _ in client.deviceEvents() {}
-            } catch {}
+            } catch {
+                // Suppress cancellation errors; we're testing cleanup, not error handling
+            }
         }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Tests/RorkDeviceTests/USBMuxClientIntegrationTests.swift` around lines 86 -
88, Add a brief comment inside the empty catch block that explains why
exceptions from client.deviceEvents() are intentionally suppressed (e.g., to
ignore CancellationError during test shutdown/teardown). Locate the catch
immediately following the for try await _ in client.deviceEvents() loop and add
a one-line comment such as "// Intentionally ignore cancellation/errors from
deviceEvents() during test teardown" so future maintainers understand the
intent.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@Tests/RorkDeviceTests/USBMuxClientIntegrationTests.swift`:
- Around line 86-88: Add a brief comment inside the empty catch block that
explains why exceptions from client.deviceEvents() are intentionally suppressed
(e.g., to ignore CancellationError during test shutdown/teardown). Locate the
catch immediately following the for try await _ in client.deviceEvents() loop
and add a one-line comment such as "// Intentionally ignore cancellation/errors
from deviceEvents() during test teardown" so future maintainers understand the
intent.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f41e9e71-873a-41cb-8854-ccf78a8c55c4

📥 Commits

Reviewing files that changed from the base of the PR and between 404eae7 and 10af940.

📒 Files selected for processing (7)
  • Sources/RorkDevice/AFC/AFCClient.swift
  • Sources/RorkDevice/HouseArrest/HouseArrestClient.swift
  • Sources/RorkDevice/Public/DeviceSession.swift
  • Sources/RorkDevice/USBMux/USBMuxClient.swift
  • Tests/RorkDeviceTests/DeviceClientIntegrationTests.swift
  • Tests/RorkDeviceTests/FakeUSBMuxDaemon.swift
  • Tests/RorkDeviceTests/USBMuxClientIntegrationTests.swift
🚧 Files skipped from review as they are similar to previous changes (5)
  • Tests/RorkDeviceTests/DeviceClientIntegrationTests.swift
  • Sources/RorkDevice/USBMux/USBMuxClient.swift
  • Sources/RorkDevice/HouseArrest/HouseArrestClient.swift
  • Sources/RorkDevice/AFC/AFCClient.swift
  • Tests/RorkDeviceTests/FakeUSBMuxDaemon.swift

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.

1 participant