Skip to content

fix(files): open PDFs from the CLI in the desktop viewer - #24331

Closed
innocarpe wants to merge 6 commits into
stablyai:mainfrom
innocarpe:fix-24328-cli-open-pdf
Closed

innocarpe wants to merge 6 commits into
stablyai:mainfrom
innocarpe:fix-24328-cli-open-pdf

Conversation

@innocarpe

Copy link
Copy Markdown
Contributor

Description

orca file open on a PDF returned ok: true with kind: "binary" and opened: false, and no tab opened. The same PDF opens in the desktop PDF viewer from the file explorer. Markdown in the same command opens a tab.

Focused fix

  • In scope: files.open treats a .pdf path as previewable and calls the same renderer openFile path images already use, with kind: "pdf" and opened: true.
  • Out of scope: zip, audio, and video, which stay opened: false. Diff opening is unchanged.

Preserves

  • A missing PDF still fails the RPC before a tab is created, same as a missing text file.
  • .PDF is included. A name that only contains .pdf in the middle is not.
  • Non-previewable binaries still do not call openFile.

Evidence

  • Test: node node_modules/vitest/vitest.mjs run --config config/vitest.config.ts src/main/runtime/orca-runtime-files-mobile-explorer-reads.test.ts
  • 9 passed.
  • Not run against a live desktop window.

User-regression-tradeoffs

  • A mobile tap that uses files.open for a PDF now gets opened: true and opens the desktop viewer tab, the same way an image opened through this RPC does. Zip files still decline.

Fixes #24328

@greptile-apps

greptile-apps Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 5/5

[Medium risk] Adds PDF file opening to the file handler.

The PR appears safe to merge; no new blocking issue was established.

Summary

The PR opens PDFs in the desktop viewer through files.open while preserving the previous unopened response for clients that do not advertise the new capability.

  • Mobile clients that advertise the capability avoid activating a PDF tab they cannot read.
  • Direct CLI opens now return an error when the runtime declines a binary file.
  • PDF-named directories are rejected before a tab opens.
Diagram
%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A["files.open PDF request"] --> B{"Client supports PDF desktop open?"}
  B -- No --> C["Return binary, opened: false"]
  B -- Yes --> D{"Target is a file?"}
  D -- No --> E["Return error; do not open tab"]
  D -- Yes --> F["Open desktop viewer; return pdf, opened: true"]
  F --> G["Mobile skips synced-tab activation"]
Loading

Reviews (5) · Last reviewed commit: "test(files): put the stat-mock assertion..."

Comment thread src/main/runtime/runtime-file-commands-constructor.ts

@pullfrog pullfrog Bot left a comment

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.

✅ No new issues found.

Reviewed changes

  • PDF recognized as previewable in files.open — openMobileFile gains an isRuntimePreviewablePdfPath branch, ordered before the binary check, so a .pdf now flows to host.openFile and returns opened: true.
  • RuntimeFileOpenResult['kind'] widened to include 'pdf' — informational result field; no consumer does an exhaustive switch, and mobile's reply schema is a loose object, so old clients are unaffected.
  • New test — asserts the PDF reaches openFile and returns { kind: 'pdf', opened: true }; it would fail without the branch, so it pins the fix.

The mechanism is sound: the desktop renderer already renders PDFs off isImage: true + mimeType: 'application/pdf' (EditorEditFileSurface → ImageViewer → PdfViewer), and both the local readFileExplorerPreview and the relay mime map supply those fields for .pdf, so routing through the existing openFile path reaches the same viewer the file explorer uses. The leading-dot guard (dotIndex > 0) and case-insensitive match are correct, and a missing PDF still fails the RPC before any tab is created.

ℹ️ files.list / files.searchPaths still report PDFs as binary

The new kind: 'pdf' is only produced by files.open. The list and search methods still classify .pdf through isMobileBinaryPath, so the mobile file explorer row remains disabled (canPreviewMobileFileRow returns false for .pdf), meaning the mobile tap path described in the PR description is reachable for PDFs only from terminal path taps, not the explorer. Nothing blocking — flagging in case wiring the explorer was intended, since it would be a one-line follow-up.

Technical details
# PDF open lists as binary in list/search

