fix: recording session ID routing, IIFE for timestamps, default speed 5x
cdp-relay.ts:
- Fix pre-existing bug: recording HTTP routes (/recording/start, stop,
status, cancel) were treating CDP session IDs (pw-tab-*) as executor
session IDs, causing 'Session not found' errors. Now skip executor
lookup when sessionId is a CDP session ID and pass it through directly
to the extension for tab routing.
executor.ts:
- Use async IIFE instead of let+try for VM execution timestamp tracking.
Cleaner pattern: const result = await (async () => { try/finally })()
ffmpeg.ts + skill.md:
- Change default idle speed from 4x to 5x
This commit is contained in:
@@ -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
|
||||
|
||||
+15
-14
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user