@farmslot/recipe-runner 0.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (212) hide show
  1. package/CHANGELOG.md +255 -0
  2. package/LICENSE +21 -0
  3. package/README.md +274 -0
  4. package/bin/farmslot-visual-review.mjs +152 -0
  5. package/dist/adapters/app-lifecycle.d.ts +41 -0
  6. package/dist/adapters/app-lifecycle.d.ts.map +1 -0
  7. package/dist/adapters/app-lifecycle.js +224 -0
  8. package/dist/adapters/app-lifecycle.js.map +1 -0
  9. package/dist/adapters/core.d.ts +5 -0
  10. package/dist/adapters/core.d.ts.map +1 -0
  11. package/dist/adapters/core.js +433 -0
  12. package/dist/adapters/core.js.map +1 -0
  13. package/dist/adapters/gesture.d.ts +12 -0
  14. package/dist/adapters/gesture.d.ts.map +1 -0
  15. package/dist/adapters/gesture.js +76 -0
  16. package/dist/adapters/gesture.js.map +1 -0
  17. package/dist/adapters/ui.d.ts +33 -0
  18. package/dist/adapters/ui.d.ts.map +1 -0
  19. package/dist/adapters/ui.js +130 -0
  20. package/dist/adapters/ui.js.map +1 -0
  21. package/dist/cli/error-output.d.ts +6 -0
  22. package/dist/cli/error-output.d.ts.map +1 -0
  23. package/dist/cli/error-output.js +19 -0
  24. package/dist/cli/error-output.js.map +1 -0
  25. package/dist/cli/index.d.ts +12 -0
  26. package/dist/cli/index.d.ts.map +1 -0
  27. package/dist/cli/index.js +29 -0
  28. package/dist/cli/index.js.map +1 -0
  29. package/dist/cli/run-command.d.ts +8 -0
  30. package/dist/cli/run-command.d.ts.map +1 -0
  31. package/dist/cli/run-command.js +253 -0
  32. package/dist/cli/run-command.js.map +1 -0
  33. package/dist/cli/support.d.ts +31 -0
  34. package/dist/cli/support.d.ts.map +1 -0
  35. package/dist/cli/support.js +297 -0
  36. package/dist/cli/support.js.map +1 -0
  37. package/dist/cli/validate-command.d.ts +4 -0
  38. package/dist/cli/validate-command.d.ts.map +1 -0
  39. package/dist/cli/validate-command.js +52 -0
  40. package/dist/cli/validate-command.js.map +1 -0
  41. package/dist/core/compose.d.ts +23 -0
  42. package/dist/core/compose.d.ts.map +1 -0
  43. package/dist/core/compose.js +104 -0
  44. package/dist/core/compose.js.map +1 -0
  45. package/dist/core/execution.d.ts +44 -0
  46. package/dist/core/execution.d.ts.map +1 -0
  47. package/dist/core/execution.js +304 -0
  48. package/dist/core/execution.js.map +1 -0
  49. package/dist/core/failure.d.ts +23 -0
  50. package/dist/core/failure.d.ts.map +1 -0
  51. package/dist/core/failure.js +43 -0
  52. package/dist/core/failure.js.map +1 -0
  53. package/dist/core/graph.d.ts +12 -0
  54. package/dist/core/graph.d.ts.map +1 -0
  55. package/dist/core/graph.js +65 -0
  56. package/dist/core/graph.js.map +1 -0
  57. package/dist/core/hud.d.ts +11 -0
  58. package/dist/core/hud.d.ts.map +1 -0
  59. package/dist/core/hud.js +120 -0
  60. package/dist/core/hud.js.map +1 -0
  61. package/dist/core/invocation.d.ts +13 -0
  62. package/dist/core/invocation.d.ts.map +1 -0
  63. package/dist/core/invocation.js +68 -0
  64. package/dist/core/invocation.js.map +1 -0
  65. package/dist/core/json.d.ts +9 -0
  66. package/dist/core/json.d.ts.map +1 -0
  67. package/dist/core/json.js +74 -0
  68. package/dist/core/json.js.map +1 -0
  69. package/dist/core/library-manifest.d.ts +53 -0
  70. package/dist/core/library-manifest.d.ts.map +1 -0
  71. package/dist/core/library-manifest.js +263 -0
  72. package/dist/core/library-manifest.js.map +1 -0
  73. package/dist/core/library.d.ts +80 -0
  74. package/dist/core/library.d.ts.map +1 -0
  75. package/dist/core/library.js +321 -0
  76. package/dist/core/library.js.map +1 -0
  77. package/dist/core/parameters.d.ts +5 -0
  78. package/dist/core/parameters.d.ts.map +1 -0
  79. package/dist/core/parameters.js +57 -0
  80. package/dist/core/parameters.js.map +1 -0
  81. package/dist/core/passive-observations.d.ts +25 -0
  82. package/dist/core/passive-observations.d.ts.map +1 -0
  83. package/dist/core/passive-observations.js +62 -0
  84. package/dist/core/passive-observations.js.map +1 -0
  85. package/dist/core/path.d.ts +7 -0
  86. package/dist/core/path.d.ts.map +1 -0
  87. package/dist/core/path.js +147 -0
  88. package/dist/core/path.js.map +1 -0
  89. package/dist/core/recording-cleanup.d.ts +10 -0
  90. package/dist/core/recording-cleanup.d.ts.map +1 -0
  91. package/dist/core/recording-cleanup.js +32 -0
  92. package/dist/core/recording-cleanup.js.map +1 -0
  93. package/dist/core/resolution-error.d.ts +7 -0
  94. package/dist/core/resolution-error.d.ts.map +1 -0
  95. package/dist/core/resolution-error.js +9 -0
  96. package/dist/core/resolution-error.js.map +1 -0
  97. package/dist/core/runner.d.ts +4 -0
  98. package/dist/core/runner.d.ts.map +1 -0
  99. package/dist/core/runner.js +738 -0
  100. package/dist/core/runner.js.map +1 -0
  101. package/dist/core/scroll-to.d.ts +111 -0
  102. package/dist/core/scroll-to.d.ts.map +1 -0
  103. package/dist/core/scroll-to.js +353 -0
  104. package/dist/core/scroll-to.js.map +1 -0
  105. package/dist/core/suite.d.ts +25 -0
  106. package/dist/core/suite.d.ts.map +1 -0
  107. package/dist/core/suite.js +76 -0
  108. package/dist/core/suite.js.map +1 -0
  109. package/dist/core/trace.d.ts +5 -0
  110. package/dist/core/trace.d.ts.map +1 -0
  111. package/dist/core/trace.js +25 -0
  112. package/dist/core/trace.js.map +1 -0
  113. package/dist/core/trust-error.d.ts +9 -0
  114. package/dist/core/trust-error.d.ts.map +1 -0
  115. package/dist/core/trust-error.js +18 -0
  116. package/dist/core/trust-error.js.map +1 -0
  117. package/dist/core/trust-input.d.ts +20 -0
  118. package/dist/core/trust-input.d.ts.map +1 -0
  119. package/dist/core/trust-input.js +68 -0
  120. package/dist/core/trust-input.js.map +1 -0
  121. package/dist/core/trust.d.ts +28 -0
  122. package/dist/core/trust.d.ts.map +1 -0
  123. package/dist/core/trust.js +284 -0
  124. package/dist/core/trust.js.map +1 -0
  125. package/dist/core/types.d.ts +319 -0
  126. package/dist/core/types.d.ts.map +1 -0
  127. package/dist/core/types.js +2 -0
  128. package/dist/core/types.js.map +1 -0
  129. package/dist/core/validation.d.ts +4 -0
  130. package/dist/core/validation.d.ts.map +1 -0
  131. package/dist/core/validation.js +18 -0
  132. package/dist/core/validation.js.map +1 -0
  133. package/dist/index.d.ts +32 -0
  134. package/dist/index.d.ts.map +1 -0
  135. package/dist/index.js +25 -0
  136. package/dist/index.js.map +1 -0
  137. package/dist/node/writers.d.ts +27 -0
  138. package/dist/node/writers.d.ts.map +1 -0
  139. package/dist/node/writers.js +99 -0
  140. package/dist/node/writers.js.map +1 -0
  141. package/dist/recording/android-mirror.d.ts +10 -0
  142. package/dist/recording/android-mirror.d.ts.map +1 -0
  143. package/dist/recording/android-mirror.js +180 -0
  144. package/dist/recording/android-mirror.js.map +1 -0
  145. package/dist/recording/capture-helper-timing.d.ts +4 -0
  146. package/dist/recording/capture-helper-timing.d.ts.map +1 -0
  147. package/dist/recording/capture-helper-timing.js +47 -0
  148. package/dist/recording/capture-helper-timing.js.map +1 -0
  149. package/dist/recording/capture-helper.d.ts +10 -0
  150. package/dist/recording/capture-helper.d.ts.map +1 -0
  151. package/dist/recording/capture-helper.js +335 -0
  152. package/dist/recording/capture-helper.js.map +1 -0
  153. package/dist/recording/cdp-video-recorder.d.ts +9 -0
  154. package/dist/recording/cdp-video-recorder.d.ts.map +1 -0
  155. package/dist/recording/cdp-video-recorder.js +319 -0
  156. package/dist/recording/cdp-video-recorder.js.map +1 -0
  157. package/dist/recording/frame-sampler.d.ts +19 -0
  158. package/dist/recording/frame-sampler.d.ts.map +1 -0
  159. package/dist/recording/frame-sampler.js +29 -0
  160. package/dist/recording/frame-sampler.js.map +1 -0
  161. package/dist/recording/timeline.d.ts +15 -0
  162. package/dist/recording/timeline.d.ts.map +1 -0
  163. package/dist/recording/timeline.js +88 -0
  164. package/dist/recording/timeline.js.map +1 -0
  165. package/dist/runtime/browser-extension.d.ts +17 -0
  166. package/dist/runtime/browser-extension.d.ts.map +1 -0
  167. package/dist/runtime/browser-extension.js +42 -0
  168. package/dist/runtime/browser-extension.js.map +1 -0
  169. package/dist/runtime/cdp.d.ts +138 -0
  170. package/dist/runtime/cdp.d.ts.map +1 -0
  171. package/dist/runtime/cdp.js +1429 -0
  172. package/dist/runtime/cdp.js.map +1 -0
  173. package/dist/runtime/decision-types.d.ts +23 -0
  174. package/dist/runtime/decision-types.d.ts.map +1 -0
  175. package/dist/runtime/decision-types.js +2 -0
  176. package/dist/runtime/decision-types.js.map +1 -0
  177. package/dist/runtime/deps-readiness.d.ts +21 -0
  178. package/dist/runtime/deps-readiness.d.ts.map +1 -0
  179. package/dist/runtime/deps-readiness.js +141 -0
  180. package/dist/runtime/deps-readiness.js.map +1 -0
  181. package/dist/runtime/log-analysis.d.ts +48 -0
  182. package/dist/runtime/log-analysis.d.ts.map +1 -0
  183. package/dist/runtime/log-analysis.js +128 -0
  184. package/dist/runtime/log-analysis.js.map +1 -0
  185. package/dist/runtime/metro-probe.d.ts +5 -0
  186. package/dist/runtime/metro-probe.d.ts.map +1 -0
  187. package/dist/runtime/metro-probe.js +24 -0
  188. package/dist/runtime/metro-probe.js.map +1 -0
  189. package/dist/runtime/operation.d.ts +32 -0
  190. package/dist/runtime/operation.d.ts.map +1 -0
  191. package/dist/runtime/operation.js +184 -0
  192. package/dist/runtime/operation.js.map +1 -0
  193. package/dist/runtime/orchestrate-up.d.ts +22 -0
  194. package/dist/runtime/orchestrate-up.d.ts.map +1 -0
  195. package/dist/runtime/orchestrate-up.js +35 -0
  196. package/dist/runtime/orchestrate-up.js.map +1 -0
  197. package/dist/runtime/react-native-bridge.d.ts +17 -0
  198. package/dist/runtime/react-native-bridge.d.ts.map +1 -0
  199. package/dist/runtime/react-native-bridge.js +57 -0
  200. package/dist/runtime/react-native-bridge.js.map +1 -0
  201. package/dist/version.d.ts +2 -0
  202. package/dist/version.d.ts.map +1 -0
  203. package/dist/version.js +8 -0
  204. package/dist/version.js.map +1 -0
  205. package/package.json +163 -0
  206. package/test/fixtures/scroll-to-visible/README.md +39 -0
  207. package/visual-review/feedback-draft.mjs +82 -0
  208. package/visual-review/index.d.mts +41 -0
  209. package/visual-review/index.mjs +4 -0
  210. package/visual-review/recipe-board.mjs +142 -0
  211. package/visual-review/review-board.mjs +981 -0
  212. package/visual-review/server.mjs +69 -0
