Skip to content

Conversation

@onyedikachi-david
Copy link
Contributor

@onyedikachi-david onyedikachi-david commented Jan 22, 2026

/claim #1540
Fixes #1540

I am still trying to record a demo, currently unable to do that due to the error below when i run this command: pnpm tauri:build --debug; is there a specific node version that the repo recommends, only this commands fails, commands like pnpm dev:desktop works fine.

Error:

@cap/web-domain:build: import { styleText } from "node:util";
@cap/web-domain:build:          ^^^^^^^^^
@cap/web-domain:build: SyntaxError: The requested module 'node:util' does not provide an export named 'styleText'
@cap/web-domain:build:     at ModuleJob._instantiate (node:internal/modules/esm/module_job:122:21)
@cap/web-domain:build:     at async ModuleJob.run (node:internal/modules/esm/module_job:188:5)
@cap/web-domain:build: 
@cap/web-domain:build: Node.js v20.2.0
@cap/env:build:  ELIFECYCLE  Command failed with exit code 1.
@cap/web-domain:build:  ELIFECYCLE  Command failed with exit code 1.
@cap/env:build: ERROR: command finished with error: command (/Users/onyedikachi/Documents/codes/Cap/packages/env) /Users/onyedikachi/.nvm/versions/node/v20.2.0/bin/pnpm run build exited (1)
@cap/web-domain:build: ERROR: command finished with error: command (/Users/onyedikachi/Documents/codes/Cap/packages/web-domain) /Users/onyedikachi/.nvm/versions/node/v20.2.0/bin/pnpm run build exited (1)
@cap/env#build: command (/Users/onyedikachi/Documents/codes/Cap/packages/env) /Users/onyedikachi/.nvm/versions/node/v20.2.0/bin/pnpm run build exited (1)
@cap/web-domain#build: command (/Users/onyedikachi/Documents/codes/Cap/packages/web-domain) /Users/onyedikachi/.nvm/versions/node/v20.2.0/bin/pnpm run build exited (1)

 Tasks:    0 successful, 2 total
Cached:    0 cached, 2 total
  Time:    1.834s 
Failed:    @cap/env#build, @cap/web-domain#build

 ERROR  run failed: command  exited (1)
beforeBuildCommand `pnpm turbo build --filter @cap/desktop` failed with exit code 1
       Error [tauri_cli_node] beforeBuildCommand `pnpm turbo build --filter @cap/desktop` failed with exit code 1
 ELIFECYCLE  Command failed with exit code 1.
 ELIFECYCLE  Command failed with exit code 1.

Greptile Summary

Extends Cap's deeplink API with 8 new actions for recording control and adds a functional Raycast extension for controlling Cap from Raycast.

