diff --git a/playwriter/src/cdp-relay.ts b/playwriter/src/cdp-relay.ts index 274c640..7174a96 100644 --- a/playwriter/src/cdp-relay.ts +++ b/playwriter/src/cdp-relay.ts @@ -1729,9 +1729,15 @@ export async function startPlayWriterCDPRelayServer({ } const sessionId = normalizeSessionId(body.sessionId) const { sessionId: _sessionId, ...recordingOptions } = body + + // sessionId serves two purposes here: + // 1. CDP session ID (pw-tab-*) — routes to the correct tab in the extension + // 2. Executor session ID (numeric) — used to find which extension to route to + // Only look up the executor when sessionId is NOT a CDP session ID. + const isCdpSessionId = sessionId?.startsWith('pw-tab-') const manager = await getExecutorManager() - const executor = sessionId ? manager.getSession(sessionId) : null - if (sessionId && !executor) { + const executor = sessionId && !isCdpSessionId ? manager.getSession(sessionId) : null + if (sessionId && !isCdpSessionId && !executor) { return c.json({ success: false, error: `Session ${sessionId} not found` }, 404) } const extensionId = executor?.getSessionMetadata().extensionId || null @@ -1748,9 +1754,10 @@ export async function startPlayWriterCDPRelayServer({ app.post('/recording/stop', async (c) => { const body = (await c.req.json()) as { sessionId?: string | number } const sessionId = normalizeSessionId(body.sessionId) + const isCdpSessionId = sessionId?.startsWith('pw-tab-') const manager = await getExecutorManager() - const executor = sessionId ? manager.getSession(sessionId) : null - if (sessionId && !executor) { + const executor = sessionId && !isCdpSessionId ? manager.getSession(sessionId) : null + if (sessionId && !isCdpSessionId && !executor) { return c.json({ success: false, error: `Session ${sessionId} not found` }, 404) } const extensionId = executor?.getSessionMetadata().extensionId || null @@ -1767,8 +1774,9 @@ export async function startPlayWriterCDPRelayServer({ app.get('/recording/status', async (c) => { const sessionId = normalizeSessionId(c.req.query('sessionId')) const normalizedSessionId = sessionId || undefined + const isCdpSessionId = normalizedSessionId?.startsWith('pw-tab-') const manager = await getExecutorManager() - const executor = normalizedSessionId ? manager.getSession(normalizedSessionId) : null + const executor = normalizedSessionId && !isCdpSessionId ? manager.getSession(normalizedSessionId) : null const extensionId = executor?.getSessionMetadata().extensionId || null const relay = getRecordingRelay(extensionId) if (!relay) { @@ -1782,9 +1790,10 @@ export async function startPlayWriterCDPRelayServer({ app.post('/recording/cancel', async (c) => { const body = (await c.req.json()) as { sessionId?: string | number } const sessionId = normalizeSessionId(body.sessionId) + const isCdpSessionId = sessionId?.startsWith('pw-tab-') const manager = await getExecutorManager() - const executor = sessionId ? manager.getSession(sessionId) : null - if (sessionId && !executor) { + const executor = sessionId && !isCdpSessionId ? manager.getSession(sessionId) : null + if (sessionId && !isCdpSessionId && !executor) { return c.json({ success: false, error: `Session ${sessionId} not found` }, 404) } const extensionId = executor?.getSessionMetadata().extensionId || null diff --git a/playwriter/src/executor.ts b/playwriter/src/executor.ts index ba5a2a6..16ab6f2 100644 --- a/playwriter/src/executor.ts +++ b/playwriter/src/executor.ts @@ -1035,26 +1035,27 @@ export class PlaywrightExecutor { // Track execution timestamps relative to recording start (seconds). // Used to identify idle gaps that can be sped up in demo videos. - // Captured before try so we can record timing even if execution throws. + // Captured before execution so we can record timing even if it throws. const recordingStartSnapshot = this.recordingStartedAt const execStartSec = recordingStartSnapshot !== null ? (Date.now() - recordingStartSnapshot) / 1000 : -1 - let result: unknown - try { - result = await Promise.race([ - vm.runInContext(wrappedCode, vmContext, { timeout, displayErrors: true }), - new Promise((_, reject) => setTimeout(() => reject(new CodeExecutionTimeoutError(timeout)), timeout)), - ]) - } finally { - // Record timestamp even on error — the execution still occupied real time - // that should not be sped up in the demo video. - if (recordingStartSnapshot !== null && execStartSec >= 0 && this.recordingStartedAt !== null) { - const execEndSec = (Date.now() - recordingStartSnapshot) / 1000 - this.executionTimestamps.push({ start: execStartSec, end: execEndSec }) + const result = await (async () => { + try { + return await Promise.race([ + vm.runInContext(wrappedCode, vmContext, { timeout, displayErrors: true }), + new Promise((_, reject) => setTimeout(() => reject(new CodeExecutionTimeoutError(timeout)), timeout)), + ]) + } finally { + // Record timestamp even on error — the execution still occupied real time + // that should not be sped up in the demo video. + if (recordingStartSnapshot !== null && execStartSec >= 0 && this.recordingStartedAt !== null) { + const execEndSec = (Date.now() - recordingStartSnapshot) / 1000 + this.executionTimestamps.push({ start: execStartSec, end: execEndSec }) + } } - } + })() let responseText = formatConsoleLogs(consoleLogs) diff --git a/playwriter/src/ffmpeg.ts b/playwriter/src/ffmpeg.ts index 73fdc1c..2045d38 100644 --- a/playwriter/src/ffmpeg.ts +++ b/playwriter/src/ffmpeg.ts @@ -420,13 +420,13 @@ export interface ExecutionTimestamp { export function computeIdleSections({ executionTimestamps, totalDurationMs, - speed = 4, + speed = 5, bufferSeconds = INTERACTION_BUFFER_SECONDS, }: { executionTimestamps: ExecutionTimestamp[] /** Total recording duration in milliseconds (from stopRecording result) */ totalDurationMs: number - /** Speed multiplier for idle sections (default 4) */ + /** Speed multiplier for idle sections (default 5) */ speed?: number /** Override the default buffer around each execution (seconds) */ bufferSeconds?: number @@ -497,7 +497,7 @@ export interface CreateDemoVideoOptions { durationMs: number /** Execution timestamps (from stopRecording result) */ executionTimestamps: ExecutionTimestamp[] - /** Speed multiplier for idle sections (default 4) */ + /** Speed multiplier for idle sections (default 5) */ speed?: number /** Output file path (defaults to recordingPath with `-demo` suffix) */ outputFile?: string @@ -522,7 +522,7 @@ export async function createDemoVideo( recordingPath, durationMs, executionTimestamps, - speed = 4, + speed = 5, signal, } = options diff --git a/playwriter/src/skill.md b/playwriter/src/skill.md index c40f6ac..39fa017 100644 --- a/playwriter/src/skill.md +++ b/playwriter/src/skill.md @@ -924,7 +924,7 @@ const demoPath = await createDemoVideo({ recordingPath: recording.path, durationMs: recording.duration, executionTimestamps: recording.executionTimestamps, - speed: 4, // optional, default 4x for idle sections + speed: 5, // optional, default 5x for idle sections // outputFile: './demo.mp4', // optional, defaults to recording-demo.mp4 }) console.log('Demo video:', demoPath)