Skip to content

fix(host-service): serve external open actions for local hosts (#5850, #5893) - #5900

Closed
hknakn wants to merge 2 commits into
ParadevAI:mainfrom
hknakn:fix/host-service-external-open-local
Closed

hknakn wants to merge 2 commits into
ParadevAI:mainfrom
hknakn:fix/host-service-external-open-local

Conversation

@hknakn

@hknakn hknakn commented Jul 23, 2026 •

Copy link
Copy Markdown

Problem

On shipped builds, the "Open in app" button / CMD+O fail with:

Failed to open: No procedure found on path "external.openInApp"

(#5850, #5893). The renderer calls external.openInApp through the Electron-IPC client typed to the desktop AppRouter, but in the running build that call is serviced by the host service rather than the desktop main process. The host-service router has no external key, so it throws No procedure found on path "external.openInApp". The error string is emitted only by the host-service tRPC stack, confirming that's where the call lands.

The misroute is a transport-level issue that isn't visible in the renderer source (every external.* call site uses ipcLink), which is why it hasn't been reproducible from source alone. Users confirm it still reproduces on the current stable, on local project dashboards.

Solution

Add a locality-guarded external router to the host service so the misrouted call succeeds — but only when the host is the caller's own machine (ctx.clientMachineId === getHostId(), the same equality the desktop uses for isLocalWorkspace). For a remote host these actions are meaningless (they'd open an editor on the server), so they reject with PRECONDITION_FAILED instead of doing the wrong thing.

  • Scope limited to the reported actions: openInApp, openInFinder, openUrl.
  • Launch logic is pure Node (open / xdg-open / explorer) — no Electron; the no-electron-coupling guard still passes.
  • The desktop path is untouched.

This is a mitigation, not the root-cause fix. The correct fix belongs on the desktop transport so external.* always reaches the main-process router; this makes the local case work in the meantime and stays safe for remote hosts.

Verification

  • bun run typecheck (host-service) — clean
  • biome check — clean
  • bun run build:host — bundles; openInApp now present in dist/host-service.js
  • New tests (external.test.ts, 4 cases): guard rejects remote / missing machine id; rejects relative paths and disallowed URL schemes
  • Existing no-electron-coupling test still passes

Related

Summary by CodeRabbit

  • New Features
    • Added an “external” action set to open files in external apps and reveal them in the system file manager.
    • Added support for opening approved external links (HTTP, HTTPS, mailto) with validated input.
    • Improved handling for file paths including file URLs, home-directory shortcuts, wrapped paths, and :line[:col] references.
  • Bug Fixes
    • Restricted external actions to the local host only, preventing mismatched or missing machine context.
    • Blocked unsafe URL schemes and invalid relative paths.
  • Tests
    • Added coverage for external action authorization and input validation.

…evAI#5850, ParadevAI#5893)

In shipped builds `external.openInApp` (an Electron-IPC call typed to the
desktop AppRouter) can be serviced by the host service instead of the
desktop main process. The host service router has no `external` key, so
the call fails with `No procedure found on path "external.openInApp"` and
the "Open in app" button / CMD+O silently break.

Add a locality-guarded `external` router to the host service so the
misrouted call succeeds, but only when the host is the caller's own
machine (`ctx.clientMachineId === getHostId()` — the same equality the
desktop uses for `isLocalWorkspace`). Remote hosts reject with
PRECONDITION_FAILED rather than opening an editor on the wrong machine.

Scope is limited to the reported actions (openInApp, openInFinder,
openUrl). The desktop path is untouched. This is a mitigation; the
root-cause fix belongs on the desktop transport so `external.*` reaches
the main-process router.
@coderabbitai

coderabbitai Bot commented Jul 23, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 59102763-e011-43f7-b2be-9cb92b3c6366

📥 Commits

Reviewing files that changed from the base of the PR and between 494d453 and 03f7457.

📒 Files selected for processing (2)
  • packages/host-service/src/trpc/router/external/external.test.ts
  • packages/host-service/src/trpc/router/external/external.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/host-service/src/trpc/router/external/external.test.ts
  • packages/host-service/src/trpc/router/external/external.ts

📝 Walkthrough

Walkthrough

Adds host-service support for local-only external actions. The new router opens applications, reveals files, and opens allowlisted URLs using platform-specific commands, with path resolution, process execution helpers, router registration, and validation tests.

Changes

External host actions

Layer / File(s) Summary
External command and path helpers
packages/host-service/package.json, packages/host-service/src/trpc/router/external/helpers.ts
Adds application command mappings, path sanitization and resolution, process spawning, and the ExternalApp type dependency.
Local-only external procedures
packages/host-service/src/trpc/router/external/external.ts
Adds locality checks and procedures for opening applications, revealing files, and opening only http:, https:, and mailto: URLs.
Router registration and validation
packages/host-service/src/trpc/router/router.ts, packages/host-service/src/trpc/router/external/external.test.ts
Registers the external router and tests locality and input validation behavior.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant HostService
  participant externalRouter
  participant OS
  Caller->>HostService: external.openInApp
  HostService->>externalRouter: validate local machine and path
  externalRouter->>OS: launch application or reveal file
  OS-->>externalRouter: process result
  externalRouter-->>Caller: mutation response
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise, conventional, and accurately summarizes the host-service external actions fix.
Description check ✅ Passed The description covers the problem, solution, and verification clearly, with only minor template mismatches and a missing checklist.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@hknakn
hknakn marked this pull request as ready for review July 23, 2026 16:21

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

🤖 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 `@packages/host-service/src/trpc/router/external/external.ts`:
- Around line 127-131: Update the openInFinder mutation to validate that its
input is an absolute path before calling revealInFileManager, rejecting relative
paths while preserving valid absolute-path behavior. Add a matching test
covering rejection of relative input and ensuring revealInFileManager is not
invoked.
- Around line 55-57: Update the Windows branch in the target-opening function to
avoid passing the caller-controlled target to cmd.exe via spawnAsync. Use a
direct Windows shell-association executable or API that opens the target without
shell command parsing, while preserving the existing target-opening behavior for
valid URLs, including mailto links.

In `@packages/host-service/src/trpc/router/external/helpers.ts`:
- Around line 69-71: Update the Linux fallback in the helper that invokes
xdg-open so it passes nodePath.dirname(targetPath) rather than targetPath,
ensuring files open their containing directory while preserving existing
behavior for directories.
🪄 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 Plus

Run ID: 9b66cf52-8a90-4a9c-8e1f-afca10007d01

📥 Commits

Reviewing files that changed from the base of the PR and between f313df2 and 494d453.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (5)
  • packages/host-service/package.json
  • packages/host-service/src/trpc/router/external/external.test.ts
  • packages/host-service/src/trpc/router/external/external.ts
  • packages/host-service/src/trpc/router/external/helpers.ts
  • packages/host-service/src/trpc/router/router.ts

Address CodeRabbit review on ParadevAI#5900:
- Drop the win32 branches so targets are never routed through cmd.exe
  (which parses `&`/`|` in a URL or path as command separators). Only
  macOS and Linux are handled, matching the desktop's getAppCommand
  platform support.
- Guard openInFinder against relative paths, like openInApp (extracted a
  shared assertAbsolutePath helper).
- On Linux, reveal a path by opening its containing directory instead of
  xdg-open'ing the file itself (which would launch its default app).

Add a test for the openInFinder absolute-path guard.
@hknakn hknakn closed this Aug 6, 2026
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