screenci 0.0.69 → 0.0.71

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 (144) hide show
  1. package/README.md +45 -122
  2. package/dist/cli.d.ts +1 -0
  3. package/dist/cli.d.ts.map +1 -1
  4. package/dist/cli.js +68 -4
  5. package/dist/cli.js.map +1 -1
  6. package/dist/docs/manifest.d.ts +81 -50
  7. package/dist/docs/manifest.d.ts.map +1 -1
  8. package/dist/docs/manifest.js +36 -17
  9. package/dist/docs/manifest.js.map +1 -1
  10. package/dist/docs/video-sources/installation.screenci.d.ts +2 -0
  11. package/dist/docs/video-sources/installation.screenci.d.ts.map +1 -0
  12. package/dist/docs/video-sources/{installation.video.js → installation.screenci.js} +1 -1
  13. package/dist/docs/video-sources/installation.screenci.js.map +1 -0
  14. package/dist/docs/video-sources/landing.screenci.d.ts +2 -0
  15. package/dist/docs/video-sources/landing.screenci.d.ts.map +1 -0
  16. package/dist/docs/video-sources/{landing.video.js → landing.screenci.js} +1 -1
  17. package/dist/docs/video-sources/landing.screenci.js.map +1 -0
  18. package/dist/docs/video-sources/locator-highlight-animated.screenci.d.ts +2 -0
  19. package/dist/docs/video-sources/locator-highlight-animated.screenci.d.ts.map +1 -0
  20. package/dist/docs/video-sources/locator-highlight-animated.screenci.js +41 -0
  21. package/dist/docs/video-sources/locator-highlight-animated.screenci.js.map +1 -0
  22. package/dist/docs/video-sources/locator-highlight-still.screenci.d.ts +2 -0
  23. package/dist/docs/video-sources/locator-highlight-still.screenci.d.ts.map +1 -0
  24. package/dist/docs/video-sources/locator-highlight-still.screenci.js +19 -0
  25. package/dist/docs/video-sources/locator-highlight-still.screenci.js.map +1 -0
  26. package/dist/docs/video-sources/product-pitch.screenci.d.ts +2 -0
  27. package/dist/docs/video-sources/product-pitch.screenci.d.ts.map +1 -0
  28. package/dist/docs/video-sources/product-pitch.screenci.js +136 -0
  29. package/dist/docs/video-sources/product-pitch.screenci.js.map +1 -0
  30. package/dist/docs/video-sources/public-urls.screenci.d.ts +2 -0
  31. package/dist/docs/video-sources/public-urls.screenci.d.ts.map +1 -0
  32. package/dist/docs/video-sources/public-urls.screenci.js +34 -0
  33. package/dist/docs/video-sources/public-urls.screenci.js.map +1 -0
  34. package/dist/docs/video-sources/studio.screenci.d.ts +2 -0
  35. package/dist/docs/video-sources/studio.screenci.d.ts.map +1 -0
  36. package/dist/docs/video-sources/{studio.video.js → studio.screenci.js} +1 -1
  37. package/dist/docs/video-sources/studio.screenci.js.map +1 -0
  38. package/dist/docs/videos.d.ts +28 -18
  39. package/dist/docs/videos.d.ts.map +1 -1
  40. package/dist/docs/videos.js +14 -5
  41. package/dist/docs/videos.js.map +1 -1
  42. package/dist/e2e/screenshot.e2e.d.ts +2 -0
  43. package/dist/e2e/screenshot.e2e.d.ts.map +1 -0
  44. package/dist/e2e/screenshot.e2e.js +73 -0
  45. package/dist/e2e/screenshot.e2e.js.map +1 -0
  46. package/dist/index.d.ts +7 -3
  47. package/dist/index.d.ts.map +1 -1
  48. package/dist/index.js +3 -1
  49. package/dist/index.js.map +1 -1
  50. package/dist/src/asset.d.ts +119 -15
  51. package/dist/src/asset.d.ts.map +1 -1
  52. package/dist/src/asset.js +310 -124
  53. package/dist/src/asset.js.map +1 -1
  54. package/dist/src/audio.d.ts +14 -0
  55. package/dist/src/audio.d.ts.map +1 -1
  56. package/dist/src/audio.js +4 -1
  57. package/dist/src/audio.js.map +1 -1
  58. package/dist/src/config.d.ts.map +1 -1
  59. package/dist/src/config.js +9 -3
  60. package/dist/src/config.js.map +1 -1
  61. package/dist/src/contextOptions.d.ts +48 -0
  62. package/dist/src/contextOptions.d.ts.map +1 -0
  63. package/dist/src/contextOptions.js +71 -0
  64. package/dist/src/contextOptions.js.map +1 -0
  65. package/dist/src/crop.d.ts +93 -0
  66. package/dist/src/crop.d.ts.map +1 -0
  67. package/dist/src/crop.js +133 -0
  68. package/dist/src/crop.js.map +1 -0
  69. package/dist/src/cue.d.ts +17 -4
  70. package/dist/src/cue.d.ts.map +1 -1
  71. package/dist/src/cue.js +31 -3
  72. package/dist/src/cue.js.map +1 -1
  73. package/dist/src/defaults.d.ts +6 -0
  74. package/dist/src/defaults.d.ts.map +1 -1
  75. package/dist/src/defaults.js +6 -0
  76. package/dist/src/defaults.js.map +1 -1
  77. package/dist/src/dimensions.d.ts +6 -0
  78. package/dist/src/dimensions.d.ts.map +1 -1
  79. package/dist/src/dimensions.js +15 -0
  80. package/dist/src/dimensions.js.map +1 -1
  81. package/dist/src/events.d.ts +152 -10
  82. package/dist/src/events.d.ts.map +1 -1
  83. package/dist/src/events.js +61 -3
  84. package/dist/src/events.js.map +1 -1
  85. package/dist/src/htmlRasterizer.d.ts +43 -0
  86. package/dist/src/htmlRasterizer.d.ts.map +1 -1
  87. package/dist/src/htmlRasterizer.js +32 -10
  88. package/dist/src/htmlRasterizer.js.map +1 -1
  89. package/dist/src/init.d.ts.map +1 -1
  90. package/dist/src/init.js +19 -8
  91. package/dist/src/init.js.map +1 -1
  92. package/dist/src/overlayFlush.d.ts +18 -0
  93. package/dist/src/overlayFlush.d.ts.map +1 -0
  94. package/dist/src/overlayFlush.js +56 -0
  95. package/dist/src/overlayFlush.js.map +1 -0
  96. package/dist/src/overlayRect.d.ts +69 -0
  97. package/dist/src/overlayRect.d.ts.map +1 -0
  98. package/dist/src/overlayRect.js +55 -0
  99. package/dist/src/overlayRect.js.map +1 -0
  100. package/dist/src/recording.d.ts +3 -2
  101. package/dist/src/recording.d.ts.map +1 -1
  102. package/dist/src/recordingData.d.ts +20 -0
  103. package/dist/src/recordingData.d.ts.map +1 -1
  104. package/dist/src/runtimeContext.d.ts +19 -1
  105. package/dist/src/runtimeContext.d.ts.map +1 -1
  106. package/dist/src/runtimeContext.js +15 -0
  107. package/dist/src/runtimeContext.js.map +1 -1
  108. package/dist/src/screenshot.d.ts +105 -0
  109. package/dist/src/screenshot.d.ts.map +1 -0
  110. package/dist/src/screenshot.js +188 -0
  111. package/dist/src/screenshot.js.map +1 -0
  112. package/dist/src/stillCapture.d.ts +68 -0
  113. package/dist/src/stillCapture.d.ts.map +1 -0
  114. package/dist/src/stillCapture.js +119 -0
  115. package/dist/src/stillCapture.js.map +1 -0
  116. package/dist/src/studio.d.ts +8 -11
  117. package/dist/src/studio.d.ts.map +1 -1
  118. package/dist/src/studio.js +2 -25
  119. package/dist/src/studio.js.map +1 -1
  120. package/dist/src/types.d.ts +100 -8
  121. package/dist/src/types.d.ts.map +1 -1
  122. package/dist/src/types.js.map +1 -1
  123. package/dist/src/video.d.ts +20 -0
  124. package/dist/src/video.d.ts.map +1 -1
  125. package/dist/src/video.js +71 -24
  126. package/dist/src/video.js.map +1 -1
  127. package/dist/tsconfig.tsbuildinfo +1 -1
  128. package/package.json +1 -1
  129. package/skills/screenci/SKILL.md +10 -10
  130. package/skills/screenci/references/init.md +1 -1
  131. package/skills/screenci/references/record.md +2 -2
  132. package/dist/docs/video-sources/cli.video.d.ts +0 -2
  133. package/dist/docs/video-sources/cli.video.d.ts.map +0 -1
  134. package/dist/docs/video-sources/cli.video.js +0 -36
  135. package/dist/docs/video-sources/cli.video.js.map +0 -1
  136. package/dist/docs/video-sources/installation.video.d.ts +0 -2
  137. package/dist/docs/video-sources/installation.video.d.ts.map +0 -1
  138. package/dist/docs/video-sources/installation.video.js.map +0 -1
  139. package/dist/docs/video-sources/landing.video.d.ts +0 -2
  140. package/dist/docs/video-sources/landing.video.d.ts.map +0 -1
  141. package/dist/docs/video-sources/landing.video.js.map +0 -1
  142. package/dist/docs/video-sources/studio.video.d.ts +0 -2
  143. package/dist/docs/video-sources/studio.video.d.ts.map +0 -1
  144. package/dist/docs/video-sources/studio.video.js.map +0 -1