## Affected sites
- `src/main/runtime/runtime-file-commands-constructor.ts:176-184` — `openMobileFile` now resolves `.pdf` to `'pdf'`.
- `src/main/runtime/runtime-file-commands-constructor.ts:61` and `:116`/`:157` — `listMobileFiles` / `searchMobileFilePaths` / `searchQuickOpenFilePaths` still use `isMobileBinaryPath`, so `.pdf` is reported `'binary'`.
- `mobile/src/files/mobile-file-preview-navigation.ts:33` — `canPreviewMobileFileRow` rejects non-previewable binaries, so a `.pdf` row is disabled.

## Required outcome
- Decide whether the mobile explorer should surface PDFs. If yes, align the list/search kind with `openMobileFile`; if no, the divergence is intentional and no change is needed.

## Open questions for the human
- Is opening PDFs from the mobile file explorer in scope, or is the CLI/terminal-tap path the only intended entry point?

Pullfrog  | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

@coderabbitai

coderabbitai Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

🧰 Additional context used
📚 Code guidelines (1)
AGENTS.md — auto-discovered

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: 63ce7742-e1d1-452d-ba19-bdec2f229fe7

📥 Commits

Reviewing files that changed from the base of the PR and between 6d624bc29d2f4129a36692b9f3164891c7953a99 and 09b4752.

📒 Files selected for processing (11)
  • docs/reference/remote-wire-compatibility.md
  • src/cli/handlers/file.test.ts
  • src/cli/handlers/file.ts
  • src/cli/runtime/websocket-transport.test.ts
  • src/main/runtime/orca-runtime-files-mobile-explorer-reads.test.ts
  • src/main/runtime/rpc/methods/files-open-pdf-desktop-capability.test.ts
  • src/main/runtime/rpc/methods/files-open-pdf-desktop-capability.ts
  • src/main/runtime/rpc/methods/files.ts
  • src/main/runtime/runtime-file-commands-constructor.ts
  • src/shared/files-pdf-desktop-open-capability.ts
  • src/shared/remote-runtime-client-capabilities.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

The runtime now classifies PDF paths separately and opens PDFs when desktop PDF opening is enabled. Capability negotiation preserves unopened binary results for clients that do not advertise support. Directory paths ending in .pdf reject with EISDIR. The mobile flow does not activate a worktree tab after a successful PDF open. The CLI reports declined direct opens as unsupported-type errors.

Priority: ➖ Normal

Severity of issue fixed: Medium

Merge Risk: ⚪ Minimal · up to 09b47

PDF opening and client handling appear consistent, while unsupported direct CLI opens now report errors. No actionable merge-blocking issue remains in the supplied evidence; normal checks should complete before merging.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 09b47

PDF opening retains the existing file checks and preserves the previous response for clients without support. No introduced security vulnerability was established, but remote authentication and end-to-end viewer failure recovery were not fully verified.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — Caller-selected worktree-relative PDF paths gain reachability to the desktop file-opening path rather than being declined. The inspected exposure concerns the resolved runtime worktrees and desktop viewer. Local targets retain allowed-root checks; SSH targets use a separate remote-stat route whose authorization was not independently verified.

Trust Boundaries and Controls

  • observed — The capability gate consumes clientKind and clientCapabilities copied from dispatch options, not files.open parameters. RpcContext documents capabilities as bound to the authenticated socket. Undefined clientKind enables the in-process compatibility case; verifying that every remote transport supplies the intended metadata remains an end-to-end coverage gap.

