Files
Olli Paloviita 4cbdbc47f3 Replace start with setup, drop merge-complete and the source modes
One source model: the scripts live wherever the agent ran, ScreenCI keeps a
snapshot uploaded with every preview and export (opt out with
uploadSources: false). `screenci setup <code>` uses an existing workspace as
is (--force replaces it with the snapshot), pulls the snapshot into an
absent one, or scaffolds a new project. The repository clone under
.screenci/repo is read-only context; the workspace never lives in it.

Removed: `screenci merge-complete`, .screenci/pending-merge.json, the merge
setup-code kind, sourceMode/sourcesUnmerged in the exchange,
SCREENCI_UPLOAD_SOURCES, stripIslandProjectId, and the clone-workspace and
merge-prepared outcomes. No `start` alias: breaking change on purpose.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-18 10:37:55 +03:00

996 lines
38 KiB
Plaintext

# CLI
import { Tabs, TabItem } from '@astrojs/starlight/components'
The `screenci` CLI keeps the workflow small: initialize a project, iterate
locally with `screenci test`, refine videos with `screenci preview` and the web
editor, export the finished videos with `screenci export`, 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 setup <code>` | Set up a workspace from a setup code created in the web app |
| `screenci context` | Print the organisation's AI context for this project |
| `screenci login [url]` | Sign in to your own app once and reuse that session in every recording |
| `screenci logout` | Forget the signed-in session saved on this machine |
| `screenci init [name]` | Scaffold a ScreenCI project |
| `screenci ci-workflow` | Write the GitHub Actions workflow that records the workspace from CI |
| `screenci test [playwrightArgs]` | Run `.screenci.ts` files locally without final recording |
| `screenci preview [pattern]` | Record live previews and print their links |
| `screenci export [patterns...]` | Produce finished videos: re-record every requested video, render, and download |
| `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 setup <code>`
The entry point of the [web-app flow](/docs/make-videos). The
**Add project**, **Add video**, **Add screenshot**, **Edit**, **Add a
language**, **Re-record** / **Record all**, and **Add to CI** buttons in the
app produce a prompt that carries a one-time setup code and points the agent
at the brief for that button (`screenci.com/add-project.md`,
`/add-video.md`, `/add-screenshot.md`, `/edit-video.md`, `/add-language.md`,
`/record-video.md`, or `/add-to-ci.md`); the coding agent runs the command in
the repository of the app to record, or in an empty folder:
```bash
npx screenci@latest setup SC-7K3Q-M9XA
```
It runs without a config file (there may be no workspace yet) and does the
following:
1. Exchanges the code for a project-scoped `SCREENCI_SECRET` and writes it
into the workspace's env file. The secret only addresses that project. A
code belongs to the first machine that exchanges it (rerunning there
resumes it until a run lands; other machines are refused) and expires 24
hours after it was created.
2. Reads the organisation's [AI context](/docs/guides/ai-context) (with the
project's overrides) and locates the repository: the current checkout when
its `origin` is the configured repository or when it already holds a
`screenci/` workspace named after the project (a fork, or a repository
whose URL nobody told ScreenCI), else a shallow clone in `.screenci/repo`
(refreshed when it exists). The clone is read-only context for the agent
(routes, components, starting the app); the workspace never lives in it. A
failed clone is reported and the command continues when the site answers.
3. Prepares the workspace: `screenci/` inside the repository when the command
runs inside it and one exists there, else `./screenci`. The local
workspace is the source of truth; ScreenCI only holds a snapshot of it
(uploaded with every `preview` and `export`, see
[Source sync](#source-sync)).
- **Absent, new project:** scaffolds the workspace like `init --yes`
(agent skills on, no GitHub workflow) and writes `projectId` into
`screenci.config.ts`.
- **Absent, existing project with a snapshot:** pulls the project's
latest uploaded sources, installs dependencies and the Playwright
browser, and installs the agent skills at the repository root. Binary
media is not pulled; recordings reuse the project's uploaded assets.
- **Absent, existing project without a snapshot:** scaffolds a fresh
workspace for it. A code that addresses one video (Edit, Add a
language, Re-record of one video) refuses instead: its script is only
in the repository, so run the command there or pass `--dir`.
- **Present, same `projectId`, or no `projectId` and the same project
name (a workspace made by `screenci init`):** used as is. Nothing is
pulled; `--force` replaces it with the snapshot instead.
- **Present, another project:** refuses; use `--dir`.
- **Record code:** nothing is changed; the brief names the script (for
one video) and says whether a pipeline already records the project, in
which case the agent triggers it instead of recording locally.
- **CI code:** mints a CI key (no person behind it, so its uploads show as
"CI"), needs the repository (the one the command runs in, or the clone),
uses its `screenci/` workspace (pulling the snapshot into it when the
repository has none yet, for the agent to commit together with the
pipeline), lists the CI providers whose files the repository has
(`.github/workflows`, `.gitlab-ci.yml`, `.circleci/config.yml`,
`.buildkite`, `bitbucket-pipelines.yml`, `Jenkinsfile`,
`azure-pipelines.yml`), and prints a brief that stores the key in the
provider's secret store, adds the pipeline
([`ci-workflow`](#screenci-ci-workflow) for GitHub Actions, the
[CI setup templates](/docs/ci-setup#other-providers) elsewhere), and
triggers the first run. The site is not probed.
4. Looks for a signed-in session on this machine
(`.screenci/auth/default.json`, saved by [`login`](#screenci-login)) and
reports it in the brief. Nothing about your own product's credentials comes
from, or goes to, ScreenCI.
5. Probes the site (the prompt's app URL, else the context's site URL). When
nothing answers and the agent may not start the app, the brief says
**STOP** and the command exits with code 2; see
[Running the app locally](/docs/guides/ai-context#running-the-app-locally).
6. Prints a brief for the agent (the task, the repository, the site, how to
sign in, the team's notes, which script to edit, the commands to run)
followed by one machine-readable JSON line (`{"status":"ready", ...}`, or
`"stopped"` with a `stop` object).
Options:
- `--name <projectName>` names a new project. Precedence: this flag, then the
name typed in the web dialog, then the current folder name.
- `--dir <path>` uses another workspace folder than `./screenci`.
- `--force` replaces an existing workspace of this project with the snapshot
ScreenCI holds (local files that differ are overwritten and listed).
- `--package-manager <npm|pnpm|yarn>` overrides the auto-detected manager.
- `--agent <name>` targets a specific coding agent for the skills install.
- `--skip-site-check` records even when the site does not answer.
- `--no-clone` never clones the repository when running outside it.
- `-v, --verbose` prints underlying command output.
After `setup`, the usual commands run from inside the workspace: `test`,
`preview`, `export`. They upload the workspace's text sources (see
[Source sync](#source-sync)) and report the finished run, which is what makes
the originating browser tab open the result. When the workspace lives in a
repository, the agent commits its change on a branch; that is the whole of
"keeping the videos in the repository".
## `screenci context`
Prints the organisation's [AI context](/docs/guides/ai-context) as resolved
for the workspace's project (repository URL, site URL, whether the agent may
start the app, whether the site needs a sign-in, the team's notes), whether a
signed-in session is saved on this machine, the
[branding](/docs/guides/branding) new videos start from, and one
JSON line (with a `branding` key). When the branding voice is cloned from a
sample, the sample is saved as `branding/<file>` in the workspace and the JSON
line names it as `brandingSamplePath`. The
[shared branding assets](/docs/guides/branding#shared-assets) are listed with
their kind and guide, saved as `branding/<name>`, and named in the JSON line as
`brandingAssetPaths`. Runs inside a workspace; uses `SCREENCI_SECRET`
from the env file. The session line is read from disk,
not from the service.
```bash
npx screenci context
npx screenci context --json # the JSON line only
```
## `screenci login`
Captures the signed-in session of **your own app**, so recordings start signed
in and your video scripts contain no sign-in steps. It opens a real browser you
sign in to yourself, which is why two-factor, single sign-on, passkeys, and
magic links all work. See [Signing In](/docs/guides/signing-in) for the whole
picture.
Run it from the `screenci/` workspace. It needs no ScreenCI account and no
secret: an address is enough.
```bash
npx screenci login https://app.example.com # opens a browser, returns at once
npx screenci login # uses use.baseURL from the config
npx screenci login --wait # blocks until you finish signing in
npx screenci login --done # finish it now, from the terminal
npx screenci login --status # what is saved, and until when
npx screenci login --cancel # close it, save nothing
```
The command returns immediately so whoever started it can tell you to sign in.
Finish by clicking **I'm signed in** on the small ScreenCI card floating over
the page (drag it if it is in the way); closing the window finishes it too.
An agent then runs `--wait`, which blocks until you do, so your click actually
reaches it. Without that the agent has ended its turn and the click tells it
nothing. `--wait` gives up after eight minutes and says the sign-in is still
going, so it fits inside an agent's command timeout and can simply be run
again. `--done` is the other direction: it finishes the sign-in right now, for
when you say you are done rather than clicking.
Options:
- `--profile <name>` names the session, for a video that needs a second role.
Pick it for a run with `SCREENCI_AUTH_PROFILE=<name>`.
- `--timeout <minutes>` closes the browser by itself after this long
(default 30).
- `-c, --config <path>` points at another `screenci.config.ts`.
The session is written to `.screenci/auth/<profile>.json`, owner-readable only
and gitignored, and `screenci.config.ts` picks it up automatically. It never
leaves the machine: ScreenCI stores no credential and no session for your app.
Nothing prints its contents, `--status` included.
On a machine with no display (a server, a container, CI) the command refuses
and points at [CI Setup](/docs/ci-setup), which is where an unattended session
comes from.
## `screenci logout`
Forgets the saved session on this machine.
```bash
npx screenci logout
npx screenci logout --profile admin
```
## `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, record, or preview: without one,
`preview` uploads under a local, anonymous trial session and prints the web
preview link, then you sign up to keep it. Recording an anonymous trial agrees
to the [Terms](https://screenci.com/legal/tos), which the CLI prints before it
starts. Exporting requires an account with an active paid subscription.
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 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. `init` adds no CI pipeline: recording from CI is
a separate step, **Add to CI** in the web app or
[`ci-workflow`](#screenci-ci-workflow).
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:
- `--github-workflow` also writes the GitHub Actions workflow
(`.github/workflows/screenci.yaml`), the same file `ci-workflow` writes
- `--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: install dependencies, add React overlay support, install
the Chromium shell, skip OS dependency installation, install the AI agent
skills (ScreenCI + `playwright-cli`), and write no CI workflow.
The AI skills install is best effort: if it fails (for example on an older
Node.js runtime), `init` warns, prints the command to retry it later, and
continues with the rest of the setup.
Use this command in [Manual Setup & First Video](/docs/manual-setup).
## `screenci ci-workflow`
Writes the GitHub Actions workflow that records the repository's `screenci/`
workspace from CI, at `.github/workflows/screenci.yaml` in the repository
root. Run it from the repository root (or inside the workspace, or with
`--config <path>`):
```bash
npx screenci ci-workflow
```
The workflow is keyed to the package manager the workspace uses (from its
lockfile; `--package-manager <npm|pnpm|yarn>` overrides it). An existing
file is never overwritten unless `--force` is given. The **Add to CI** brief
runs this for GitHub repositories; other providers follow the templates in
[CI Setup](/docs/ci-setup#other-providers). The secret still has to be
stored in the repository's secrets: see [Required secret](/docs/ci-setup#required-secret).
## `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 a real recording 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 a real recording exposes timing
differences.
If you want that behavior by default for a project, set
`test.mockRecord: true` in `screenci.config.ts`.
## `screenci export [patterns...]`
Produce finished videos and download them. `export` is the one-shot command
(also the one CI runs): it pulls any queued editor edits into the sources,
re-records every requested video (sources can carry changes a hash check would
miss, so export always captures fresh footage), waits for the renders to
finish, and downloads the outputs into `./exports/`:
<Tabs syncKey="package-manager">
<TabItem label="npm">
```bash
npx screenci export
npx screenci export "Onboarding"
npx screenci export --grep "billing"
npx screenci export --languages fi,en
```
</TabItem>
<TabItem label="pnpm">
```bash
pnpm exec screenci export
pnpm exec screenci export "Onboarding"
pnpm exec screenci export --grep "billing"
pnpm exec screenci export --languages fi,en
```
</TabItem>
<TabItem label="yarn">
```bash
yarn screenci export
yarn screenci export "Onboarding"
yarn screenci export --grep "billing"
yarn screenci export --languages fi,en
```
</TabItem>
</Tabs>
Positional arguments are title patterns (multiple are OR-combined, like
`playwright test <pattern>`); with no patterns, every video is exported in
every language. Downloads are named `<title>.<lang>.mp4` (screenshots use
`.png`). The workspace's sources are uploaded first (see
[Source sync](#source-sync)).
The command polls render status every 5 seconds for up to 30 minutes
and exits `0` only when every requested video rendered and downloaded. After a
run it prints the video's page in the app: a run with exactly one rendered
pass links that video's overview page with the run preselected
(`<app>/project/<projectId>/video/<videoId>?export=<recordId>`); several
passes (several videos, or one video exported in several languages) link the
run's page (`<app>/export/<recordId>`), which links each version.
### `--languages <langs>`
Export only specific language versions of videos declared with
`video.languages([...])`. Pass a comma-separated list:
```bash
npx screenci export --languages fi
npx screenci export --languages fi,en
```
Per-language videos record and render 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.
### `-o, --output <dir>`
Directory for the downloaded files. Defaults to `exports`:
```bash
npx screenci export -o dist/videos
```
### `--no-wait`
Start the renders and exit immediately, without waiting for them or
downloading files. Use this when the finished videos are consumed from the
web (public URLs or embeds) and the run only needs to publish fresh footage:
```bash
npx screenci export --no-wait
```
The command still records, uploads, and dispatches the renders, and prints
the results URL. It exits `0` as soon as the upload succeeds; render failures
are visible on the results page, not in the exit code. Without this flag,
`export` waits for every render and exits non-zero if any of them fails.
### `--share`
Instead of downloading files, share each finished version with a permanent,
version-pinned public URL and print the URLs:
```bash
npx screenci export --share
```
The command records, renders, and waits like a normal export, then shares the
run's finished versions and prints one public URL per video and language
(plus thumbnail and subtitle URLs when they exist). A shared version keeps
serving that exact render until it is unshared or deleted from the app: it is
exempt from version retention, and the URL works even when the video's own
public URL is switched off. See
[version-pinned URLs](/docs/reference/public-delivery-api#version-pinned-urls-shared-versions)
for the URL shape and behavior.
Shared versions count against your organization's shared-version limit
(1000 by default, shown on the billing page). When the limit is reached the
remaining versions are not shared and the command exits non-zero with the
refusal message.
`--share` needs finished renders, so combining it with `--no-wait` is an
error. It exits `0` only when every requested render finished and was shared.
### `--select`
Make each finished render the served version of its language:
```bash
npx screenci export --select
```
Without `--select`, an export never changes which version a video serves: the
public URL (and any video that embeds this one as a dependency) keeps pointing
at the version selected in the app until someone selects a newer one. With
`--select`, every render this run produces becomes the selected version of its
language as soon as it finishes, so a CI export updates the public URL on its
own. Selecting also re-renders dependent videos. Nothing is selected for
renders that fail.
The flag is never implied, in CI or elsewhere: pass it explicitly in the
workflow that should publish. The workflow `screenci init` generates shows it
on the commented export line.
### `--pr <url>`
Record for a GitHub pull request:
```bash
npx screenci export --no-wait --pr "https://github.com/acme/app/pull/42"
```
The run records and renders as usual, and the service groups the versions
under the pull request: it posts a check run and one comment with a thumbnail
and a watch link per video and language, and updates both as renders finish.
A run whose Playwright recording failed fails the check. Nothing is selected;
approving the previews in the app and merging the pull request makes exactly
those versions the served ones. `--pr` cannot combine with `--select` or
`--remote`. The generated workflow passes
`github.event.pull_request.html_url` on its pull request trigger. See
[Pull request previews](/docs/pr-previews).
### `--force`
Deprecated no-op, kept so existing scripts do not break: `export` always
re-records every requested video.
Behavior:
- enables recording timing for every requested video
- writes local output into `.screenci/`
- holds a per-project run lock at `.screenci/.record.lock` while recording and uploading. If another `screenci preview` or `screenci export` run 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
- requires an account with an active paid subscription: without a `SCREENCI_SECRET`, `export` refuses before recording and prints a sign-up link (the anonymous trial is preview-only; use `screenci preview` for the free live preview, see [Anonymous Trial](/docs/guides/anonymous-trial)). Once you have signed up, re-running `export` in the same folder links your account automatically. Set `SCREENCI_SECRET` from the project `.env`, the environment, or by running `init` with it
- uploads only the recordings requested by this run (for example the videos matched by patterns, `--grep`, or `--languages`), with or without `SCREENCI_SECRET`
- exits non-zero unless every requested video rendered and downloaded. If a requested video is missing its `recording.mp4` or another failure occurs, `export` 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 choose a plan before exporting: exports need an active Starter, Pro, or Business subscription. Higher tiers raise export and active-video limits and unlock multiple languages
Relevant options:
- `-c, --config <path>`
- `-v, --verbose`
- `-g, --grep <pattern>` (same filter as Playwright's `--grep`)
Important restriction:
- `--retries` is not supported because ScreenCI forces retries to `0`
## `screenci preview [pattern]`
Records live previews and prints their links: it re-records and uploads the
live preview of every matched video (all declared videos when no pattern is
given), prints the link, and exits. A preview always records fresh footage;
there is no freshness skip. A run with exactly one
recording pass prints that video's overview page
(`<app>/project/<projectId>/video/<videoId>`); several passes (several
videos, or one video recorded in several languages) print the run listing
page (`<app>/preview/<recordId>`), which links each video and language.
```bash
screenci preview
```
Filter videos by passing a title pattern, the same way
`playwright test <pattern>` does; `--grep` does the same and takes precedence
when both are given.
```bash
screenci preview "Auto-zoom"
```
Right before recording a preview, the CLI sends a best-effort "preview
recording started" notice to the backend, so an open web preview page shows a
live "Recording preview..." indicator and refreshes automatically when the
new preview lands.
### Source sync
`preview` and `export` upload the island's text sources right before
recording (unless `screenci.config.ts` sets `uploadSources: false`), and
report the finished run afterwards. That snapshot is what the video page
shows under "Sources" and what `screenci setup` pulls onto a machine without a
workspace; the local workspace stays the source of truth.
- The bundle holds `screenci.config.ts`, `package.json`, `tsconfig.json`,
`.prettierrc`, `.gitignore`, `README.md`, the package-manager workspace
files, and every text file under `recordings/`. Env files, lockfiles,
`node_modules`, `.screenci`, `exports`, and binary media are never included.
Files over 256 KB and anything past 2 MB in total are skipped with a warning.
- The bundle is content addressed: an unchanged island is not re-uploaded.
Its canonical form is `{ "files": [{ "path", "content" }] }` sorted by path
(plain code-unit order), hashed with SHA-256 over the JSON text.
- Both steps are best effort. A failed upload warns and the recording still
runs (the next run retries); the run report never fails a command.
- Anonymous previews (no `SCREENCI_SECRET`) never sync sources.
- A project-scoped secret that pins a different project than the island's
`projectId` (an env file copied from another workspace) stops the run
before recording, with the pinned project's name in the message.
A run prints one line per phase: the record announcement, the upload results,
one line per video whose editor-uploaded media (overlays, audio, narration
audio, cloned voices) shapes the render, and the video link:
```
Recording: Search for Alfa Forni Moderno 2
...
✔ Uploaded "Search for Alfa Forni Moderno 2 [en]"
✔ Uploaded "Search for Alfa Forni Moderno 2 [et]"
Editor-uploaded media for "Search for Alfa Forni Moderno 2" applies at render time; recordings always run from code.
Open the live preview for "Search for Alfa Forni Moderno 2" at:
<app>/project/<projectId>/video/<videoId>
```
Options:
- `-c, --config <path>`: path to the ScreenCI config 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 record pass is limited to the matching videos.
- `-v, --verbose`: verbose output.
Setup (with an account): none beyond `SCREENCI_SECRET`. That secret is the
only credential ScreenCI needs; there is nothing else to paste.
Without an account, no setup is needed either: `screenci preview` runs under a
local, anonymous trial session (see
[Anonymous Trial](/docs/guides/anonymous-trial)). Signing up claims the trial,
and the next `screenci` command picks up your new `SCREENCI_SECRET` (written
into `screenci/.env`).
## `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 export` 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 an
export.
```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 export`), `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
- the latest finished render for each language becomes the active public output
(later exports change it only with `export --select` or a selection in the app)
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:
- workspace setup from a web-app setup code with `setup`
- reading the organisation's AI context with `context`
- signing in to your own app with `login` and `logout`
- project scaffolding with `init`
- project auth setup (handled automatically on first `export`)
- local iteration with `test`
- live previews and editing sessions with `preview`
- finished videos with `export`
- per-run URLs and render status with `info`
- public visibility changes with `make-public` and `make-private`
- permanent video deletion with `delete`
Version selection happens in two places:
- `screenci export --select` selects each render this run produces as it
finishes
- in the app, open a language section and choose the version to mark as
**Selected**
A selected version that is served at a public URL cannot be deleted until
another version is selected or the public URL is switched off.
## Shared environment and config behavior
These commands support `--config <path>`:
- `context`
- `login`
- `logout`
- `test`
- `preview`
- `export`
- `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 Branding page in the app instead (see
[Narration](/docs/guides/narration#elevenlabs-voices)).
## Related pages
- [Configuration](/docs/reference/configuration) for `screenci.config.ts`.