package/README.md CHANGED
@@ -3,53 +3,45 @@
3
3
  Your UI changed. Your demo videos didn't. screenci fixes that.
4
4
 
5
5
  Record product walkthroughs as code. When the UI ships, run
6
- `npx screenci record` and your videos regenerate.
6
+ `npx screenci record` and your videos regenerate. You keep the Playwright
7
+ mental model, but the output is a maintainable product video instead of a
8
+ test report.
7
9
 
8
- ScreenCI keeps the Playwright mental model, but the output is a maintainable
9
- product video instead of a test report.
10
+ Learn more at [screenci.com](https://screenci.com).
10
11
 
11
- ## Install and scaffold
12
+ ## Get started
12
13
 
13
14
  ```bash
14
15
  npm init screenci@latest
16
+ # or
15
17
  pnpm create screenci
16
18
  ```
17
19
 
18
- `init` creates a ready-to-run, self-contained `screenci/` directory ("the
19
- island") with its own dependencies, and installs Chromium by default. The
20
- island is deliberately isolated from any surrounding workspace, which makes
21
- installation reliable inside complex monorepos. When using `npm init`, pass
22
- extra initializer flags after `--`, for example
23
- `npm init screenci@latest -- --yes --package-manager pnpm`.
24
-
25
- ```text
26
- screenci/
27
- screenci.config.ts
28
- package.json
29
- package-lock.json # or pnpm-lock.yaml / yarn.lock
30
- .gitignore
31
- videos/
32
- example.video.ts
33
- .github/workflows/screenci.yaml # at the repo root, scoped to screenci/
34
- ```
20
+ This scaffolds a self-contained `screenci/` directory with its own
21
+ dependencies and installs Chromium. The directory is isolated from the
22
+ surrounding workspace, which keeps installation reliable inside monorepos.
23
+
24
+ Then write a video, run it locally, and record the final output:
35
25
 
36
- `screenci test` and `screenci record` work both from inside `screenci/` and
37
- from the repository root.
26
+ ```bash
27
+ npx screenci test # author the video
28
+ npx screenci record # render and upload the final video
29
+ ```
38
30
 
39
- Docs:
31
+ Full docs:
40
32
 
41
- - Getting started: `https://screenci.com/docs`
42
- - Writing scripts: `https://screenci.com/docs/write-video-scripts`
43
- - CLI reference: `https://screenci.com/docs/reference/cli`
33
+ - [Getting started](https://screenci.com/docs)
34
+ - [Writing scripts](https://screenci.com/docs/video-script-basics)
35
+ - [CLI reference](https://screenci.com/docs/reference/cli)
44
36
 
45
37
  ## Write a video
46
38
 
47
- Video scripts are Playwright-style files with a `.video.ts` extension. If you
39
+ Video scripts are Playwright-style files with a `.screenci.ts` extension. If you
48
40
  already know Playwright locators, navigation, and waiting, you already know
49
41
  most of the automation layer.
50
42
 
51
43
  ```ts
52
- // videos/onboarding.video.ts
44
+ // videos/onboarding.screenci.ts
53
45
  import { hide, speed, time, video } from 'screenci'
54
46
 
55
47
  video('Onboarding flow', async ({ page }) => {
@@ -70,101 +62,32 @@ video('Onboarding flow', async ({ page }) => {
70
62
  ```
71
63
 
72
64
  Each `video()` call becomes one output video. The title becomes the filename
73
- and the remote video identity.
74
-
75
- Inside `video()`, `page` is a `ScreenCIPage`: a Playwright `Page` with animated
76
- cursor movement and visible typing layered on top of normal Playwright
77
- behavior.
65
+ and the remote video identity. Inside `video()`, `page` is a `ScreenCIPage`: a
66
+ Playwright `Page` with animated cursor movement and visible typing layered on
67
+ top of normal Playwright behavior.
78
68
 
79
69
  `hide()` removes setup entirely. `speed()` and `time()` keep a section visible
80
- but remap its rendered duration. Narration cue audio keeps its original
81
- playback speed.
82
-
83
- ## Run locally
84
-
85
- ```bash
86
- npx screenci test
87
- npx screenci test --ui
88
- ```
89
-
90
- Use `test` for the normal authoring loop. It runs the video scripts through
91
- Playwright without starting the final recording and upload path.
92
-
93
- ## Record the final output
94
-
95
- ```bash
96
- npx screenci record
97
- ```
98
-
99
- On the first run without `SCREENCI_SECRET` in an interactive terminal, `record`
100
- prints a one-time ScreenCI link, waits for you to finish sign-in in the browser,
101
- saves the secret into the project env file, and then continues. In a
102
- non-interactive session (no terminal, or `SCREENCI_NONINTERACTIVE=1`) `record`
103
- does not wait: it prints the sign-in link and exits, so you can open the link,
104
- sign in, choose a plan, and rerun `record`, which then detects the completed
105
- session and continues. Set `SCREENCI_SECRET` ahead of time to skip sign-in
106
- entirely. Pending auth state is cached in `.screenci/link-session.json`, so
107
- rerunning `record` reuses the same link until it expires or completes. Recorded
108
- artifacts still live in `.screenci/<video-name>/`.
109
-
110
- ## Configure
111
-
112
- ```ts
113
- // screenci.config.ts
114
- import { defineConfig } from 'screenci'
115
-
116
- export default defineConfig({
117
- projectName: 'my-project',
118
- envFile: '.env',
119
- videoDir: './videos',
120
- use: {
121
- baseURL: 'https://app.example.com',
122
- recordOptions: {
123
- aspectRatio: '16:9',
124
- quality: '1080p',
125
- fps: 60,
126
- },
127
- trace: 'retain-on-failure',
128
- },
129
- projects: [{ name: 'chromium' }],
130
- })
131
- ```
132
-
133
- ScreenCI manages `testDir`, `testMatch`, and `retries` for you. Most other
134
- Playwright config still passes through.
135
-
136
- If you keep local runtime secrets in an env file, point `envFile` at it or use
137
- the project `.env`. ScreenCI loads that file automatically for CLI commands.
138
- That is also the right place for BYOK-style secrets such as
139
- `ELEVENLABS_API_KEY`. ScreenCI does not store raw API keys from your env file.
70
+ but remap its rendered duration.
140
71
 
141
72
  ## Authoring helpers
142
73
 
143
- | Export | What it does |
144
- | ----------------- | --------------------------------------------------------- |
145
- | `defineConfig` | Wraps Playwright config with ScreenCI defaults |
146
- | `video` | Declares a video recording test |
147
- | `createNarration` | Creates typed narration controllers |
148
- | `hide` | Cuts setup or cleanup out of the visible recording |
149
- | `autoZoom` | Smooth camera follow for an interaction block |
150
- | `zoomTo` | Manual camera framing for a locator or point |
151
- | `resetZoom` | Returns from manual framing to the full viewport |
152
- | `createOverlays` | Inserts timed media overlays into the recording timeline |
153
- | `voices` | Available voice constants such as `voices.Ava` |
154
- | `modelTypes` | Narration model constants such as `modelTypes.consistent` |
155
-
156
- ## Output
157
-
158
- ```text
159
- .screenci/
160
- <video-name>/
161
- recording.mp4
162
- data.json
163
- ```
164
-
165
- When `SCREENCI_SECRET` is configured, `screenci record` uploads the output to
166
- ScreenCI for rendering, narration generation, and hosted delivery.
167
-
168
- For narration authoring, keep cues short and usually one sentence at a time.
169
- That makes overlap timing easier to manage and should reduce TTS regeneration
170
- cost when only one line changes.
74
+ | Export | What it does |
75
+ | ----------------- | -------------------------------------------------------- |
76
+ | `defineConfig` | Wraps Playwright config with ScreenCI defaults |
77
+ | `video` | Declares a video recording test |
78
+ | `createNarration` | Creates typed narration controllers |
79
+ | `hide` | Cuts setup or cleanup out of the visible recording |
80
+ | `autoZoom` | Smooth camera follow for an interaction block |
81
+ | `zoomTo` | Manual camera framing for a locator or point |
82
+ | `resetZoom` | Returns from manual framing to the full viewport |
83
+ | `createOverlays` | Inserts timed media overlays into the recording timeline |
84
+ | `voices` | Available voice constants such as `voices.Ava` |
85
+ | `modelTypes` | Narration model constants |
86
+
87
+ See the [docs](https://screenci.com/docs) for configuration, narration,
88
+ camera, and CI setup.
89
+
90
+ ## Community
91
+
92
+ Questions, ideas, or want to show off your videos? Join us on
93
+ [Discord](https://discord.gg/DyjSRFzeBc).
package/dist/cli.d.ts CHANGED
@@ -69,6 +69,7 @@ export declare function extractConfigStringLiteral(configSource: string, propert
69
69
  export declare function extractRecordUploadPolicyLiteral(configSource: string): RecordUploadPolicy | undefined;
70
70
  export declare function extractMockRecordLiteral(configSource: string): boolean | undefined;
71
71
  export declare function getConfigModuleSpecifier(resolvedConfigPath: string): string;
72
+ export declare function extractGrep(args: string[]): string | undefined;
72
73
  export declare function ensureScreenciSecret(resolvedConfigPath?: string, opts?: {
73
74
  interactive?: boolean;
74
75
  pollAuth?: boolean;
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../cli.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../cli.ts"],"names":[],"mappings":"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"}
package/dist/cli.js CHANGED
@@ -342,7 +342,15 @@ async function uploadRecordingCandidate(candidate, screenciDir, projectName, api
342
342
  let plan = null;
343
343
  try {
344
344
  uploadAbort.throwIfAborted();
345
- const recordingPath = resolve(screenciDir, entry, 'recording.mp4');
345
+ // A screenshot recording uploads its raw page capture (always a PNG) through
346
+ // the same recording endpoint a video uses; the renderer reads those bytes as
347
+ // the capture. Videos upload recording.mp4. Output kind defaults to 'video'.
348
+ const isScreenshot = data.output === 'screenshot';
349
+ const recordingFileName = isScreenshot
350
+ ? (data.screenshot?.path ?? 'screenshot.png')
351
+ : 'recording.mp4';
352
+ const recordingContentType = isScreenshot ? 'image/png' : 'video/mp4';
353
+ const recordingPath = resolve(screenciDir, entry, recordingFileName);
346
354
  if (!existsSync(recordingPath)) {
347
355
  progressReporter.complete(progressIndex, 'failure');
348
356
  return {
@@ -350,7 +358,7 @@ async function uploadRecordingCandidate(candidate, screenciDir, projectName, api
350
358
  videoId: null,
351
359
  hadFailure: true,
352
360
  videoName,
353
- failureMessage: `Missing recording.mp4 for "${videoName}"`,
361
+ failureMessage: `Missing ${recordingFileName} for "${videoName}"`,
354
362
  recordId,
355
363
  };
356
364
  }
@@ -408,7 +416,7 @@ async function uploadRecordingCandidate(candidate, screenciDir, projectName, api
408
416
  uploadAbort.throwIfAborted();
409
417
  const fileStat = await stat(recordingPath);
410
418
  if (verbose) {
411
- logger.info(`Uploading recording.mp4 size=${(fileStat.size / 1024 / 1024).toFixed(1)}MB`);
419
+ logger.info(`Uploading ${recordingFileName} size=${(fileStat.size / 1024 / 1024).toFixed(1)}MB`);
412
420
  }
413
421
  const recordingResponse = await withUploadRetry(async () => {
414
422
  const stream = createReadStream(recordingPath);
@@ -420,7 +428,7 @@ async function uploadRecordingCandidate(candidate, screenciDir, projectName, api
420
428
  return await fetch(`${apiUrl}/cli/upload/${recordingId}/recording`, {
421
429
  method: 'PUT',
422
430
  headers: {
423
- 'Content-Type': 'video/mp4',
431
+ 'Content-Type': recordingContentType,
424
432
  'Content-Length': String(fileStat.size),
425
433
  'X-ScreenCI-Secret': secret,
426
434
  ...(elevenLabsApiKey
@@ -1417,6 +1425,49 @@ async function updateVideoVisibility(videoId, isPublic, configPath) {
1417
1425
  }
1418
1426
  logger.info(`${isPublic ? 'Made public' : 'Made private'}: ${videoId}`);
1419
1427
  }
1428
+ // Extract a `--grep <value>` / `--grep=<value>` from the pass-through args so a
1429
+ // remote trigger can forward it as a filter (records only matching videos).
1430
+ export function extractGrep(args) {
1431
+ for (let i = 0; i < args.length; i++) {
1432
+ const arg = args[i];
1433
+ if (arg === undefined)
1434
+ continue;
1435
+ if (arg === '--grep' || arg === '-g') {
1436
+ return args[i + 1];
1437
+ }
1438
+ if (arg.startsWith('--grep=')) {
1439
+ return arg.slice('--grep='.length);
1440
+ }
1441
+ }
1442
+ return undefined;
1443
+ }
1444
+ // `screenci record --remote` triggers the project's GitHub Actions recording
1445
+ // workflow instead of recording locally. The project is resolved from the
1446
+ // existing SCREENCI_SECRET + config `projectName`, exactly like the other
1447
+ // authenticated commands; the backend dispatches the workflow using the GitHub
1448
+ // token stored for the project. An optional `--grep` records only matching
1449
+ // videos/screenshots.
1450
+ async function triggerRemoteRun(configPath, grep) {
1451
+ const { screenciConfig, secret, apiUrl } = await requireScreenCISecret(configPath, { interactive: detectInteractiveSession() });
1452
+ const res = await fetch(`${apiUrl}/cli/trigger-run`, {
1453
+ method: 'POST',
1454
+ headers: {
1455
+ 'X-ScreenCI-Secret': secret,
1456
+ 'Content-Type': 'application/json',
1457
+ },
1458
+ body: JSON.stringify({
1459
+ projectName: screenciConfig.projectName,
1460
+ ...(grep ? { grep } : {}),
1461
+ }),
1462
+ });
1463
+ if (!res.ok) {
1464
+ const text = await res.text();
1465
+ throw new Error(`Failed to trigger remote run: ${res.status} ${text}${hint401(res.status, secret)}`);
1466
+ }
1467
+ logger.info(grep
1468
+ ? `Triggered the remote recording workflow for "${screenciConfig.projectName}" (filter: ${grep}).`
1469
+ : `Triggered the remote recording workflow for "${screenciConfig.projectName}".`);
1470
+ }
1420
1471
  function getLastRecordFilePath(screenciDir) {
1421
1472
  return resolve(screenciDir, SCREENCI_LAST_RECORD_FILE);
1422
1473
  }
@@ -1669,9 +1720,17 @@ export async function main() {
1669
1720
  .description('Record videos using Playwright')
1670
1721
  .option('-v, --verbose', 'verbose output')
1671
1722
  .option('--poll-auth', 'wait for sign-in to complete (polling every 5s, up to 5 minutes) instead of exiting, then continue recording')
1723
+ .option('--remote', 'trigger the GitHub Actions recording workflow for this project remotely instead of recording locally')
1672
1724
  .allowUnknownOption(true)
1673
1725
  .action(async () => {
1674
1726
  const parsed = parseRecordCliArgs(getSubcommandArgv('record'));
1727
+ // `--remote` is a pure dispatch: it fires the project's GitHub Actions
1728
+ // recording workflow and exits, so there is no local Playwright run. A
1729
+ // pass-through `--grep` becomes the remote recording filter.
1730
+ if (parsed.remote) {
1731
+ await triggerRemoteRun(parsed.configPath, extractGrep(parsed.otherArgs));
1732
+ return;
1733
+ }
1675
1734
  let playwrightFailure = null;
1676
1735
  try {
1677
1736
  await run('record', parsed.otherArgs, parsed.configPath, parsed.verbose, false, parsed.pollAuth);
@@ -1911,6 +1970,7 @@ function parseRecordCliArgs(args) {
1911
1970
  let configPath;
1912
1971
  let verbose = false;
1913
1972
  let pollAuth = false;
1973
+ let remote = false;
1914
1974
  const otherArgs = [];
1915
1975
  for (let i = 0; i < args.length; i++) {
1916
1976
  const arg = args[i];
@@ -1931,6 +1991,9 @@ function parseRecordCliArgs(args) {
1931
1991
  else if (arg === '--poll-auth') {
1932
1992
  pollAuth = true;
1933
1993
  }
1994
+ else if (arg === '--remote') {
1995
+ remote = true;
1996
+ }
1934
1997
  else {
1935
1998
  otherArgs.push(arg);
1936
1999
  }
@@ -1939,6 +2002,7 @@ function parseRecordCliArgs(args) {
1939
2002
  configPath,
1940
2003
  verbose,
1941
2004
  pollAuth,
2005
+ remote,
1942
2006
  otherArgs,
1943
2007
  };
1944
2008
  }