Files
Olli Paloviita 97f28572e1 cli: filter 'screenci edit' videos by positional title patterns
'screenci edit [patterns...]' filters managed videos by title, like
'playwright test <pattern>' (multiple positionals OR-combined). --grep
takes precedence when both are given.
2026-07-12 19:08:10 +03:00

738 lines
26 KiB
Plaintext

# CLI
import { Tabs, TabItem } from '@astrojs/starlight/components'
The `screenci` CLI keeps the workflow small: initialize a project, iterate
locally, record final output, and manage public delivery when needed. Run
commands from inside your `screenci/` project directory. Most commands resolve
`screenci.config.ts` from the current directory unless you pass
`--config <path>`.
## Command overview
| Command | Purpose |
| ---------------------------------- | ----------------------------------------------------------------------------- |
| `screenci init [name]` | Scaffold a ScreenCI project |
| `screenci test [playwrightArgs]` | Run `.screenci.ts` files locally without final recording |
| `screenci record [playwrightArgs]` | Record videos and upload results when configured |
| `screenci edit` | Connect this machine to the editor: unlocks web editing and on-demand records |
| `screenci info` | Print the last record run's URLs and render status |
| `screenci make-public <videoId>` | Enable public delivery for a video |
| `screenci make-private <videoId>` | Disable public delivery for a video |
| `screenci delete <videoId>` | Permanently delete a video and its renders |
## `screenci init`
Create a new ScreenCI project in the current directory:
<Tabs syncKey="package-manager">
<TabItem label="npm">
```bash
npm init screenci@latest
```
</TabItem>
<TabItem label="pnpm">
```bash
pnpm create screenci
```
</TabItem>
<TabItem label="yarn">
```bash
yarn create screenci
```
</TabItem>
</Tabs>
The project name defaults to the repository root directory name.
### Connecting to an existing account
No account or secret is needed to scaffold or record: without one, `record`
uploads under a local, anonymous trial session and prints a link to view the
result, then you sign up to keep it. Recording an anonymous trial agrees to the
[Terms](https://screenci.com/legal/tos), which `record` prints before it starts.
To connect the project to an account you already have, pass your
`SCREENCI_SECRET` (from your secrets page) as `init`'s positional argument, and
it writes the secret straight into `screenci/.env`:
```bash
npm init screenci@latest PASTE_YOUR_SCREENCI_SECRET_HERE
```
You can also skip this and copy `SCREENCI_SECRET` into `screenci/.env` by hand
at any point before recording.
When using `npm init`, pass extra initializer flags after `--`:
```bash
npm init screenci@latest -- --yes --package-manager pnpm
npm init screenci@latest -- --yes --package-manager yarn
```
The package manager is auto-detected from the `npm_config_user_agent` environment
variable (set automatically when you run `pnpm create` or `yarn create`), lockfile
presence (`pnpm-lock.yaml`, `yarn.lock`), or the `packageManager` field in
`package.json`. Use `--package-manager` to override.
To keep setup fast, `init` only prompts for the choices that genuinely vary:
the project name, whether to add the GitHub Actions workflow, and whether to
install AI agent skills (the ScreenCI skill plus `playwright-cli`). Everything
else is applied at a sensible default and can be steered with a flag.
Common options:
- `-y, --yes` accepts all defaults and skips every prompt
- `--package-manager <npm|pnpm|yarn>` overrides auto-detected package manager
- `--agent <name>` passes an agent name to the selected skills install command
- `-v, --verbose` prints underlying command output
Flags to override an auto-applied default without going interactive:
- `--no-github-workflow` skips the GitHub Actions workflow (and its prompt)
- `--no-skills` skips both AI agent skills (and their prompt)
- `--no-playwright-cli` keeps the ScreenCI skill but drops `playwright-cli`
(the skill and the `@playwright/cli` dev dependency)
- `--no-react` skips React overlay support (react/react-dom and JSX)
- `--no-playwright-browsers` skips installing the Chromium shell
- `--playwright-os-deps` installs Playwright operating system dependencies
(off by default because it may require sudo)
Applied defaults: create the GitHub Actions workflow, install dependencies,
add React overlay support, install the Chromium shell, skip OS dependency
installation, and install the AI agent skills (ScreenCI + `playwright-cli`).
Use this command in [Manual Setup & First Video](/docs/manual-setup).
## `screenci test [playwrightArgs...]`
Run videos locally without the final recording pipeline:
<Tabs syncKey="package-manager">
<TabItem label="npm">
```bash
npx screenci test
npx screenci test recordings/onboarding.screenci.ts
npx screenci test --grep "billing"
npx screenci test --ui
```
</TabItem>
<TabItem label="pnpm">
```bash
pnpm exec screenci test
pnpm exec screenci test recordings/onboarding.screenci.ts
pnpm exec screenci test --grep "billing"
pnpm exec screenci test --ui
```
</TabItem>
<TabItem label="yarn">
```bash
yarn screenci test
yarn screenci test recordings/onboarding.screenci.ts
yarn screenci test --grep "billing"
yarn screenci test --ui
```
</TabItem>
</Tabs>
Use this during normal authoring. Most trailing arguments are forwarded to
Playwright.
Plain `screenci test` disables recording-style waits so authoring runs stay
fast. That includes ScreenCI cursor/camera pauses and
`page.waitForTimeout(...)`, which is collapsed to `0ms`. Use Playwright locator
or load-state waits for real application readiness. Use `--mock-record` when you
need to preview the same pacing that `screenci record` will capture.
Common Playwright examples that also work here:
<Tabs syncKey="package-manager">
<TabItem label="npm">
```bash
npx screenci test --project=chromium
npx screenci test --grep "onboarding"
npx screenci test --ui
```
</TabItem>
<TabItem label="pnpm">
```bash
pnpm exec screenci test --project=chromium
pnpm exec screenci test --grep "onboarding"
pnpm exec screenci test --ui
```
</TabItem>
<TabItem label="yarn">
```bash
yarn screenci test --project=chromium
yarn screenci test --grep "onboarding"
yarn screenci test --ui
```
</TabItem>
</Tabs>
### `--mock-record`
<Tabs syncKey="package-manager">
<TabItem label="npm">
```bash
npx screenci test --mock-record
```
</TabItem>
<TabItem label="pnpm">
```bash
pnpm exec screenci test --mock-record
```
</TabItem>
<TabItem label="yarn">
```bash
yarn screenci test --mock-record
```
</TabItem>
</Tabs>
This keeps recording-like pacing enabled without starting the real recording
capture path. Use it when `test` passes but `record` exposes timing
differences.
If you want that behavior by default for a project, set
`test.mockRecord: true` in `screenci.config.ts`.
## `screenci record [playwrightArgs...]`
Record final output:
<Tabs syncKey="package-manager">
<TabItem label="npm">
```bash
npx screenci record
npx screenci record recordings/onboarding.screenci.ts
npx screenci record --grep "billing"
npx screenci record --project=chromium
```
</TabItem>
<TabItem label="pnpm">
```bash
pnpm exec screenci record
pnpm exec screenci record recordings/onboarding.screenci.ts
pnpm exec screenci record --grep "billing"
pnpm exec screenci record --project=chromium
```
</TabItem>
<TabItem label="yarn">
```bash
yarn screenci record
yarn screenci record recordings/onboarding.screenci.ts
yarn screenci record --grep "billing"
yarn screenci record --project=chromium
```
</TabItem>
</Tabs>
`record` forwards normal Playwright file filters and `--grep`, so you can limit
recording to only some videos just like with `screenci test`.
### `--languages <langs>`
Record (and render) only specific language versions of videos declared with
`video.languages([...])`. Pass a comma-separated list:
```bash
npx screenci record --languages fi
npx screenci record --languages fi,en
```
Per-language videos record only the requested languages, so a run never produces
more than you asked for. Videos that do not declare those languages are skipped.
A shared-mode recording (`{ mode: 'shared' }`) is a single capture and is not
split by this filter. See
[Languages](/docs/guides/languages) for the recording API.
### `--export`
Export a finished video from this recording. Without `--export`, `record`
refreshes the video's live preview in the Editor only, so you can iterate for
free and export when you are happy. Export minutes are spent only on export:
```bash
npx screenci record --export
```
`--publish` and `--render` are accepted as deprecated aliases.
### `--no-render`
Upload the recording and its editable timeline data without dispatching a
render. Useful for a fast first sync with the web editor: the timeline, the
action identities (`editId` slugs), and the editable parameters land in the
Editor in seconds, without waiting for (or spending) a render. Export later
with `screenci record --export`:
```bash
npx screenci record --no-render
```
Behavior:
- enables recording timing
- writes local output into `.screenci/`
- holds a per-project run lock at `.screenci/.record.lock` while recording and uploading. If another `screenci record` is already active, the second run exits non-zero instead of sharing the same `.screenci` output. Stale locks are reclaimed automatically when the pid is gone or the lock is older than the maximum run TTL
- needs no `SCREENCI_SECRET` to run: without one, it uploads under a local, anonymous trial session and prints a link to view the result. Recording an anonymous trial agrees to the [Terms](https://screenci.com/legal/tos), which `record` prints before it starts. To upload straight to your organization instead, set `SCREENCI_SECRET` (from the project `.env`, the environment, or by running `init` with it) before recording
- gives an anonymous trial exactly one recording. Once that first recording is used (or the trial expires), a second `screenci record` stops before it starts and prints a sign-up link instead of recording again, so no time is spent rendering a video that would only be refused. Sign up to keep recording; once you have, re-running `record` in the same folder links your account automatically and uploads normally. The trial previews expressive narration and up to 3 languages at once; see [Anonymous Trial](/docs/guides/anonymous-trial) for its full limits and what happens to that content after signup
- uploads only the recordings requested by this run (for example the videos matched by `--grep` or `--languages`), with or without `SCREENCI_SECRET`
- prints a results URL only when the requested recordings uploaded successfully. If a requested video is missing its `recording.mp4` or another upload failure occurs, `record` exits non-zero, does not save `.screenci/last-record.json`, and does not print a success URL for unrelated output
- prints any informational notices returned by the service in cyan after upload (occasional, non-error status messages such as maintenance or timing notes). These never affect the exit code
- new accounts start on the Free plan automatically (no plan selection step). Free exports include a ScreenCI watermark; paid plans remove it and raise export and active-video limits, and unlock multiple languages
Relevant options:
- `-c, --config <path>`
- `-v, --verbose`
- `--remote` (trigger the project's GitHub Actions recording workflow instead of recording locally, see below)
Important restriction:
- `--retries` is not supported because ScreenCI forces retries to `0`
### `--remote`
`screenci record --remote` does not record on your machine. Instead it asks
ScreenCI to dispatch the project's GitHub Actions recording workflow, so the
recording runs in CI on demand. The project is resolved the same way as every
other command, from `SCREENCI_SECRET` and the config `projectName`, and exits as
soon as the workflow has been triggered.
```bash
npx screenci record --remote
```
Pass `--grep` to record only matching videos remotely (the filter
is forwarded to the workflow's `grep` input):
```bash
npx screenci record --remote --grep "Onboarding"
```
This requires connecting the GitHub App to the project first (a one-time setup on
the project page in the app, see
[Trigger recordings remotely](/docs/ci-setup#trigger-recordings-remotely)).
Without a connected repository the command reports that GitHub is not connected.
Targeted (`--grep`) runs need the workflow to declare a `grep` input; projects
scaffolded by `screenci init` include it.
## `screenci edit`
Connects this machine to the ScreenCI editor. This is the live editing
channel: editing in the web editor is locked until a machine you own is
connected, because every web edit is written back into your `.screenci.ts`
sources by this command. While connected, the editor can also trigger a local
`screenci record` of one video and language on demand. The command keeps
running, polling the service for edit and record requests, until you stop it
with Ctrl-C. See [Editor](/docs/guides/editor) for the editing model.
```bash
screenci edit
```
Limit the session to specific videos by passing filter patterns, the same way
`playwright test <pattern>` does. Positional patterns match video titles
(multiple are OR-combined); `--grep` does the same and takes precedence when
both are given:
```bash
screenci edit "Auto-zoom"
```
Options:
- `-c, --config <path>`: path to the ScreenCI config file.
- `--token <token>`: personal editor token. Defaults to `SCREENCI_EDIT_TOKEN` from
your project env file.
- `-g, --grep <pattern>`: only manage videos whose title matches this pattern
(the same filter as Playwright's `--grep`, and the same as a positional
pattern above). The startup check and any
startup records are limited to the matching videos.
- `--force-record`: re-record every managed video at startup even when the
kept recordings are up to date.
- `--record-kill-window <seconds>`: while connected, a running record younger
than this is killed and replaced when a newer record request arrives; an
older one finishes first and the new request runs right after (only the
latest queued request is kept). Default: 10.
- `--no-watch`: disable the source-file watcher (see below).
- `-v, --verbose`: verbose output.
Startup check: `screenci edit` keeps each recording's `data.json` on disk after
upload, along with a hash of the test source file. On startup it compares that
hash with the current source and verifies every editable action carries an
`editId`. Up-to-date videos skip recording entirely; anything stale gets its
missing `editId` slugs stamped into the source and is re-recorded as a preview
(no render) before the session starts serving the editor.
Editor codegen: while connected, every edit made in the web editor is sent to
this machine as a codegen request and written directly into the
`.screenci.ts` source (the code is the single source of truth). Edits that
change recorded behavior additionally trigger an automatic preview record.
An edit the codegen cannot apply is reported back and the editor reverts it.
Besides timeline edits this covers the editor's option panels: render and
record option changes are merged into the video's `.renderOptions({...})` /
`.recordOptions({...})` builder calls (the call is added when the section does
not exist yet), and narration text edits are merged into the
`video.narration(...)` declaration. Editing a narration text in a non-default
language converts a flat declaration to the language-major form (the existing
values move under `default`). A names-only narration declaration
(`.narration(['intro'])`) stays app-managed: its content lives in the web app
and is never codegen'd.
Source watching: while connected, `screenci edit` watches the test source files
backing the managed videos, plus `screenci.config.ts`. Saving a real change to
a source file automatically re-records that file's videos as a preview (the
same freshness hash as the startup check filters out no-op saves and the
CLI's own codegen writes); changing the config re-records every managed video.
Watching follows the `-g/--grep` filter and can be turned off with
`--no-watch`.
Setup:
1. Create a personal editor token on the Secrets page in the app (one token per
machine, up to five per user).
2. Add it to your project env file as `SCREENCI_EDIT_TOKEN=<token>`, next to
your `SCREENCI_SECRET`.
3. Run `screenci edit` in your project. The editor header shows your machine as
connected (for example `you@laptop`) and offers "Record ... on laptop" in
the Export button's record menu.
While connected:
- Only your own account can trigger records on your machine. Teammates see
whose machine is connected but cannot use it.
- A triggered record runs exactly one video in one language (the one open in
the editor), then uploads as a normal record run: it refreshes the live
preview, and renders only when the trigger was an export. The editor's
record menu also offers "Record raw preview footage", which records without
rendering at all.
- Web edits are applied to your sources as you make them in the editor; the
editor shows a syncing state on your machine's chip while a change is being
written.
- The regular record run lock applies: if another `screenci record` is already
running, the request is reported back to the editor as failed.
- Stopping the command (Ctrl-C) disconnects the machine; the editor updates
within about fifteen seconds.
Editor tokens are separate from `SCREENCI_SECRET`: the secret authenticates your
project to the service, the editor token additionally proves which user and
machine is listening. Revoking the token on the Secrets page immediately
disconnects the machine.
## `screenci info`
<Tabs syncKey="package-manager">
<TabItem label="npm">
```bash
npx screenci info
```
</TabItem>
<TabItem label="pnpm">
```bash
pnpm exec screenci info
```
</TabItem>
<TabItem label="yarn">
```bash
yarn screenci info
```
</TabItem>
</Tabs>
Lists **every video in the project** with its public URLs as JSON, keyed by
video name and language code. It is where you get the remote `videoId` for
`make-public`, `make-private`, and `delete`.
When this machine has recorded a successful run, `info` also reports that run.
Each `screenci record` upload stores its unique record id in
`.screenci/last-record.json`; `info` then attaches, **only to the videos
produced by that run**, a per-language `latestRecord` with the run's render
status and its record-pinned URLs, plus a `latestRecordId` on the video. Videos
that were not part of the run are still listed, with `static` URLs only. This is
the command to use in CI to gate on rendering or grab links right after a record.
```json
{
"projectName": "My Product",
"videos": {
"Onboarding": {
"videoId": "kh74…",
"latestRecordId": "5f1c…",
"isPublic": true,
"languages": {
"en": {
"static": {
"video": "https://api.screenci.com/public/kh74…/en/video",
"thumbnail": "https://api.screenci.com/public/kh74…/en/thumbnail",
"subtitle": "https://api.screenci.com/public/kh74…/en/subtitle",
"screenshot": "https://api.screenci.com/public/kh74…/en/screenshot"
},
"download": {
"video": "https://api.screenci.com/cli/download/kh74…/en/video",
"thumbnail": "https://api.screenci.com/cli/download/kh74…/en/thumbnail",
"subtitle": "https://api.screenci.com/cli/download/kh74…/en/subtitle",
"screenshot": "https://api.screenci.com/cli/download/kh74…/en/screenshot"
},
"latestRecord": {
"status": "finished",
"video": "https://api.screenci.com/public/kh74…/records/5f1c…/en/video",
"thumbnail": "https://api.screenci.com/public/kh74…/records/5f1c…/en/thumbnail",
"subtitle": "https://api.screenci.com/public/kh74…/records/5f1c…/en/subtitle",
"screenshot": "https://api.screenci.com/public/kh74…/records/5f1c…/en/screenshot",
"download": {
"video": "https://api.screenci.com/cli/download/kh74…/records/5f1c…/en/video",
"thumbnail": "https://api.screenci.com/cli/download/kh74…/records/5f1c…/en/thumbnail",
"subtitle": "https://api.screenci.com/cli/download/kh74…/records/5f1c…/en/subtitle",
"screenshot": "https://api.screenci.com/cli/download/kh74…/records/5f1c…/en/screenshot"
}
}
}
}
}
}
}
```
Each language exposes up to three URL sets:
- `static`: stable, public URLs that always follow the currently selected
version
- `download`: authenticated download URLs for the selected version (see below)
- `latestRecord`: this run's render status plus public URLs pinned to it via a
`records/<recordId>` path segment (an immutable contract: the exact run, or
`404`, see
[resolution rules](/docs/reference/public-delivery-api#resolution-rules)). Once
finished, it also carries record-pinned `download` URLs.
Each URL set carries one entry per asset: `video`, `thumbnail`, `subtitle`, and
`screenshot`. A version is either a video or a screenshot, never both, so use the
`video` asset for video recordings and the `screenshot` asset for screenshot
recordings: the asset that does not match the version's type `404`s when fetched.
`static` is `null` for videos without public delivery enabled. `latestRecord`
carries the render status even for private videos, but its public URLs are then
`null`.
The `download` URLs are **private**: they require your `X-ScreenCI-Secret`
header, so they are not embeddable, and they work even for private videos. Use
them to archive a render permanently, before older versions are pruned (see
[Keep a render forever](/docs/reference/public-delivery-api#keep-a-render-forever)):
```bash
curl -H "X-ScreenCI-Secret: $SCREENCI_SECRET" \
"https://api.screenci.com/cli/download/kh74…/records/5f1c…/en/video" \
-o video.mp4
```
Render status (under `latestRecord.status`) is one of:
- `finished`: a render finished and is servable
- `rendering`: a render is still in progress (or has not started)
- `failed`: a render failed and none for that language is still in progress
Because rendering happens after upload, poll `info` until each `latestRecord`
reaches `finished` (or gate your pipeline on `failed`).
**Not recorded on this machine?** If there is no `.screenci/last-record.json`
(for example a fresh checkout that has never run `screenci record`), `info` does
not fail. It simply omits `latestRecordId` and the `latestRecord` fields and
prints the project-wide listing with `static` URLs only.
## `screenci make-public <videoId>`
<Tabs syncKey="package-manager">
<TabItem label="npm">
```bash
npx screenci make-public kh74…
```
</TabItem>
<TabItem label="pnpm">
```bash
pnpm exec screenci make-public kh74…
```
</TabItem>
<TabItem label="yarn">
```bash
yarn screenci make-public kh74…
```
</TabItem>
</Tabs>
Enables public delivery for a video. Get the ID from `screenci info`.
When you make a video public, ScreenCI starts it in the same mode as the app:
- public delivery is enabled for the video
- auto-select latest is enabled
- the latest finished render for each language becomes the active public output
That means `make-public` is the CLI equivalent of turning on **Enable public
URL** in the dashboard.
## `screenci make-private <videoId>`
<Tabs syncKey="package-manager">
<TabItem label="npm">
```bash
npx screenci make-private kh74…
```
</TabItem>
<TabItem label="pnpm">
```bash
pnpm exec screenci make-private kh74…
```
</TabItem>
<TabItem label="yarn">
```bash
yarn screenci make-private kh74…
```
</TabItem>
</Tabs>
Disables public delivery for a video.
This is the CLI equivalent of turning off **Enable public URL** in the
dashboard.
## `screenci delete <videoId>`
<Tabs syncKey="package-manager">
<TabItem label="npm">
```bash
npx screenci delete kh74…
```
</TabItem>
<TabItem label="pnpm">
```bash
pnpm exec screenci delete kh74…
```
</TabItem>
<TabItem label="yarn">
```bash
yarn screenci delete kh74…
```
</TabItem>
</Tabs>
Permanently deletes a video and all of its renders. Use the same `videoId` that
`make-public` and `make-private` accept (copy it from `screenci info`).
This is irreversible, so the command prints the video's name and asks you to
confirm before deleting. Pass `-y` (or `--yes`) to skip the prompt in CI or
scripts:
```bash
npx screenci delete kh74… --yes
```
You can only delete videos that belong to your own project's organization.
## What the CLI does not do
The CLI currently covers:
- project scaffolding with `init`
- project auth setup (handled automatically on first `record`)
- local iteration with `test`
- final capture and upload with `record`
- per-run URLs and render status with `info`
- public visibility changes with `make-public` and `make-private`
- permanent video deletion with `delete`
Manual version pinning is currently handled in the app UI:
- turn off **Auto-select latest version**
- open a language section
- choose the version to mark as **Selected**
## Shared environment and config behavior
These commands support `--config <path>`:
- `test`
- `record`
- `info`
- `make-public`
- `make-private`
- `delete`
`SCREENCI_SECRET` is used for:
- auth bootstrap and persistence
- uploads
- per-run URLs and render status (`info`)
- public delivery changes
- video deletion (`delete`)
If `envFile` is configured in `screenci.config.ts`, the CLI loads it
automatically. Otherwise it falls back to the project `.env`. The CLI resolves
`envFile` by evaluating the config the same way Playwright does, so a dynamic
value like `envFile: isLocal ? '.env.local' : '.env'` picks the right file at
run time.
That env file is the recommended place to keep `SCREENCI_SECRET` and other
runtime variables your setup needs. Your ElevenLabs key is not kept here: add it
on the Secrets page in the app instead (see
[Narration](/docs/guides/narration#elevenlabs-voices)).
## Related pages
- [Configuration](/docs/reference/configuration) for `screenci.config.ts`.