@@ -0,0 +1,319 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { mkdir, mkdtemp, rm, stat, writeFile } from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import { CdpSession, selectCdpTarget, sleep } from '../runtime/cdp.js';
5
+ import { errorMessage } from './capture-helper.js';
6
+ import { RecordingFrameSampler } from './frame-sampler.js';
7
+ import { optionalVideoTiming } from './timeline.js';
8
+ export function createCdpVideoRecorder(options) {
9
+ return new CdpVideoRecorder(options);
10
+ }
11
+ class CdpVideoRecorder {
12
+ #cdpPort;
13
+ #cdpHost;
14
+ #urlIncludes;
15
+ #ffmpegPath;
16
+ constructor(options) {
17
+ this.name = 'cdp-screencast';
18
+ this.platform = 'web';
19
+ this.#cdpPort = options.cdpPort;
20
+ this.#cdpHost = options.cdpHost ?? '127.0.0.1';
21
+ this.#urlIncludes = options.urlIncludes;
22
+ this.#ffmpegPath = options.ffmpegPath ?? process.env.FFMPEG_PATH ?? 'ffmpeg';
23
+ }
24
+ async doctor() {
25
+ try {
26
+ const bytes = await probeCdpScreenshot(this.#cdpHost, this.#cdpPort, this.#urlIncludes);
27
+ if (bytes < 64) {
28
+ return {
29
+ ok: false,
30
+ code: 'cdp_screenshot_empty',
31
+ message: 'CDP Page.captureScreenshot returned no image data.',
32
+ suggestedFix: 'Ensure CDP Chrome is open on the recipe UI route.',
33
+ };
34
+ }
35
+ return {
36
+ ok: true,
37
+ code: 'ok',
38
+ message: `CDP Page.captureScreenshot ready (${bytes} bytes probe; browser capture).`,
39
+ };
40
+ }
41
+ catch (error) {
42
+ return {
43
+ ok: false,
44
+ code: 'cdp_unavailable',
45
+ message: `CDP screenshot probe failed: ${errorMessage(error)}`,
46
+ suggestedFix: 'Launch debug-chrome on the slot UI port before --record-video.',
47
+ };
48
+ }
49
+ }
50
+ async start(request) {
51
+ const maxFps = request.maxFps ?? 30;
52
+ if (!Number.isFinite(maxFps) || maxFps <= 0)
53
+ throw new Error('Recording maxFps must be positive.');
54
+ const target = await selectCdpTarget({
55
+ host: this.#cdpHost,
56
+ port: this.#cdpPort,
57
+ type: 'page',
58
+ urlIncludes: this.#urlIncludes,
59
+ });
60
+ if (!target.webSocketDebuggerUrl)
61
+ throw new Error('Selected CDP target has no debugger URL.');
62
+ const session = await CdpSession.connect(target.webSocketDebuggerUrl);
63
+ let framesDir;
64
+ try {
65
+ framesDir = await mkdtemp(path.join(path.dirname(request.outputPath), '.cdp-frames-'));
66
+ }
67
+ catch (error) {
68
+ session.close();
69
+ throw error;
70
+ }
71
+ const timestamps = [];
72
+ let lastReceivedAt = 0;
73
+ const pendingAcks = new Set();
74
+ let captureError;
75
+ let receivedFrames = 0;
76
+ const sampler = new RecordingFrameSampler(maxFps);
77
+ let writeChain = Promise.resolve();
78
+ let clock;
79
+ let clockError;
80
+ try {
81
+ clock = await measureBrowserClock(session);
82
+ }
83
+ catch (error) {
84
+ clockError = `Recording clock unavailable: ${errorMessage(error)}`;
85
+ }
86
+ const retain = async (frame) => {
87
+ const index = timestamps.length;
88
+ timestamps.push(frame.timestampMs);
89
+ lastReceivedAt = frame.receivedAtUnixMs;
90
+ await writeFile(path.join(framesDir, `frame_${String(index).padStart(6, '0')}.png`), Buffer.from(frame.data, 'base64'));
91
+ };
92
+ const unsubscribe = session.on('Page.screencastFrame', (params) => {
93
+ const timestamp = params.metadata?.timestamp;
94
+ const data = params.data;
95
+ const receivedAtUnixMs = Date.now();
96
+ const ack = session
97
+ .call('Page.screencastFrameAck', { sessionId: params.sessionId })
98
+ .catch((error) => {
99
+ captureError = error;
100
+ })
101
+ .finally(() => pendingAcks.delete(ack));
102
+ pendingAcks.add(ack);
103
+ writeChain = writeChain
104
+ .then(async () => {
105
+ if (typeof timestamp !== 'number' ||
106
+ !Number.isFinite(timestamp) ||
107
+ typeof data !== 'string')
108
+ throw new Error('CDP frame is missing its source timestamp or image.');
109
+ const ready = sampler.accept({ timestampMs: timestamp * 1000, receivedAtUnixMs, data });
110
+ if (ready)
111
+ await retain(ready);
112
+ receivedFrames++;
113
+ })
114
+ .catch((error) => {
115
+ captureError = error;
116
+ });
117
+ });
118
+ try {
119
+ await session.call('Page.enable');
120
+ await session.call('Page.startScreencast', {
121
+ format: 'png',
122
+ everyNthFrame: 1,
123
+ ...(screencastMaxBounds(request.maxSize) ?? {}),
124
+ });
125
+ const deadline = Date.now() + 10_000;
126
+ while (!receivedFrames && !captureError && Date.now() < deadline)
127
+ await sleep(20);
128
+ if (captureError)
129
+ throw captureError;
130
+ if (!receivedFrames)
131
+ throw new Error('CDP recorder did not produce its first frame.');
132
+ }
133
+ catch (error) {
134
+ unsubscribe();
135
+ session.close();
136
+ await writeChain;
137
+ await rm(framesDir, { recursive: true, force: true });
138
+ throw error;
139
+ }
140
+ const ffmpegPath = this.#ffmpegPath;
141
+ let stopped = false;
142
+ return {
143
+ async stop() {
144
+ if (stopped)
145
+ throw new Error('CDP recording was already stopped.');
146
+ stopped = true;
147
+ const stoppedAtUnixMs = Date.now();
148
+ unsubscribe();
149
+ try {
150
+ await Promise.all(pendingAcks);
151
+ await session.call('Page.stopScreencast');
152
+ await writeChain;
153
+ if (captureError)
154
+ throw captureError;
155
+ const finalFrame = sampler.finish();
156
+ if (finalFrame)
157
+ await retain(finalFrame);
158
+ if (clock) {
159
+ try {
160
+ const endClock = await measureBrowserClock(session);
161
+ clock.minOffsetMs = Math.min(clock.minOffsetMs, endClock.minOffsetMs);
162
+ clock.maxOffsetMs = Math.max(clock.maxOffsetMs, endClock.maxOffsetMs);
163
+ }
164
+ catch (error) {
165
+ clockError = `Recording end clock unavailable: ${errorMessage(error)}`;
166
+ clock = undefined;
167
+ }
168
+ }
169
+ await encodePngSequenceToMp4({
170
+ ffmpegPath,
171
+ framesDir,
172
+ outputPath: request.outputPath,
173
+ timestamps,
174
+ lastFrameDurationMs: Math.max(1, clock
175
+ ? stoppedAtUnixMs + (clock.minOffsetMs + clock.maxOffsetMs) / 2 - timestamps.at(-1)
176
+ : stoppedAtUnixMs - lastReceivedAt),
177
+ });
178
+ const stats = await stat(request.outputPath);
179
+ if (stats.size <= 0)
180
+ throw new Error(`Recording output is empty: ${request.outputPath}`);
181
+ // A CDP first frame can predate startScreencast. Use its source clock,
182
+ // calibrated through an isolated world, rather than process lifetime.
183
+ const timing = clock
184
+ ? await optionalVideoTiming(request.outputPath, {
185
+ clock: {
186
+ source: 'cdp-source-clock',
187
+ earliestZeroUnixMs: timestamps[0] - clock.maxOffsetMs,
188
+ latestZeroUnixMs: timestamps[0] - clock.minOffsetMs,
189
+ },
190
+ })
191
+ : { timingUnavailableReason: clockError };
192
+ return {
193
+ ...timing,
194
+ recorder: {
195
+ name: 'cdp-screencast',
196
+ platform: 'web',
197
+ target: { selector: 'cdp-page', value: target.url ?? `cdp:${request.nodeId}` },
198
+ },
199
+ };
200
+ }
201
+ finally {
202
+ session.close();
203
+ await rm(framesDir, { recursive: true, force: true });
204
+ }
205
+ },
206
+ };
207
+ }
208
+ }
209
+ async function measureBrowserClock(session) {
210
+ const tree = await session.call('Page.getFrameTree');
211
+ const world = await session.call('Page.createIsolatedWorld', {
212
+ frameId: tree.frameTree.frame.id,
213
+ worldName: 'recipe-recording-clock',
214
+ });
215
+ const before = Date.now();
216
+ const result = await session.call('Runtime.evaluate', {
217
+ expression: 'Date.now()',
218
+ contextId: world.executionContextId,
219
+ returnByValue: true,
220
+ });
221
+ const after = Date.now();
222
+ const remote = result.result.value;
223
+ if (typeof remote !== 'number' || !Number.isFinite(remote))
224
+ throw new Error('Browser clock sample is unavailable.');
225
+ // Millisecond clock quantization contributes one millisecond at each bound.
226
+ return { minOffsetMs: remote - after - 1, maxOffsetMs: remote - before + 1 };
227
+ }
228
+ function screencastMaxBounds(maxSize) {
229
+ if (maxSize == null || !Number.isFinite(maxSize) || maxSize <= 0)
230
+ return undefined;
231
+ const edge = Math.round(maxSize);
232
+ return { maxWidth: edge, maxHeight: edge };
233
+ }
234
+ async function probeCdpScreenshot(host, port, urlIncludes) {
235
+ const target = await selectCdpTarget({
236
+ host,
237
+ port,
238
+ type: 'page',
239
+ urlIncludes,
240
+ });
241
+ if (!target.webSocketDebuggerUrl) {
242
+ throw new Error('CDP target missing webSocketDebuggerUrl.');
243
+ }
244
+ const session = await CdpSession.connect(target.webSocketDebuggerUrl);
245
+ try {
246
+ await session.call('Page.enable');
247
+ const shot = await session.call('Page.captureScreenshot', {
248
+ format: 'png',
249
+ });
250
+ return shot.data ? Buffer.from(shot.data, 'base64').length : 0;
251
+ }
252
+ finally {
253
+ session.close();
254
+ }
255
+ }
256
+ async function encodePngSequenceToMp4({ ffmpegPath, framesDir, outputPath, timestamps, lastFrameDurationMs, }) {
257
+ if (!timestamps.length)
258
+ throw new Error('No CDP frames captured.');
259
+ await mkdir(path.dirname(outputPath), { recursive: true });
260
+ // ffconcat preserves variable capture intervals. The last repeated image
261
+ // holds the final captured state through stop; it is not another observation.
262
+ const file = (index) => `frame_${String(index).padStart(6, '0')}.png`;
263
+ const rows = timestamps.map((timestamp, index) => {
264
+ const durationMs = index + 1 < timestamps.length ? timestamps[index + 1] - timestamp : lastFrameDurationMs;
265
+ return `file '${file(index)}'\noption framerate 1000\nduration ${(durationMs / 1000).toFixed(6)}`;
266
+ });
267
+ rows.push(`file '${file(timestamps.length - 1)}'\noption framerate 1000`);
268
+ const listPath = path.join(framesDir, 'frames.ffconcat');
269
+ await writeFile(listPath, `ffconcat version 1.0\n${rows.join('\n')}\n`);
270
+ await runCommand(ffmpegPath, [
271
+ '-y',
272
+ '-hide_banner',
273
+ '-loglevel',
274
+ 'error',
275
+ '-f',
276
+ 'concat',
277
+ '-safe',
278
+ '0',
279
+ '-i',
280
+ listPath,
281
+ '-fps_mode',
282
+ 'vfr',
283
+ '-c:v',
284
+ 'libx264',
285
+ '-pix_fmt',
286
+ 'yuv420p',
287
+ '-vf',
288
+ 'pad=ceil(iw/2)*2:ceil(ih/2)*2',
289
+ '-video_track_timescale',
290
+ '1000000',
291
+ '-movflags',
292
+ '+faststart',
293
+ outputPath,
294
+ ], { timeoutMs: 120_000 });
295
+ }
296
+ function runCommand(command, args, options) {
297
+ return new Promise((resolve, reject) => {
298
+ const child = spawn(command, args, { stdio: ['ignore', 'pipe', 'pipe'] });
299
+ let stderr = '';
300
+ const timer = setTimeout(() => {
301
+ child.kill('SIGKILL');
302
+ reject(new Error(`${command} timed out after ${options.timeoutMs}ms`));
303
+ }, options.timeoutMs);
304
+ child.stderr.setEncoding('utf-8');
305
+ child.stderr.on('data', (chunk) => (stderr += chunk));
306
+ child.once('error', (error) => {
307
+ clearTimeout(timer);
308
+ reject(error);
309
+ });
310
+ child.once('exit', (code) => {
311
+ clearTimeout(timer);
312
+ if (code === 0)
313
+ resolve();
314
+ else
315
+ reject(new Error(`${command} failed (exit ${code}): ${stderr.trim()}`));
316
+ });
317
+ });
318
+ }
319
+ //# sourceMappingURL=cdp-video-recorder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cdp-video-recorder.js","sourceRoot":"","sources":["../../src/recording/cdp-video-recorder.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ export interface CapturedFrame {
2
+ timestampMs: number;
3
+ receivedAtUnixMs: number;
4
+ data: string;
5
+ }
6
+ /** Keep the last observed repaint in each source-clock sampling window.
7
+ * Dropping every frame too close to the preceding one can lose the final
8
+ * repaint of an otherwise idle page indefinitely. */
9
+ export declare class RecordingFrameSampler {
10
+ private readonly maxFps;
11
+ private originMs;
12
+ private bucket;
13
+ private pending;
14
+ private lastTimestampMs;
15
+ constructor(maxFps: number);
16
+ accept(frame: CapturedFrame): CapturedFrame | undefined;
17
+ finish(): CapturedFrame | undefined;
18
+ }
19
+ //# sourceMappingURL=frame-sampler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frame-sampler.d.ts","sourceRoot":"","sources":["../../src/recording/frame-sampler.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;qDAEqD;AACrD,qBAAa,qBAAqB;IAMpB,OAAO,CAAC,QAAQ,CAAC,MAAM;IALnC,OAAO,CAAC,QAAQ,CAAqB;IACrC,OAAO,CAAC,MAAM,CAAM;IACpB,OAAO,CAAC,OAAO,CAA4B;IAC3C,OAAO,CAAC,eAAe,CAAa;gBAEP,MAAM,EAAE,MAAM;IAK3C,MAAM,CAAC,KAAK,EAAE,aAAa,GAAG,aAAa,GAAG,SAAS;IAYvD,MAAM,IAAI,aAAa,GAAG,SAAS;CAKpC"}
@@ -0,0 +1,29 @@
1
+ /** Keep the last observed repaint in each source-clock sampling window.
2
+ * Dropping every frame too close to the preceding one can lose the final
3
+ * repaint of an otherwise idle page indefinitely. */
4
+ export class RecordingFrameSampler {
5
+ constructor(maxFps) {
6
+ this.maxFps = maxFps;
7
+ this.bucket = -1;
8
+ this.lastTimestampMs = -Infinity;
9
+ if (!Number.isFinite(maxFps) || maxFps <= 0)
10
+ throw new Error('Recording maxFps must be positive.');
11
+ }
12
+ accept(frame) {
13
+ if (!Number.isFinite(frame.timestampMs) || frame.timestampMs < this.lastTimestampMs)
14
+ throw new Error('Recording frame timestamp is invalid or moved backwards.');
15
+ this.lastTimestampMs = frame.timestampMs;
16
+ this.originMs ??= frame.timestampMs;
17
+ const bucket = Math.floor(((frame.timestampMs - this.originMs) * this.maxFps) / 1000);
18
+ const ready = bucket !== this.bucket ? this.pending : undefined;
19
+ this.bucket = bucket;
20
+ this.pending = frame;
21
+ return ready;
22
+ }
23
+ finish() {
24
+ const frame = this.pending;
25
+ this.pending = undefined;
26
+ return frame;
27
+ }
28
+ }
29
+ //# sourceMappingURL=frame-sampler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frame-sampler.js","sourceRoot":"","sources":["../../src/recording/frame-sampler.ts"],"names":[],"mappings":"AAMA;;qDAEqD;AACrD,MAAM,OAAO,qBAAqB;IAMhC,YAA6B,MAAc;QAAd,WAAM,GAAN,MAAM,CAAQ;QAJnC,WAAM,GAAG,CAAC,CAAC,CAAC;QAEZ,oBAAe,GAAG,CAAC,QAAQ,CAAC;QAGlC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,MAAM,IAAI,CAAC;YACzC,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;IAC1D,CAAC;IAED,MAAM,CAAC,KAAoB;QACzB,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,KAAK,CAAC,WAAW,GAAG,IAAI,CAAC,eAAe;YACjF,MAAM,IAAI,KAAK,CAAC,0DAA0D,CAAC,CAAC;QAC9E,IAAI,CAAC,eAAe,GAAG,KAAK,CAAC,WAAW,CAAC;QACzC,IAAI,CAAC,QAAQ,KAAK,KAAK,CAAC,WAAW,CAAC;QACpC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC;QACtF,MAAM,KAAK,GAAG,MAAM,KAAK,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;QAChE,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;QACrB,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM;QACJ,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC;QAC3B,IAAI,CAAC,OAAO,GAAG,SAAS,CAAC;QACzB,OAAO,KAAK,CAAC;IACf,CAAC;CACF"}
@@ -0,0 +1,15 @@
1
+ import { type RecipeRecordingTimelineDocument, type RecipeVideoTiming } from '@farmslot/protocol';
2
+ import type { TraceEntry, VideoRecordingResult } from '../core/types.js';
3
+ /** For continuous, real-time recordings: capture happened within these host
4
+ * lifecycle bounds. This is deliberately a range, never a process-start guess. */
5
+ export declare function probeVideoTiming(file: string, bounds: {
6
+ startedAtUnixMs: number;
7
+ stoppedAtUnixMs: number;
8
+ } | {
9
+ clock: RecipeVideoTiming['clock'];
10
+ }, ffprobePath?: string): Promise<RecipeVideoTiming>;
11
+ /** Optional observability failure stays explicit without discarding a valid video. */
12
+ export declare function optionalVideoTiming(...args: Parameters<typeof probeVideoTiming>): Promise<Pick<VideoRecordingResult, 'timing' | 'timingUnavailableReason'>>;
13
+ export declare function createRecordingTimeline(videoPath: string, videoDigest: string, timing: RecipeVideoTiming, trace: TraceEntry[]): RecipeRecordingTimelineDocument;
14
+ export declare function writeRecordingTimeline(artifactsDir: string, videoPath: string, timing: RecipeVideoTiming, trace: TraceEntry[]): Promise<string>;
15
+ //# sourceMappingURL=timeline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timeline.d.ts","sourceRoot":"","sources":["../../src/recording/timeline.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,+BAA+B,EACpC,KAAK,iBAAiB,EAEvB,MAAM,oBAAoB,CAAC;AAG5B,OAAO,KAAK,EAAE,UAAU,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAIzE;kFACkF;AAClF,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,MAAM,EACZ,MAAM,EACF;IAAE,eAAe,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,CAAA;CAAE,GACpD;IAAE,KAAK,EAAE,iBAAiB,CAAC,OAAO,CAAC,CAAA;CAAE,EACzC,WAAW,SAAwC,GAClD,OAAO,CAAC,iBAAiB,CAAC,CAwD5B;AAED,sFAAsF;AACtF,wBAAsB,mBAAmB,CACvC,GAAG,IAAI,EAAE,UAAU,CAAC,OAAO,gBAAgB,CAAC,GAC3C,OAAO,CAAC,IAAI,CAAC,oBAAoB,EAAE,QAAQ,GAAG,yBAAyB,CAAC,CAAC,CAM3E;AAED,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,iBAAiB,EACzB,KAAK,EAAE,UAAU,EAAE,GAClB,+BAA+B,CAuBjC;AAED,wBAAsB,sBAAsB,CAC1C,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,iBAAiB,EACzB,KAAK,EAAE,UAAU,EAAE,GAClB,OAAO,CAAC,MAAM,CAAC,CAMjB"}
@@ -0,0 +1,88 @@
1
+ import { execFile } from 'node:child_process';
2
+ import { promisify } from 'node:util';
3
+ import { digestRecipeDocument, validateRecipeRecordingTimelineDocument, } from '@farmslot/protocol';
4
+ import { digestFileWithinRoot, writeFileWithinRoot } from '../core/path.js';
5
+ const exec = promisify(execFile);
6
+ /** For continuous, real-time recordings: capture happened within these host
7
+ * lifecycle bounds. This is deliberately a range, never a process-start guess. */
8
+ export async function probeVideoTiming(file, bounds, ffprobePath = process.env.FFPROBE_PATH ?? 'ffprobe') {
9
+ const { stdout } = await exec(ffprobePath, [
10
+ '-v',
11
+ 'error',
12
+ '-select_streams',
13
+ 'v:0',
14
+ '-show_frames',
15
+ '-show_entries',
16
+ 'frame=best_effort_timestamp_time,pkt_duration_time:format=duration',
17
+ '-of',
18
+ 'json',
19
+ file,
20
+ ], { timeout: 120_000, maxBuffer: 32 * 1024 * 1024 });
21
+ const probe = JSON.parse(stdout);
22
+ const framesMs = (probe.frames ?? []).map((frame) => Number(frame.best_effort_timestamp_time) * 1000);
23
+ const first = framesMs[0];
24
+ const last = framesMs.at(-1);
25
+ if (first === undefined ||
26
+ last === undefined ||
27
+ framesMs.some((time, i) => !Number.isFinite(time) || time < 0 || (i > 0 && time <= framesMs[i - 1])))
28
+ throw new Error('Video has no strictly ordered presentation timestamps.');
29
+ const durationMs = Number(probe.format?.duration) * 1000;
30
+ if (!Number.isFinite(durationMs) || durationMs <= last)
31
+ throw new Error('Video duration does not contain its measured frames.');
32
+ const earliestZeroUnixMs = 'clock' in bounds ? bounds.clock.earliestZeroUnixMs : bounds.startedAtUnixMs - first;
33
+ const latestZeroUnixMs = 'clock' in bounds ? bounds.clock.latestZeroUnixMs : bounds.stoppedAtUnixMs - last;
34
+ if (!Number.isFinite(earliestZeroUnixMs) ||
35
+ !Number.isFinite(latestZeroUnixMs) ||
36
+ latestZeroUnixMs < earliestZeroUnixMs)
37
+ throw new Error('Video timestamps do not fit the observed recorder lifetime.');
38
+ return {
39
+ framesMs,
40
+ durationMs,
41
+ clock: {
42
+ source: 'clock' in bounds ? bounds.clock.source : 'recorder-lifetime-bound',
43
+ earliestZeroUnixMs,
44
+ latestZeroUnixMs,
45
+ },
46
+ };
47
+ }
48
+ /** Optional observability failure stays explicit without discarding a valid video. */
49
+ export async function optionalVideoTiming(...args) {
50
+ try {
51
+ return { timing: await probeVideoTiming(...args) };
52
+ }
53
+ catch (error) {
54
+ return { timingUnavailableReason: error instanceof Error ? error.message : String(error) };
55
+ }
56
+ }
57
+ export function createRecordingTimeline(videoPath, videoDigest, timing, trace) {
58
+ const { earliestZeroUnixMs: earliest, latestZeroUnixMs: latest } = timing.clock;
59
+ const document = {
60
+ ...timing,
61
+ version: 1,
62
+ videoPath,
63
+ videoDigest,
64
+ traceDigest: digestRecipeDocument(trace),
65
+ markers: trace.map((entry, traceIndex) => ({
66
+ traceIndex,
67
+ nodeId: entry.nodeId,
68
+ action: entry.action,
69
+ ok: entry.ok,
70
+ ...(entry.intent ? { intent: entry.intent } : {}),
71
+ ...(entry.proves ? { proves: entry.proves } : {}),
72
+ startRangeMs: [Date.parse(entry.startedAt) - latest, Date.parse(entry.startedAt) - earliest],
73
+ endRangeMs: [Date.parse(entry.endedAt) - latest, Date.parse(entry.endedAt) - earliest],
74
+ })),
75
+ };
76
+ const result = validateRecipeRecordingTimelineDocument(document);
77
+ if (result.status === 'invalid')
78
+ throw new Error(`Invalid recording timeline: ${JSON.stringify(result.findings)}`);
79
+ return document;
80
+ }
81
+ export async function writeRecordingTimeline(artifactsDir, videoPath, timing, trace) {
82
+ const videoDigest = await digestFileWithinRoot(artifactsDir, videoPath);
83
+ const document = createRecordingTimeline(videoPath, videoDigest, timing, trace);
84
+ const timelinePath = `${videoPath}.timeline.json`;
85
+ await writeFileWithinRoot(artifactsDir, timelinePath, `${JSON.stringify(document)}\n`);
86
+ return timelinePath;
87
+ }
88
+ //# sourceMappingURL=timeline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timeline.js","sourceRoot":"","sources":["../../src/recording/timeline.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import { type CdpTargetInfo } from './cdp.js';
2
+ export interface SelectBrowserExtensionPageTargetOptions {
3
+ host?: string;
4
+ port: number;
5
+ extensionId?: string;
6
+ pathIncludes?: string;
7
+ titleIncludes?: string;
8
+ }
9
+ export declare function extensionIdFromTarget(target: Pick<CdpTargetInfo, 'url'>): string | undefined;
10
+ export declare function selectBrowserExtensionPageTarget(options: SelectBrowserExtensionPageTargetOptions): Promise<CdpTargetInfo>;
11
+ export declare function openBrowserExtensionPage(options: {
12
+ host?: string;
13
+ port: number;
14
+ extensionId: string;
15
+ path?: string;
16
+ }): Promise<CdpTargetInfo>;
17
+ //# sourceMappingURL=browser-extension.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-extension.d.ts","sourceRoot":"","sources":["../../src/runtime/browser-extension.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAAiC,MAAM,UAAU,CAAC;AAE7E,MAAM,WAAW,uCAAuC;IACtD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,IAAI,CAAC,aAAa,EAAE,KAAK,CAAC,GAAG,MAAM,GAAG,SAAS,CAG5F;AAED,wBAAsB,gCAAgC,CACpD,OAAO,EAAE,uCAAuC,GAC/C,OAAO,CAAC,aAAa,CAAC,CAcxB;AAED,wBAAsB,wBAAwB,CAAC,OAAO,EAAE;IACtD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,GAAG,OAAO,CAAC,aAAa,CAAC,CAmBzB"}
@@ -0,0 +1,42 @@
1
+ import { retryJsonGet, selectCdpTarget } from './cdp.js';
2
+ export function extensionIdFromTarget(target) {
3
+ const match = target.url?.match(/^chrome-extension:\/\/([^/]+)/);
4
+ return match?.[1];
5
+ }
6
+ export async function selectBrowserExtensionPageTarget(options) {
7
+ return selectCdpTarget({
8
+ host: options.host,
9
+ port: options.port,
10
+ type: 'page',
11
+ titleIncludes: options.titleIncludes,
12
+ predicate(target) {
13
+ const extensionId = extensionIdFromTarget(target);
14
+ if (!extensionId)
15
+ return false;
16
+ if (options.extensionId && extensionId !== options.extensionId)
17
+ return false;
18
+ if (options.pathIncludes && !target.url?.includes(options.pathIncludes))
19
+ return false;
20
+ return true;
21
+ },
22
+ });
23
+ }
24
+ export async function openBrowserExtensionPage(options) {
25
+ const host = options.host ?? '127.0.0.1';
26
+ const extensionPath = options.path?.startsWith('/')
27
+ ? options.path.slice(1)
28
+ : (options.path ?? '');
29
+ const url = `chrome-extension://${options.extensionId}/${extensionPath}`;
30
+ const response = await fetch(`http://${host}:${options.port}/json/new?${encodeURIComponent(url)}`, {
31
+ method: 'PUT',
32
+ });
33
+ if (!response.ok) {
34
+ throw new Error(`Opening extension page ${url} failed with HTTP ${response.status}.`);
35
+ }
36
+ const targets = await retryJsonGet(`http://${host}:${options.port}/json`);
37
+ const target = targets.find((candidate) => candidate.url === url);
38
+ if (!target)
39
+ throw new Error(`Opened extension page ${url}, but no CDP target appeared.`);
40
+ return target;
41
+ }
42
+ //# sourceMappingURL=browser-extension.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-extension.js","sourceRoot":"","sources":["../../src/runtime/browser-extension.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,YAAY,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAU7E,MAAM,UAAU,qBAAqB,CAAC,MAAkC;IACtE,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,+BAA+B,CAAC,CAAC;IACjE,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;AACpB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gCAAgC,CACpD,OAAgD;IAEhD,OAAO,eAAe,CAAC;QACrB,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,IAAI,EAAE,MAAM;QACZ,aAAa,EAAE,OAAO,CAAC,aAAa;QACpC,SAAS,CAAC,MAAM;YACd,MAAM,WAAW,GAAG,qBAAqB,CAAC,MAAM,CAAC,CAAC;YAClD,IAAI,CAAC,WAAW;gBAAE,OAAO,KAAK,CAAC;YAC/B,IAAI,OAAO,CAAC,WAAW,IAAI,WAAW,KAAK,OAAO,CAAC,WAAW;gBAAE,OAAO,KAAK,CAAC;YAC7E,IAAI,OAAO,CAAC,YAAY,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,QAAQ,CAAC,OAAO,CAAC,YAAY,CAAC;gBAAE,OAAO,KAAK,CAAC;YACtF,OAAO,IAAI,CAAC;QACd,CAAC;KACF,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAAC,OAK9C;IACC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,WAAW,CAAC;IACzC,MAAM,aAAa,GAAG,OAAO,CAAC,IAAI,EAAE,UAAU,CAAC,GAAG,CAAC;QACjD,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;QACvB,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;IACzB,MAAM,GAAG,GAAG,sBAAsB,OAAO,CAAC,WAAW,IAAI,aAAa,EAAE,CAAC;IACzE,MAAM,QAAQ,GAAG,MAAM,KAAK,CAC1B,UAAU,IAAI,IAAI,OAAO,CAAC,IAAI,aAAa,kBAAkB,CAAC,GAAG,CAAC,EAAE,EACpE;QACE,MAAM,EAAE,KAAK;KACd,CACF,CAAC;IACF,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,0BAA0B,GAAG,qBAAqB,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;IACxF,CAAC;IACD,MAAM,OAAO,GAAG,MAAM,YAAY,CAAkB,UAAU,IAAI,IAAI,OAAO,CAAC,IAAI,OAAO,CAAC,CAAC;IAC3F,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC;IAClE,IAAI,CAAC,MAAM;QAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,GAAG,+BAA+B,CAAC,CAAC;IAC1F,OAAO,MAAM,CAAC;AAChB,CAAC"}