Repository navigation
fix(record): record a remote client's default recording on the daemon host - #3696
Conversation
… host A remote record start without a path left the default name to the daemon, which resolved ./recording-<ts>.mp4 against the client's cwd and failed with EACCES on the daemon host. The client now sends its local destination without an extension; the daemon completes it with the session platform's extension, records to a temp path on its own host, and the client downloads the file there on record stop. Found while driving an EAS cloud simulator session.
Size Report
Startup median (7 runs, lower is better):
|
|
The code change looks right, but I cannot call this ready yet: the Coverage check fails on this PR, and the remote runs are only partly covered (reviewed at 9c1e558). Coverage fails in eager-closure-budgets. The Smoke Tests failure (iOS deep-link wait, "app com.callstack.agentdevicelab is not running", on a local daemon) looks unrelated, because this diff only touches the remote no-path Not blocking: line 127 of For evidence, I did not rerun the remote flow. The Android proxy result and the 840 KB download come from the PR body only, and no remote web (.webm) or iOS default-recording run is reported. Please run one of those with Before merge, the eager-closure-budgets failure in Coverage needs to go green after the move into the daemon record handler. |
…ecord handler Keep resolveRecordingOutputPaths transport-free: capture-kit picks the extension and resolves paths, and only the daemon knows a request came from a remote client. Document where a remote record start without a path downloads.
|
Addressed in
|
There was a problem hiding this comment.
1 issue found across 4 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/daemon/src/sessions/handlers/record-runtime.ts">
<violation number="1" location="packages/daemon/src/sessions/handlers/record-runtime.ts:222">
P2: Blank path arguments are treated as remote defaults by the client but not by the daemon. Treat an empty or whitespace-only `requestedPath` as absent here so the daemon uses its temp artifact and completes the client extension.</violation>
</file>
Reply with feedback, questions, or to request a fix.
View guided diff | Turn on auto-fix | Re-trigger cubic
| function resolveRecordingStartPaths(req: DaemonRequest, platform: Platform): RecordingStartPaths { | ||
| const requestedPath = req.positionals?.[1]; | ||
| const clientOutputPath = req.meta?.clientArtifactPaths?.outPath; | ||
| if (requestedPath === undefined && clientOutputPath !== undefined) { |
There was a problem hiding this comment.
P2: Blank path arguments are treated as remote defaults by the client but not by the daemon. Treat an empty or whitespace-only requestedPath as absent here so the daemon uses its temp artifact and completes the client extension.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At packages/daemon/src/sessions/handlers/record-runtime.ts, line 222:
<comment>Blank path arguments are treated as remote defaults by the client but not by the daemon. Treat an empty or whitespace-only `requestedPath` as absent here so the daemon uses its temp artifact and completes the client extension.</comment>
<file context>
@@ -206,12 +210,30 @@ function prepareRecordingStart(params: RecordRuntimeHandlerParams, session: Sess
+function resolveRecordingStartPaths(req: DaemonRequest, platform: Platform): RecordingStartPaths {
+ const requestedPath = req.positionals?.[1];
+ const clientOutputPath = req.meta?.clientArtifactPaths?.outPath;
+ if (requestedPath === undefined && clientOutputPath !== undefined) {
+ // A remote caller that names no path cannot know the session platform's extension, so it sends
+ // its local destination without one; its cwd does not exist on this host.
</file context>
| if (requestedPath === undefined && clientOutputPath !== undefined) { | |
| if ( | |
| (requestedPath === undefined || requestedPath.trim().length === 0) && | |
| clientOutputPath !== undefined | |
| ) { |
|
|
Reviewed e166701. The earlier gap is closed: the default remote recording now lands on the daemon host. The iOS remote default run on e166701 shows this, and the Android run on 9c1e558 showed it too. Both runs are the author's reports. I did not rerun them or the eager-closure-budgets test. That test should pass, because capture-kit has no diff and the daemon already depends on contracts and daemon-http. I found no new code problems at e166701. Smoke Tests was still running when I checked. The earlier Smoke failure was an iOS deep-link wait on a local daemon. Local daemons never set clientArtifactPaths, so the changed branches in record-runtime.ts and daemon-artifacts.ts are not on that route. No overlap exists unless a remote record smoke lane fails. The Cubic thread on the blank positional path still applies: #3696 (comment). It has low impact and does not block. The client treats a blank positional as absent and sends the extensionless stem, but it leaves positionals[1] as an empty string. The daemon tests Before merge, Smoke Tests needs to finish green, or fail only with the earlier local iOS deep-link signature. Answering the Cubic thread is optional. |
|
Smoke Tests now passes at e166701, so the last open item is closed. The code is ready, all checks pass, and there are no conflicts. The Cubic thread on the blank positional path is still optional. |
Summary
Found while driving an EAS cloud simulator session. On a remote daemon (
AGENT_DEVICE_DAEMON_BASE_URL),record startwith no path and no--platformfailed: the daemon resolved./recording-<ts>.mp4against the client's cwd on its own host (EACCES/ENOENT: mkdir '<client cwd>').The client can't pick the extension here, because it depends on the session platform. It now sends its local destination without an extension (
clientArtifactPaths.outPath = <cwd>/recording-<ts>). The daemon's record handler (prepareRecordingStart) adds the platform extension, records to a daemon-host temp path, andrecord stopdownloads to the client path.resolveRecordingOutputPathsis unchanged and stays transport-free. Explicit paths were already redirected by the client. The docs now say where the default downloads. 5 files.Validation
Head
e166701772, base2a2f3f2906.pnpm check:affected --runpassed (308 files, 1970 tests, wire-compat unchanged).scripts/__tests__/eager-closure-budgets.test.tspassed (914). The new tests fail without the production change.Live run:
agent-device proxyon macOS, client in a colima Linux container, cwd/client.Android, Pixel_9_Pro_XL_API_37:
2a2f3f29069c1e5585e9record startError (UNKNOWN): ENOENT: no such file or directory, mkdir '/client'/client/recording-1792092638666.mp4; stop downloads it (840 KB)record start out/rel.mp4and/client/out/abs.mp4iOS simulator, head
e166701772:record start --jsonreturns"outPath": "/client/recording-1792130997510.mp4".record stopdownloads it:lsshows 1934217 bytes, anftypmp42header, and the telemetry file next to it.Same-machine client: default and explicit recordings are still written to cwd.
Risks:
--platformstill fails, as before.record start clip(no extension) on web without--platformstill fails. This was already the case before this change.