Skip to content

fix(run): synthesize DevTools URL from VM service URI - #44

Open
jainam-bhavasar wants to merge 1 commit into
Antoinegtir:masterfrom
jainam-bhavasar:codex/fix-devtools-url-capture
Open

fix(run): synthesize DevTools URL from VM service URI#44
jainam-bhavasar wants to merge 1 commit into
Antoinegtir:masterfrom
jainam-bhavasar:codex/fix-devtools-url-capture

Conversation

@jainam-bhavasar

Copy link
Copy Markdown

Why

Closes #43.

flutter run --machine reliably emits the VM Service URI via app.debugPort, but it does not always print a human-readable DevTools URL. Today the d keybind depends entirely on capturing app.devTools: <url> from Flutter output. When that line never arrives, the shortcut reports "DevTools not ready" even though memory polling, network polling, and the VM Service itself are all healthy.

What

  • New vm_service_uri slot on DeviceSession — stores the last VM Service URI from AppStarted / app.debugPort.
  • send_devtools_serve() — asks the Flutter daemon to serve DevTools; the {"result":{"host":"…","port":…}} response is parsed by parse_daemon_line and surfaced as an app.devToolsServer: debug log.
  • Synthesize the DevTools URL — after AppStarted, probe localhost ports 9100–9199 for an already-running DevTools server. If none is found, send devtools.serve and poll until the server appears.
  • Last-resort probe in open_devtools_all — if d is pressed after the startup window, one more probe + daemon request before showing the warning.
  • Parse both DevTools URL forms — the structured app.devTools: <url> prefix and the human-readable "Flutter DevTools … is available at: <url>" sentence.
  • app.debugPort field priority — check devToolsUrl and url before wsUri / uri so newer Flutter SDK fields are picked up.

No new workspace dependency — url is already declared in the root Cargo.toml.

How to test

cargo clippy --workspace --all-targets -- -D warnings
cargo test --workspace --locked

Live:

# Kill any leftover DevTools/DDS processes for a clean test
pkill -f "dart devtools --no-launch-browser" || true
pkill -f "dds_aot.dart.snapshot" || true

# Run
target/debug/flutter-cli run -d emulator-5554 --no-picker --no-wifi --no-tui \
  -- -t lib/main.dart --flavor staging --dart-define env=dev

Expected output after AppStarted:

app.devTools: http://127.0.0.1:9100/?uri=http%3A%2F%2F127.0.0.1%3A58565%2F…
VM Service ready

Files changed

File What
crates/fl-flutter/src/daemon.rs send_devtools_serve() method
crates/fl-flutter/src/parse.rs Parse devtools.serve response, wider app.debugPort field lookup
crates/flutter-cli/Cargo.toml url.workspace = true
crates/flutter-cli/src/multi.rs VM URI slot, URL synthesis helpers, fallback chain, 11 new tests

@Antoinegtir Antoinegtir left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Thanks for digging into this — issue #43 is real, and the core idea (parse the devtools.serve response + widen the app.debugPort field lookup) is the right one. I rebased it locally on current master and it builds + the full test suite passes, so the foundation is solid.

Before I can merge, a few things need to change. The PR is quite a bit heavier than the fix needs to be, and master has since gained a helper that overlaps with this PR. Details inline — happy to merge once these are addressed. 🙏

// the id because the daemon may reuse the same shape for
// future methods — and the worst case is a harmless extra
// debug log line.
if let Some(result) = obj.get("result") {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Matching the JSON-RPC response by its {host, port} shape instead of the request id is fragile — the comment above even acknowledges it. devtools.serve is sent with id:2 (and app.restart already uses id:1), so please key the response on obj.get("id") == 2 and only fall back to the shape check if the id is absent. As written, any future daemon response that happens to carry {host, port} gets mis-tagged as a DevTools server.

event_tx_for_task
.send(AppEvent::Flutter(FlutterEvent::Log {
level: LogLevel::Debug,
message: format!("app.devTools: {devtools_uri}"),

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Re-emitting a synthetic app.devTools: <url> Log event just so the existing string-match loop picks it up is a workaround stacked on a workaround. You already hold devtools_slot in this scope — set it directly (*devtools_slot.lock().await = Some(devtools_uri)) instead of round-tripping through a fake log line. That also removes one of the (currently four) code paths that can write that slot, which makes the races easier to reason about.

/// can synthesise the DevTools URL without waiting for the daemon to
/// print one.
async fn devtools_uri_from_existing_server(vm_service_uri: &str) -> Option<String> {
for port in 9100..9200 {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Scanning 100 ports sequentially with a 50 ms timeout each means a d keypress can block for up to ~5 s before it shows "not ready" — today that path is instant. Please drop the brute-force scan: the devtools.serve daemon response already hands you the exact host+port, so the synthesis path doesn't need to guess. If you want to keep any probe at all, bound it hard (a couple of ports, sub-second total) and keep it off the keypress path.

/// Convert a VM Service URI from its WebSocket form (`ws://…/ws`) to
/// the plain HTTP origin that DevTools expects in its `?uri=` param.
/// Strips the trailing `/ws` path segment and swaps the scheme.
fn vm_service_uri_to_http(uri: &str) -> Option<String> {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

master now has ws_uri_to_http in crates/fl-tui/src/runner.rs (added for the IntelliJ auto-attach feature) that does exactly this ws/wss → http conversion + /ws strip. Please don't add a second copy — lift one shared helper into fl-core (or fl-flutter) and call it from both crates. Two independent implementations of the same URL surgery will drift apart.

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.

d shortcut shows "DevTools not ready" even after VM Service is up

3 participants