Major Changes:

  • Added deeplink actions: pause_recording, resume_recording, toggle_pause_recording, restart_recording, take_screenshot, set_microphone, set_camera, list_devices, get_status
  • Refactored duplicate capture target resolution logic into resolve_capture_target() helper
  • Created complete Raycast extension in extensions/raycast/ with 8 commands
  • Added extensions/* to pnpm workspace configuration

Issues Found:

  • GetStatus and ListDevices actions print JSON responses to stdout via CAP_DEEPLINK_RESPONSE: prefix, but the Raycast extension has no mechanism to capture this output since deeplinks are fire-and-forget URL scheme calls
  • recording-status command triggers the action but cannot display the actual status data
  • Form descriptions reference non-existent "List Devices" command in Raycast (not defined in package.json)
  • The stdout-based response mechanism needs a different approach (IPC commands, polling, or events) for bidirectional communication

Confidence Score: 3/5

  • Mostly functional but has unimplemented features (status/list devices responses not captured)
  • Core recording control actions work correctly and integrate with existing functions, but GetStatus and ListDevices actions are incomplete as their responses cannot be received by the Raycast extension through the current deeplink architecture
  • Pay close attention to apps/desktop/src-tauri/src/deeplink_actions.rs (stdout response mechanism) and extensions/raycast/src/recording-status.tsx (incomplete implementation)

Important Files Changed

Filename Overview
apps/desktop/src-tauri/src/deeplink_actions.rs Adds 8 new deeplink actions (pause, resume, toggle pause, restart, screenshot, set microphone/camera, list devices, get status) with proper error handling and refactored capture target resolution
extensions/raycast/src/utils.ts Utility functions for creating deeplink actions and checking Cap installation status
extensions/raycast/src/start-recording.tsx Form-based command for starting recordings with screen/window selection and instant/studio mode
extensions/raycast/src/recording-status.tsx Command that triggers GetStatus action but doesn't receive or display the response data

Sequence Diagram

sequenceDiagram
    participant R as Raycast Extension
    participant OS as macOS URL Handler
    participant C as Cap Desktop App
    participant DR as Deeplink Router
    participant RA as Recording Actions

    Note over R,RA: Start Recording Flow
    R->>R: User fills form (screen/window, mode)
    R->>R: Create JSON action payload
    R->>OS: Open cap-desktop://action?value=<encoded-json>
    OS->>C: Route deeplink URL
    C->>DR: handle(url)
    DR->>DR: Parse URL & deserialize action
    DR->>RA: execute(StartRecording)
    RA->>RA: set_camera_input()
    RA->>RA: set_mic_input()
    RA->>RA: resolve_capture_target()
    RA->>RA: start_recording()
    R->>R: Show HUD "Starting recording..."

    Note over R,RA: Control Recording Flow
    R->>OS: Open cap-desktop://action?value={"pause_recording":{}}
    OS->>C: Route deeplink
    C->>DR: handle(url)
    DR->>RA: execute(PauseRecording)
    RA->>RA: pause_recording()
    R->>R: Show HUD "Pausing recording..."

    Note over R,RA: Query Status Flow (Current Issue)
    R->>OS: Open cap-desktop://action?value={"get_status":{}}
    OS->>C: Route deeplink
    C->>DR: handle(url)
    DR->>RA: execute(GetStatus)
    RA->>RA: Read app state
    RA->>RA: println!("CAP_DEEPLINK_RESPONSE:{json}")
    Note over R,RA: Response printed to stdout but not captured by Raycast
    R->>R: Show HUD (no actual status data)

    Note over R,RA: Screenshot Flow
    R->>R: User selects screen/window
    R->>OS: Open cap-desktop://action?value={"take_screenshot":{...}}
    OS->>C: Route deeplink
    C->>DR: handle(url)
    DR->>RA: execute(TakeScreenshot)
    RA->>RA: resolve_capture_target()
    RA->>RA: take_screenshot()
    R->>R: Show HUD "Taking screenshot..."
Loading

@onyedikachi-david
Copy link
Contributor Author

Ready for review @richiemcilroy

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

15 files reviewed, 4 comments

Edit Code Review Agent Settings | Greptile

Comment on lines +3 to +7
export default async function Command() {
await executeCapAction(createGetStatusAction(), {
feedbackMessage: "Checking recording status...",
feedbackType: "hud",
});
Copy link
Contributor

Choose a reason for hiding this comment

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

logic: GetStatus action prints response to stdout but Raycast extension doesn't capture it. The status is sent via deeplink but never displayed to the user.

Suggested change
export default async function Command() {
await executeCapAction(createGetStatusAction(), {
feedbackMessage: "Checking recording status...",
feedbackType: "hud",
});
import { showHUD } from "@raycast/api";
import { executeCapAction, createGetStatusAction, RecordingStatus } from "./utils";
export default async function Command() {
const status = await getRecordingStatus();
if (status) {
const message = status.is_recording
? `Recording (${status.recording_mode})${status.is_paused ? ' - Paused' : ''}`
: 'Not recording';
await showHUD(message);
}
}
async function getRecordingStatus(): Promise<RecordingStatus | null> {
return null;
}

How should the Raycast extension receive the JSON response that's printed to stdout via CAP_DEEPLINK_RESPONSE:?

Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/raycast/src/recording-status.tsx
Line: 3:7

Comment:
**logic:** `GetStatus` action prints response to stdout but Raycast extension doesn't capture it. The status is sent via deeplink but never displayed to the user.

```suggestion
import { showHUD } from "@raycast/api";
import { executeCapAction, createGetStatusAction, RecordingStatus } from "./utils";

export default async function Command() {
  const status = await getRecordingStatus();
  if (status) {
    const message = status.is_recording 
      ? `Recording (${status.recording_mode})${status.is_paused ? ' - Paused' : ''}`
      : 'Not recording';
    await showHUD(message);
  }
}

async function getRecordingStatus(): Promise<RecordingStatus | null> {
  return null;
}
```

 How should the Raycast extension receive the JSON response that's printed to stdout via `CAP_DEEPLINK_RESPONSE:`?

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

Comment on lines +238 to +242
DeepLinkAction::ListDevices => {
let devices = get_available_devices();
let json = serde_json::to_string(&devices).map_err(|e| e.to_string())?;
println!("CAP_DEEPLINK_RESPONSE:{}", json);
Ok(())
Copy link
Contributor

Choose a reason for hiding this comment

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

logic: ListDevices and GetStatus actions print JSON to stdout, but there's no mechanism in the Raycast extension to capture this output. The deeplink protocol is fire-and-forget via URL scheme, so the extension cannot receive the response. Should these actions use IPC commands instead of deeplinks, or is there a planned mechanism for capturing stdout from the deeplink handler?

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src-tauri/src/deeplink_actions.rs
Line: 238:242

Comment:
**logic:** `ListDevices` and `GetStatus` actions print JSON to stdout, but there's no mechanism in the Raycast extension to capture this output. The deeplink protocol is fire-and-forget via URL scheme, so the extension cannot receive the response. Should these actions use IPC commands instead of deeplinks, or is there a planned mechanism for capturing stdout from the deeplink handler?

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

<Form.Dropdown.Item value="instant" title="Instant" icon={Icon.Video} />
<Form.Dropdown.Item value="studio" title="Studio" icon={Icon.Camera} />
</Form.Dropdown>
<Form.Description text="Tip: Run 'List Devices' command in Cap to see available screen and window names." />
Copy link
Contributor

Choose a reason for hiding this comment

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

logic: References non-existent "List Devices" command. The package.json doesn't define a list-devices command, only the deeplink action exists.

Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/raycast/src/start-recording.tsx
Line: 53:53

Comment:
**logic:** References non-existent "List Devices" command. The `package.json` doesn't define a `list-devices` command, only the deeplink action exists.

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

value={targetName}
onChange={setTargetName}
/>
<Form.Description text="Tip: Run 'List Devices' command in Cap to see available screen and window names." />
Copy link
Contributor

Choose a reason for hiding this comment

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

logic: References non-existent "List Devices" command. The package.json doesn't define a list-devices command.

Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/raycast/src/take-screenshot.tsx
Line: 47:47

Comment:
**logic:** References non-existent "List Devices" command. The `package.json` doesn't define a `list-devices` command.

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

@@ -0,0 +1,96 @@
{
"$schema": "https://www.raycast.com/schemas/extension.json",
"name": "cap",
Copy link

Choose a reason for hiding this comment

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

Since pnpm-workspace.yaml includes extensions/*, this package ends up in the pnpm workspace. The repo root already has a package named cap, so this duplicate name will likely break workspace resolution.

Suggested change
"name": "cap",
"name": "cap-raycast",

let state = app.state::<ArcLock<App>>();
let app_state = state.read().await;
let status = if let Some(recording) = app_state.current_recording() {
let is_paused = recording.is_paused().await.unwrap_or(false);
Copy link

Choose a reason for hiding this comment

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

unwrap_or(false) will silently hide failures from is_paused() and report an incorrect status. It might be better to surface the error through the deeplink response.

Suggested change
let is_paused = recording.is_paused().await.unwrap_or(false);
let is_paused = recording.is_paused().await.map_err(|e| e.to_string())?;

DeepLinkAction::ListDevices => {
let devices = get_available_devices();
let json = serde_json::to_string(&devices).map_err(|e| e.to_string())?;
println!("CAP_DEEPLINK_RESPONSE:{}", json);
Copy link

Choose a reason for hiding this comment

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

list_devices/get_status currently print the JSON response to stdout. When invoked via a URL scheme there usually isn’t a stdout consumer, so these responses may never be observable from Raycast. If you need Raycast to read the response, consider a transport it can actually access (clipboard, file, notification, or callback URL).

})
.collect();

let microphones: Vec<String> = MicrophoneFeed::list().keys().cloned().collect();
Copy link

Choose a reason for hiding this comment

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

Microphone list ordering can be nondeterministic (map key iteration). Sorting makes the output stable.

Suggested change
let microphones: Vec<String> = MicrophoneFeed::list().keys().cloned().collect();
let mut microphones: Vec<String> = MicrophoneFeed::list().keys().cloned().collect();
microphones.sort();

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bounty: Deeplinks support + Raycast Extension

1 participant