Resilience and Maintainability Implications

  • inferred — PDF opening inherits the existing void host-opening boundary: opened:true follows pre-open validation and invocation, not confirmed viewer-load completion. Renderer editor state and workspace-tab state are updated in separate steps. Failure propagation, cleanup, and interruption recovery across that boundary remain unverified; this is not a demonstrated PR-introduced vulnerability.
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description explains the problem, scope, behavior changes, regression tradeoffs, linked issue, and one automated test run. It does not follow the repository template because it omits the required … Add the missing template sections. Include an ELI5 explanation, detailed user-facing and implementation changes, rationale and alternatives, visual proof or an exact N/A statement with a reason, testing platforms and results, AI disclosure …
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 6 functions across 15 files. (1 skipped: … Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the primary change: opening PDFs from the CLI in the desktop viewer.
Linked Issues check ✅ Passed The PR satisfies the coding requirements in [#24328]. openMobileFile classifies case-insensitive final .pdf extensions as pdf, validates that the target is a file, and calls openFile. Existing…
Out of Scope Changes check ✅ Passed The changes remain connected to [#24328]. Capability negotiation preserves compatibility for clients that cannot open PDF tabs. Mobile handling prevents an incorrect synced-tab activation after a PDF …
Full details: Description check

Explanation

The description explains the problem, scope, behavior changes, regression tradeoffs, linked issue, and one automated test run. It does not follow the repository template because it omits the required ELI5, What Changed, Why, Visual Proof, Review, Agent skill upstream boundary, Notes, and Checklist sections. It also does not state the required visual-proof N/A rationale or broader test and platform coverage.

Resolution

Add the missing template sections. Include an ELI5 explanation, detailed user-facing and implementation changes, rationale and alternatives, visual proof or an exact N/A statement with a reason, testing platforms and results, AI disclosure if applicable, review notes, the Agent skill upstream boundary checkbox, security/cross-platform/remote/mobile/backwards-compatibility notes, and the required checklist items.

Full details: Docstring Coverage

Explanation

Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 6 functions across 15 files. (1 skipped: 1 unsupported.)

  • Fix all pre-merge checks with AI
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.


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.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: 7c95af37-7570-4ad7-9dd9-d3769c2359e8

📥 Commits

Reviewing files that changed from the base of the PR and between 94f6c53 and ad4128791e700b0c06f3181f6647fb4a3999ff42.

📒 Files selected for processing (3)
  • src/main/runtime/orca-runtime-files-mobile-explorer-reads.test.ts
  • src/main/runtime/runtime-file-commands-constructor.ts
  • src/shared/runtime-file-contracts.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 3 remain after this review.

Comment thread src/main/runtime/runtime-file-commands-constructor.ts
@innocarpe
innocarpe force-pushed the fix-24328-cli-open-pdf branch from ad41287 to 6d624bc Compare October 1, 2026 09:25
@innocarpe

Copy link
Copy Markdown
Contributor Author

Sync update (6d624bc29d)

Address review: a mobile tap still opens the PDF in the desktop viewer, and it no longer activates the synced tab that would files.read the PDF and fail with binary_file.

@pullfrog pullfrog Bot left a comment

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.

ℹ️ No critical issues — one mixed-version note inline.

Reviewed changes

  • Mobile no longer re-reads a PDF tab the desktop already opened — openMobileFileTap returns early when files.open reports kind: 'pdf', skipping scheduleOpenedWorktreeTabActivation and the files.read that fails with binary_file.
  • fileTapOpenedSchema declares an optional kind — so the tap can branch on kind: 'pdf' while a host that omits it still activates the tab.
  • Tests — a mobile test that fails without the early return, plus a schema read assertion for kind.

The mobile fix is correct and well-scoped: the PDF branch on the host is reached only for worktree-contained taps, triggerOpenFeedback already fired before the early return, and the guard reads the same kind the host now publishes.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

Comment thread mobile/src/session/mobile-file-tap-open.ts
innocarpe and others added 3 commits October 1, 2026 21:37
files.open treated .pdf as a non-previewable binary and reported
success with opened:false. The editor already previews that mime.
A tap that gets opened:true was switching to the synced file tab, and that tab calls files.read, which rejects PDFs as binary_file. The desktop open stays.

Co-authored-by: Cursor <cursoragent@cursor.com>
Direct `orca file open` kept ok:true when the runtime declined a binary, so
callers treated that as success. A PDF reply that flips opened to true makes
an already-installed mobile client activate the file tab and files.read it.
Clients that advertise files.open.pdf-desktop.v1, and in-process callers,
still open the desktop viewer. file diff and file open-changed keep their
skipped-result output.
@innocarpe
innocarpe force-pushed the fix-24328-cli-open-pdf branch from 6d624bc to a7d38c9 Compare October 1, 2026 12:54
@innocarpe

Copy link
Copy Markdown
Contributor Author

Sync update (a7d38c9162)

Direct file open now fails when the runtime declines a binary. A PDF opens in the desktop viewer only for a client that will not activate that tab, including an in-process caller.

Comment thread src/main/runtime/runtime-file-commands-constructor.ts

@pullfrog pullfrog Bot left a comment

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.

ℹ️ No critical issues — one minor cleanup inline.

Reviewed changes (the increment since the prior pullfrog review, commit a7d38c9162)

  • Gated the PDF desktop open behind files.open.pdf-desktop.v1 — supportsFilesPdfDesktopOpen reads the caller's advertised capabilities, and files.open passes pdfDesktopOpen: false for a negotiated client that does not advertise it, so the host returns the old kind: 'binary', opened: false answer and opens no tab. This closes the mixed-version skew flagged in the prior review: an already-installed mobile client never sees opened: true for a .pdf, and the gate is correctly scoped because a paired phone always carries a defined clientKind from device.scope.
  • openMobileFile gained an optional pdfDesktopOpen parameter — defaults to true so in-process callers keep the viewer, and returns kind: 'binary' when a PDF is declined.
  • CLI file open now fails on a declined open — a runtime result of opened: false is thrown as unsupported-type (exit 1, ok: false under --json), while file diff and file open-changed keep their skipped-row output. Note this is broader than the PR body's PDF-only framing: any declined open (zip/audio/video) now exits nonzero.
  • The capability is advertised by the CLI and mobile from remoteRuntimeClientCapabilities, with a worked example added to docs/reference/remote-wire-compatibility.md.
  • Tests — capability function and dispatcher coverage, a host-level declined-PDF test, and two CLI decline tests.

Verified in this run: the touched unit files pass (136 tests), typecheck:node and typecheck:cli are clean, and the changed-code quality gate reports 0 findings.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

Comment thread src/cli/handlers/file.ts Outdated
A directory named notes.pdf passed the existence check and the CLI
reported success. The open now requires a file. The declined-open
error uses the same RuntimeClientError the rest of the handler throws.
@innocarpe

Copy link
Copy Markdown
Contributor Author

Sync update (d300079699)

Opening a path now requires a file. A directory named notes.pdf fails instead of reporting success, and the declined-open error is the same RuntimeClientError the handler already throws.

@pullfrog pullfrog Bot left a comment

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.

Important

The new remote-directory test adds an unchecked as never cast on a changed line, which fails the required check:code-quality:changed CI step.

Reviewed changes

  • Refuses to open a directory target, not just a missing one — assertMobileOpenTargetExists now keeps the stat result and throws EISDIR when isDirectory() is true, so a .pdf (or text/markdown/image) path that names a directory no longer returns opened: true and creates a tab that cannot load it.
  • Consolidated the duplicate RuntimeClientError import — file.ts now imports the class once from ../runtime/types, retiring the prior review's inline nit.
  • Tests — new local and SSH tests for a directory whose name ends in .pdf; the existing missing-file ENOENT tests are unchanged.

Verified in this run: the three touched test files pass (35 tests), typecheck:cli and typecheck:node are clean. The changed-code quality gate reports one new finding, anchored below.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

Comment thread src/main/runtime/orca-runtime-files-mobile-explorer-reads.test.ts
innocarpe and others added 2 commits October 1, 2026 22:21
The new as never is the only way to stub the SSH provider method this command reads, and the changed-line quality gate rejects it without a scoped reason.

Co-authored-by: Cursor <cursoragent@cursor.com>
The changed-line gate reports the mockReturnValue call, so the exemption has to sit on that line.

Co-authored-by: Cursor <cursoragent@cursor.com>
@innocarpe

Copy link
Copy Markdown
Contributor Author

Sync update (09b4752f1b)

The directory-stat mock keeps a scoped exemption on the mockReturnValue line the changed-code quality gate reports. The command only reads stat from that provider.

AmethystLiang added a commit that referenced this pull request Oct 3, 2026
…Explorer (#24445)

files.open gated the desktop open on the mobile preview list, so PDFs returned opened:false with ok:true. The host now opens every file after an existence/directory check; the CLI fails loudly if an older host still declines.

Fixes #24328. Builds on #24331.

Co-authored-by: Wooseong Kim <innocarpe@gmail.com>
@AmethystLiang

Copy link
Copy Markdown
Contributor

Thanks @innocarpe for the fix and the clear diagnosis! We landed this in #24445, which builds on your approach (it opens every binary type, not just PDFs, and makes the CLI fail if an older app declines the file). You're credited as a co-author on the merge commit.

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.

[Bug]: orca file open silently skips PDFs (kind: binary, opened: false) although the UI opens them in the PDF viewer

2 participants