Skip to content

feat: add timeout param to record_video for auto-stop#65

Open
saen-ai wants to merge 9 commits into
joshuayoes:mainfrom
saen-ai:feat/record-video-timeout
Open

feat: add timeout param to record_video for auto-stop#65
saen-ai wants to merge 9 commits into
joshuayoes:mainfrom
saen-ai:feat/record-video-timeout

Conversation

@saen-ai
Copy link
Copy Markdown
Contributor

@saen-ai saen-ai commented Apr 21, 2026

Summary

Adds an optional timeout parameter (integer, 1–3600 seconds) to record_video. When provided, the recording automatically stops after the specified duration without needing a separate stop_recording call.

Fixes #5

How it works

After the recording starts successfully, a setTimeout fires at the given duration and sends SIGINT to the tracked ChildProcess (the same targeted kill introduced in #64). The activeRecordings map is cleaned up automatically on process exit.

Changes

  • Added timeout: z.number().int().positive().max(3600).optional() to record_video schema
  • Auto-stop logic fires SIGINT via the tracked PID after timeout * 1000 ms
  • Success message adapts: shows countdown note when timeout is set, otherwise shows manual stop instruction

Test plan

  • Call record_video without timeout → records indefinitely, stopped by stop_recording
  • Call record_video with timeout: 5 → recording stops automatically after ~5 seconds, video file is saved
  • Call record_video with timeout: 5, then call stop_recording before 5s → stops immediately, timer fires harmlessly after (process already gone)
  • Call record_video with timeout: 3601 → Zod validation error (exceeds max)

saen-ai and others added 9 commits April 21, 2026 10:41
…dep CVEs

- record_video: was hardcoded to "booted", ignoring the udid param — now
  uses getBootedDeviceId() consistently with all other tools; also adds
  udid to the tool schema so callers can target a specific simulator

- ui_view: JSON.parse on idb output had no error handling — server would
  crash on malformed output; wrapped in try/catch with a clear error
  message; also validates frame dimensions are positive numbers before use

- ui_view: temp PNG/JPEG files now deleted immediately after reading
  instead of accumulating until server exit; file names include a random
  suffix to prevent collisions on rapid successive calls

- record_video: improved start detection — now rejects properly if the
  process exits early, increased timeout from 3s to 5s, tracks resolved
  state to avoid double-settling the promise

- deps: updated @modelcontextprotocol/sdk to latest (fixes CVE ReDoS,
  cross-client data leak, DNS rebinding — all high severity); ran
  npm audit fix for 6 additional moderate/low vulns in ajv, body-parser,
  minimatch, path-to-regexp, qs, diff

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
idb ui text only supports ASCII keycodes and throws 'No keycode found'
for any emoji or non-ASCII character. This adds a new ui_paste tool that
works around the limitation using the macOS pasteboard:

1. Copies text to the Mac clipboard via pbcopy
2. Syncs it to the simulator pasteboard via xcrun simctl pbsync
3. Long-presses at the given coordinates to trigger the paste menu
4. Finds the Paste button in the accessibility tree and taps it

This enables typing emoji, Arabic, Chinese, and any Unicode text into
simulator inputs — essential for testing apps with international users
or emoji-heavy content.

ui_type is unchanged and remains the right tool for ASCII text.
1.5s was triggering iOS system gestures (app switcher / home screen),
dismissing the app before the paste menu appeared. 0.8s is long enough
to trigger the contextual paste menu without conflicting with system gestures.
idb ui tap requires integer x/y values — passing floats like 55.166...
causes 'invalid int value' error. Round the calculated center coordinates.
terminate_app: kills a running app by bundle ID without having to
relaunch it — useful for testing cold-start flows and crash recovery

open_url: opens any URL or deep link in the simulator — essential for
testing universal links, custom URL schemes, and OAuth redirect flows

list_apps: lists all installed apps with their bundle IDs and display
names, sorted alphabetically — removes the need to manually look up
bundle IDs before calling launch_app or terminate_app
- max_size: resizes screenshot proportionally using sips when the image
  exceeds the given pixel dimension (width or height). Solves the Claude
  2000px API limit issue (joshuayoes#42).
- force: prevents silent overwrites by erroring when the output file
  already exists. Defaults to false (joshuayoes#19).

Fixes joshuayoes#42, Fixes joshuayoes#19
Instead of pkill-ing all simctl recordVideo processes, we now store
each recording's ChildProcess in a Map keyed by UDID. stop_recording
sends SIGINT to that specific PID, leaving any other simulators or
concurrent idb operations untouched.

Falls back to pkill if no tracked process is found (e.g. server
restarted mid-recording) so behaviour is never worse than before.

Also adds an optional udid param to stop_recording for multi-simulator
setups.

Fixes joshuayoes#20
Adds an optional timeout (1–3600 seconds) to record_video. When set,
a setTimeout fires after the given duration and sends SIGINT to the
tracked recording process — same targeted kill used by stop_recording.

If omitted, behaviour is unchanged: recording runs until stop_recording
is called.

Fixes joshuayoes#5
@joshuayoes
Copy link
Copy Markdown
Owner

Thanks — clean feature, fixes #5. Fits the spawned-process pattern from #64 well. Three things:

  1. Stacking. Same as feat: add max_size and force params to screenshot tool #63/fix: track recording process by UDID for targeted stop #64. Net-real-change is commit 87af28aa, but it depends on 611e44e5 from fix: track recording process by UDID for targeted stop #64. Cleanest path: once fix: track recording process by UDID for targeted stop #64 lands on main, rebase this PR onto upstream/main with just 87af28aa. If you want to unblock before that, a branch containing 611e44e5 + 87af28aa (in that order) works too.

    # after #64 is merged
    git fetch upstream
    git checkout -B feat/record-video-timeout upstream/main
    git cherry-pick 87af28aa
    git push --force-with-lease origin feat/record-video-timeout
    
  2. Unreffed setTimeout. The timer keeps the Node event loop alive until it fires, even if the recording was manually stopped before that via stop_recording. The activeRecordings.get(...) lookup returns undefined after the exit handler runs, so no stray kill — good — but the event-loop keepalive is still there. Either .unref() the timer, or clearTimeout in the exit handler. Small polish, not blocking.

  3. Demo recording. ## Demo with a short recording showing e.g. record_video with timeout: 5 auto-stopping would be great — see Releases and feat: add ui_find_element tool #51 for format.

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.

Add timeout for screen recording

2 participants