Repository navigation
Conversation
3c3843b to
b6fbca2
Compare
[Medium impact] The latest change appears safe to merge and fixes the previously reported browser link. SummaryThe PR adds a bounded HTTP probe before opening the datastore. When a port is occupied, startup reports either the existing ActivityWatch server or a command for finding the other program.
Diagram%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[Load configuration] --> B[Probe configured address]
B --> C{Listener found?}
C -->|No| D[Open datastore and start Rocket]
C -->|ActivityWatch| E[Show server details and browser link]
C -->|Other program| F[Show port-owner command]
E --> G[Exit with code 1]
F --> G
Reviews (4) · Last reviewed commit: "fix(aw-server): suggest the bound addres..." · Reviewed by Greptile |
This comment has been minimized.
This comment has been minimized.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #773 +/- ##
===========================================
+ Coverage 70.81% 84.91% +14.10%
===========================================
Files 51 83 +32
Lines 2916 11854 +8938
===========================================
+ Hits 2065 10066 +8001
- Misses 851 1788 +937 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
CI-green and mergeable — waiting only on a maintainer click. This PR is ready to merge, but the bot has pull-only access to this repo and can't self-merge — surfacing it here so it isn't lost. The monitoring loop will stop re-flagging it now that this note is posted. |
🤖 AI code reviewAdds a pre-bind port probe to aw-server that connects to the configured address and sends an HTTP/1.0 GET /api/0/info, exiting with a helpful message when an ActivityWatch server or another program already owns the port. Adds unit tests for the probe covering status parsing, bare-LF responses, timeouts, and payload validation. Also makes small test-only changes in aw-sync: adds .expect() to sync_datastores calls in historical_edit.rs, marks setup_test as dead_code in sync.rs, and adjusts a test helper signature and hostname argument passing. Needs a look — P2 onlyConfidence 4/5 ℹ️ Consensus was degraded on this run: 2 of 3 passes answered, so findings were filtered at 2-of-2 agreement rather than 2-of-3 — less filtered than usual; 2 of 3 fan-out jobs answered, so the findings above were filtered against less evidence than the threshold assumes. 1 finding ·
|
| commit | score | findings | engine | when |
|---|---|---|---|---|
d1f4f00bfc1d |
4/5 | 3 | llm | 2026-10-02 06:01 UTC |
1e393fdfa806 |
4/5 | 2 | llm | 2026-10-02 07:07 UTC |
88714a2b01d7 |
4/5 | 1 | llm | 2026-10-02 13:40 UTC |
145ce16ff933 |
5/5 | 0 | llm | 2026-10-07 13:43 UTC |
145ce16ff933 |
3/5 | 2 | llm | 2026-10-09 04:12 UTC |
ad60676b278d |
4/5 | 1 | llm | 2026-10-09 05:23 UTC |
ba6b0c36ec32 |
4/5 | 1 | llm | 2026-10-09 06:32 UTC |
8e076e902f28 |
4/5 | 1 | llm | 2026-10-10 13:26 UTC |
8e076e902f28 |
4/5 | 2 | llm | 2026-10-10 15:05 UTC |
a48b4118fdf8 |
5/5 | 0 | llm | 2026-10-16 01:15 UTC |
Reviewed a48b4118fdf8 · openrouter/deepseek/deepseek-v4-flash-0731 · llm engine · 336s · about this reviewer
Maintainer commands
@TimeToBuildBob review (own line) — fresh review · @TimeToBuildBob fix — a worker acts on the findings. Once per comment; 👀 = received.
e097f70 to
7690104
Compare
|
Rebased onto master to resolve the merge conflict. The only conflict was in |
Addresses #169: explain that 'Address already in use' on port 5600 usually means ActivityWatch is already running, how to check, and per-OS commands to find the blocking process. Also notes the improved detection in aw-server-rust >= 0.14 (see ActivityWatch/aw-server-rust#773). Git-Session-Id: a3f7
88714a2 to
3182c44
Compare
|
Rebased onto master again ( Local |
|
Addressed the Greptile 0/5 review. Its findings were split across two different commits, so per item: Fixed in this push
Already fixed on the current head
Not in this PR's diff
Re-verified locally: |
|
@greptileai review |
|
Greptile came back 5/5 on the latest head (145ce16). All CI checks pass, PR is MERGEABLE/CLEAN. Ready for a maintainer merge — no further code changes needed. |
When port binding fails (AddrInUse), probe the occupied port with an HTTP GET /api/0/info before emitting the error. If the response identifies an ActivityWatch instance, log: 'ActivityWatch server <version> (<hostname>) is already running on port <N>; open http://localhost:<N> in your browser or stop it first' Otherwise log the existing generic message with a per-OS hint for finding the process (ss/lsof/netstat). Uses only std::net::TcpStream — no new dependency. Closes ActivityWatch/docs#169. Git-Session-Id: a3f7
- Fix field_reassign_with_default in extension_cors.rs by initializing AWConfig with all fields - Replace deprecated DateTime::from_utc() with Utc.from_utc_datetime() in tests - Mark unused sync_datastores() results with let _ = to allow intentional ignores - Change ptr_arg &PathBuf to &Path in sync.rs discover function - Mark unused setup_test helper with #[allow(dead_code)] - Remove needless_borrow references to hostname in segment.rs tests - Allow pre-existing block_scrutinee lints in aw-query parser macro Git-Session-Id: 6e04ccc4-aaad-43fb-a2e2-5df2c80c2c02
Git-Session-Id: 0012
Addresses the AI-review findings on the pre-bind port check: - Run the occupancy check before the datastore is opened, so an exit on a busy port can never interrupt a legacy import or leave a half-initialised SQLite database (with -wal/-shm recovery) behind. - Bound the probe response to 16 KiB and cut the per-read timeout to 500 ms; set_read_timeout applies per read, so a chatty listener could previously stall startup and grow memory without limit. - Resolve host:port with ToSocketAddrs (hostnames such as "localhost" were silently skipped by the old parse-only form) and bracket IPv6 literals in the Host header per RFC 7230. Git-Session-Id: 28ceb0c5-5cfe-5191-a135-6a1f05a0a9a2
…dings - Parse the configured address as an IpAddr, exactly matching what Rocket binds. The hostname-resolving form picked the first of several resolved addresses and could exit on a port that Rocket would bind successfully; parsing as IpAddr (a) removes that, (b) still supports IPv6 literals (which the original parse form silently skipped), and (c) is honest: aw-server config only accepts IP literals. - Propagate sync_datastores errors in the historical_edit test helpers via expect() instead of let _ =, so a failed sync fails the test with a clear message rather than asserting on stale data. Git-Session-Id: 28ceb0c5-5cfe-5191-a135-6a1f05a0a9a2
A process that accepts the probe connection but never reads could otherwise block write_all indefinitely (connect_timeout bounds only the handshake). Set a 500ms write timeout alongside the read timeout. Git-Session-Id: 28ceb0c5-5cfe-5191-a135-6a1f05a0a9a2
The probe previously matched any JSON body whose version string contained 'rust' or 'python'. Also require the ActivityWatch info payload's hostname and device_id fields, so an unrelated service that happens to serve a version-shaped /api/0/info response is not misidentified as ActivityWatch. Git-Session-Id: 28ceb0c5-5cfe-5191-a135-6a1f05a0a9a2
set_read_timeout only bounds each read, so a listener trickling a byte per read could hold startup for hours inside read_to_string. Read into a capped buffer with a 2s overall deadline instead. Git-Session-Id: 79e0d28d-aca6-5f82-a8df-c1ae55dbf2cf
Git-Session-Id: 1872204e-4743-5656-9c0b-12c168efc527
Git-Session-Id: ac0371df-1886-5d6a-bd67-b403b4fdbf2d
Git-Session-Id: ef4bf844-34bb-50e6-b488-ba14a6100e95
ba6b0c3 to
8e076e9
Compare
|
Rebased onto current master ( The conflict was in Verified locally: all five port-probe tests pass, Clippy with |
…info response - Replace the separate connect pre-check plus identifying probe with a single connection returning NotListening / ActivityWatch / Other. A listener that goes away between two connects can no longer turn a now-free port into a spurious "in use by another program" exit; a refused connect lets Rocket bind as before. - Identify ActivityWatch only from an HTTP 200 /api/0/info response, so an error page carrying similar JSON fields is not misreported, and accept bare-LF header terminators. Tests: rejects_info_payload_with_error_status, identifies_bare_lf_response, released_port_is_not_reported_in_use.
|
|
||
| // Only a successful info response identifies a server: an error page from | ||
| // another service may carry similar JSON fields. | ||
| let mut status = buf.lines().next()?.split_whitespace(); |
There was a problem hiding this comment.
buf.lines().next()?.split_whitespace(). If the response is a valid HTTP/1.0 200 but the body is split across multiple TCP segments, the code reads until the deadline or MAX_RESPONSE, which is fine. However, the status line check status.next()? != "200" only checks the status code, not the reason phrase. A response like 'HTTP/1.0 200 OK' passes. That is correct. The issue is that the code does not handle chunked transfer encoding or HTTP/1.1 keep-alive responses. The request uses HTTP/1.0 and Connection: close, so a compliant server will not use chunked encoding. But a non-compliant server might. If the response is chunked, the body will contain chunk-size lines and the JSON parse will fail, returning None and mapping to Other. This is a false negative for AW detection, but the consequence is the same as above: the server exits with 'already in use' even though AW is running. The severity is P2 because the message is misleading, but the server would have failed to bind anyway. (2/2 passes)
|
Disposing both P2 findings on head fp fp The 01:15 UTC run scored 5/5 with 0 findings; the 02:08 run is a partial-consensus result (2/3 jobs answered). |
|
@greptileai review |
|
|
||
| Some(format!( | ||
| "ActivityWatch server {version} ({hostname}) is already running on \ | ||
| port {port}; open http://localhost:{port} in your browser or stop it first" |
There was a problem hiding this comment.
There was a problem hiding this comment.
Fixed in ed179a0: the hint now derives the browser address from the configured binding — localhost only for loopback and wildcard (0.0.0.0/::) bindings, the literal address otherwise, with IPv6 literals bracketed. Covered by a display_host unit test; cargo clippy --all-targets -D warnings and cargo fmt --check are clean.
…r hint Only loopback and wildcard bindings answer on localhost; a server started with --host <LAN IP> must be reached at that address instead. Build the browser URL from the configured address, bracketing IPv6 literals. Git-Session-Id: a5b9b676-fcad-5101-8a0b-db079137101c
|
@greptileai review |
Problem
When port 5600 is already in use, the server logs a generic
Address already in useerror that doesn't tell the user the most common cause: ActivityWatch is already running (double-launch or leftover process). See ActivityWatch/docs#169.Solution
Before Rocket attempts to bind, probe the port with a lightweight TCP + HTTP/1.0
GET /api/0/info. If the response identifies an ActivityWatch server, log:If the port is owned by something else, log the existing message plus a per-OS command to find the process:
ss -ltnp | grep :5600 (or: lsof -i :5600)lsof -i :5600netstat -ano | findstr :5600No new dependency — uses only
std::net::TcpStream+serde_json(already in deps).Test plan
aw-server --testing, start a second one — confirm new AW-specific message appears and exit code is 1nc -l 5600then startaw-server— confirm generic message + OS hint appear