Skip to content

fix(cli): validate fps upper bound, prune stale sessions, add upload progress, retry desktop store read#1887

Open
ManthanNimodiya wants to merge 3 commits into
CapSoftware:mainfrom
ManthanNimodiya:fix/cli-improvements
Open

fix(cli): validate fps upper bound, prune stale sessions, add upload progress, retry desktop store read#1887
ManthanNimodiya wants to merge 3 commits into
CapSoftware:mainfrom
ManthanNimodiya:fix/cli-improvements

Conversation

@ManthanNimodiya
Copy link
Copy Markdown
Contributor

@ManthanNimodiya ManthanNimodiya commented Jun 6, 2026

Addresses 4 issues found in #1886:

  • fps validation -> --fps 0 was already rejected but the documented 1-120 upper bound wasn't enforced; now rejects values > 120
  • session pruning -> stopped/errored session files in ~/.cap/sessions were never cleaned up; now pruned after 7 days on next sessions_dir() call
  • upload progress -> cap upload went silent during file transfer while cap export streams NDJSON progress; now emits {"type":"Uploading","bytes_sent":N,"total_bytes":N} every second in --json mode
  • desktop store retry -> load_desktop_store() read the tauri-plugin-store file without retrying; a concurrent Desktop flush could produce truncated JSON; now retries up to 3 times with 50ms delay

Greptile Summary

This PR addresses four previously-reported issues: FPS upper-bound validation, stale-session pruning, upload progress events, and desktop store read retries. The infinite-recursion in pruning and the progress-task lifetime issues from the prior round are now correctly fixed.

  • FPS validation (record.rs): simple fps > 120 guard added to RecordParams::validate — correct for the Option<u32> type.
  • Session pruning (session.rs): prune_old_sessions builds paths directly from its dir argument, eliminating the earlier recursion; pruning is triggered on every sessions_dir() call, so helpers like cleanup and write_session scan the directory 2–3 times per invocation.
  • Upload progress (upload.rs): atomic counter + background task correctly aborted before both the error path and the Uploaded emission, but the terminal event still uses write_json (pretty-printed, multi-line) while progress events use write_json_line (compact), breaking NDJSON stream consumers.
  • Desktop store retry (credentials.rs): correctly exits early on a successful parse; however, ENOENT is retried with 50 ms sleeps, adding ~200 ms latency per invocation on machines without Cap Desktop.

Confidence Score: 3/5

Two regressions in the changed code should be fixed before merging: the file-not-found retry adds 200 ms blocking latency on every CLI invocation for users without Cap Desktop, and the mixed JSON formatting in the upload event stream breaks NDJSON consumers.

The file-not-found retry in load_desktop_store and the mixed JSON formatting in the upload event stream are both present-defect regressions introduced by this PR affecting real usage paths. The previously-flagged bugs are correctly resolved.

apps/cli/src/credentials.rs and apps/cli/src/upload.rs

Important Files Changed

Filename Overview
apps/cli/src/credentials.rs Adds retry loop for truncated-JSON resilience; correctly exits early on successful parse. Bug: retries on ENOENT, adding 200 ms latency per invocation for users without Cap Desktop.
apps/cli/src/record.rs Adds upper-bound FPS validation (> 120 rejected). Logic is correct for the Option type.
apps/cli/src/session.rs Adds session pruning after 7 days; previously flagged infinite-recursion fixed by building paths directly from dir. Pruning runs once per sessions_dir() call, which is called by every path-helper, so cleanup/write_session scan the directory 2-3 times each.
apps/cli/src/upload.rs Adds upload progress via atomic counter + background task. Progress task is correctly aborted before both error propagation and Uploaded emission. Bug: terminal Uploaded event uses pretty-printed write_json while progress events use compact write_json_line, breaking NDJSON consumers.
Prompt To Fix All With AI
Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 2
apps/cli/src/credentials.rs:45-47
The `continue` on a read error retries even when the file simply doesn't exist (`ENOENT`). For users without Cap Desktop installed — or CI runners using `CAP_API_KEY` on a machine that has no store file — every `load_desktop_store` call burns 100 ms per bundle ID (2 sleeps × 50 ms) × 2 bundle IDs = **200 ms of unnecessary blocking I/O on every CLI invocation**. The retry is only meaningful when the file exists but was truncated by a concurrent flush; a missing file will never appear on the next attempt. Check `error.kind()` and only retry on unexpected errors, not `NotFound`.

```suggestion
            let bytes = match std::fs::read(&path) {
                Ok(b) => b,
                Err(e) if e.kind() == std::io::ErrorKind::NotFound => return None,
                Err(_) => continue,
            };
```

### Issue 2 of 2
apps/cli/src/upload.rs:93-96
Progress events are emitted with `write_json_line` (compact, single-line JSON) while the terminal `Uploaded` event uses `write_json` (pretty-printed, multi-line). A newline-delimited JSON consumer reading the stream line by line will parse the compact `Uploading` lines correctly, but then see `{` on its own when `Uploaded` arrives — the object spans multiple lines and is not a valid NDJSON record. The PR description explicitly compares this output to `cap export`'s NDJSON stream, so the final event should use `write_json_line` too.

```suggestion
            OutputFormat::Json => write_json_line(&UploadEvent::Uploaded {
                id: &video_id,
                link: &link,
            })?,
```

Reviews (3): Last reviewed commit: "fix(cli): abort upload progress task on ..." | Re-trigger Greptile

Comment thread apps/cli/src/session.rs
Comment thread apps/cli/src/upload.rs Outdated
Comment thread apps/cli/src/credentials.rs Outdated
@ManthanNimodiya
Copy link
Copy Markdown
Contributor Author

@greptileai please re-review

Comment thread apps/cli/src/upload.rs
Comment on lines +342 to +353
let body = reqwest::Body::wrap_stream(stream);
let response = http
.put(put_url)
.header(reqwest::header::CONTENT_LENGTH, total_size)
.header(reqwest::header::CONTENT_LENGTH, total_bytes)
.body(body)
.send()
.await
.map_err(|e| format!("Upload failed: {e}"))?;

if let Some(task) = progress_task {
task.abort();
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Progress task not aborted on send() failure

If http.put(...).send().await returns an Err, the ? on line 349 propagates immediately, skipping the task.abort() call at line 351–353. The background task then continues to wake up every second and emit stale Uploading events after the error JSON has already been written to stdout. A JSON consumer treating the stream as NDJSON would see progress events after the terminal error object. The fix that was intended here is only applied for the success path (HTTP non-2xx) — the network-error path still leaks the task.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/cli/src/upload.rs
Line: 342-353

Comment:
**Progress task not aborted on `send()` failure**

If `http.put(...).send().await` returns an `Err`, the `?` on line 349 propagates immediately, skipping the `task.abort()` call at line 351–353. The background task then continues to wake up every second and emit stale `Uploading` events after the error JSON has already been written to stdout. A JSON consumer treating the stream as NDJSON would see progress events after the terminal error object. The fix that was intended here is only applied for the success path (HTTP non-2xx) — the network-error path still leaks the task.

How can I resolve this? If you propose a fix, please make it concise.

@ManthanNimodiya
Copy link
Copy Markdown
Contributor Author

@greptileai please re-review

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