screenci 0.0.23 → 0.0.25
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.
- package/dist/Dockerfile +10 -2
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +247 -62
- package/dist/cli.js.map +1 -1
- package/dist/src/changeFocus.d.ts.map +1 -1
- package/dist/src/changeFocus.js +15 -3
- package/dist/src/changeFocus.js.map +1 -1
- package/dist/src/cue.d.ts +8 -6
- package/dist/src/cue.d.ts.map +1 -1
- package/dist/src/cue.js +11 -0
- package/dist/src/cue.js.map +1 -1
- package/dist/src/defaults.d.ts +4 -0
- package/dist/src/defaults.d.ts.map +1 -1
- package/dist/src/defaults.js +4 -0
- package/dist/src/defaults.js.map +1 -1
- package/dist/src/events.d.ts +6 -6
- package/dist/src/events.d.ts.map +1 -1
- package/dist/src/hide.js +1 -1
- package/dist/src/hide.js.map +1 -1
- package/dist/src/instrument.d.ts.map +1 -1
- package/dist/src/instrument.js +26 -9
- package/dist/src/instrument.js.map +1 -1
- package/dist/src/mouse.d.ts +1 -0
- package/dist/src/mouse.d.ts.map +1 -1
- package/dist/src/mouse.js +15 -6
- package/dist/src/mouse.js.map +1 -1
- package/dist/src/video.d.ts.map +1 -1
- package/dist/src/video.js +3 -3
- package/dist/src/video.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/skills/screenci/SKILL.md +10 -6
package/dist/Dockerfile
CHANGED
|
@@ -10,15 +10,23 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
|
10
10
|
&& rm -rf /var/lib/apt/lists/*
|
|
11
11
|
|
|
12
12
|
# ── Dependency layer (cached until package.json changes) ──────────────────────
|
|
13
|
-
# Install
|
|
13
|
+
# Install through a workspace to keep dependency resolution unchanged, then
|
|
14
|
+
# replace the workspace symlink with a real package under node_modules. Playwright
|
|
15
|
+
# skips transforms for node_modules packages, but follows workspace symlinks to
|
|
16
|
+
# /app/screenci and can rewrite compiled ESM as CJS while loading TS configs.
|
|
14
17
|
COPY package.json ./screenci/
|
|
15
|
-
RUN printf '{"private":true,"workspaces":["screenci"]}' > package.json &&
|
|
18
|
+
RUN printf '{"private":true,"workspaces":["screenci"]}' > package.json && \
|
|
19
|
+
npm install && \
|
|
20
|
+
rm /app/node_modules/screenci && \
|
|
21
|
+
mkdir -p /app/node_modules/screenci
|
|
16
22
|
|
|
17
23
|
# Playwright browser download: only re-runs when the playwright version changes.
|
|
18
24
|
RUN npx playwright install chromium --with-deps
|
|
19
25
|
|
|
20
26
|
# ── screenci build output ─────────────────────────────────────────────────────
|
|
21
27
|
COPY dist ./screenci/dist/
|
|
28
|
+
COPY package.json ./node_modules/screenci/package.json
|
|
29
|
+
COPY dist ./node_modules/screenci/dist/
|
|
22
30
|
|
|
23
31
|
# Explicit bin wrapper — no npm bin-linking magic needed.
|
|
24
32
|
RUN printf '#!/bin/sh\nexec node /app/screenci/dist/cli.js "$@"\n' > /app/node_modules/.bin/screenci && \
|
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../cli.ts"],"names":[],"mappings":";AAwBA,OAAO,KAAK,EACV,uBAAuB,EACvB,aAAa,EAEd,MAAM,iBAAiB,CAAA;AACxB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AA4D/C,KAAK,mBAAmB,GAAG;IACzB,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,CAAA;AAiBD,wBAAgB,8BAA8B,CAC5C,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,IAAI,GAAG,KAAK,GAAG,OAAO,CAAC,EACtD,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,KAAK,IAAI,GACxC,MAAM,IAAI,CAiBZ;AAuED,QAAA,MAAM,mCAAmC;;;CAG/B,CAAA;AAEV,KAAK,oBAAoB,GAAG,MAAM,OAAO,mCAAmC,CAAA;AAmc5E,wBAAgB,cAAc,CAC5B,KAAK,EAAE,QAAQ,GAAG,uBAAuB,GACxC,QAAQ,GAAG,uBAAuB,CAKpC;AAED,wBAAgB,oCAAoC,CAClD,IAAI,EAAE,aAAa,EACnB,MAAM,EAAE,mBAAmB,EAAE,GAC5B,aAAa,CAqEf;AAQD,wBAAgB,+BAA+B,CAC7C,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,MAAM,GACb,MAAM,
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../cli.ts"],"names":[],"mappings":";AAwBA,OAAO,KAAK,EACV,uBAAuB,EACvB,aAAa,EAEd,MAAM,iBAAiB,CAAA;AACxB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AA4D/C,KAAK,mBAAmB,GAAG;IACzB,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,CAAA;AAiBD,wBAAgB,8BAA8B,CAC5C,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,IAAI,GAAG,KAAK,GAAG,OAAO,CAAC,EACtD,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,KAAK,IAAI,GACxC,MAAM,IAAI,CAiBZ;AAuED,QAAA,MAAM,mCAAmC;;;CAG/B,CAAA;AAEV,KAAK,oBAAoB,GAAG,MAAM,OAAO,mCAAmC,CAAA;AAmc5E,wBAAgB,cAAc,CAC5B,KAAK,EAAE,QAAQ,GAAG,uBAAuB,GACxC,QAAQ,GAAG,uBAAuB,CAKpC;AAED,wBAAgB,oCAAoC,CAClD,IAAI,EAAE,aAAa,EACnB,MAAM,EAAE,mBAAmB,EAAE,GAC5B,aAAa,CAqEf;AAQD,wBAAgB,+BAA+B,CAC7C,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,MAAM,GACb,MAAM,CAeR;AAED,wBAAgB,8BAA8B,CAC5C,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,MAAM,GACb,IAAI,CAcN;AAgRD,wBAAgB,gBAAgB,IAAI,MAAM,CAKzC;AAED,wBAAgB,iBAAiB,IAAI,MAAM,CAK1C;AA0FD,wBAAgB,0BAA0B,CACxC,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,aAAa,GAAG,SAAS,GAClC,MAAM,GAAG,SAAS,CAepB;AAuvBD,wBAAsB,IAAI,kBAoQzB;AAoJD,wBAAgB,sBAAsB,CACpC,aAAa,CAAC,EAAE,oBAAoB,GACnC,oBAAoB,CAoBtB"}
|
package/dist/cli.js
CHANGED
|
@@ -570,6 +570,15 @@ function hint401(status, secret) {
|
|
|
570
570
|
}
|
|
571
571
|
export function formatUploadStartFailureMessage(videoName, status, responseText, secret) {
|
|
572
572
|
if (responseText.trim().length > 0) {
|
|
573
|
+
try {
|
|
574
|
+
const parsed = JSON.parse(responseText);
|
|
575
|
+
if (typeof parsed.error === 'string' && parsed.error.trim().length > 0) {
|
|
576
|
+
return `${parsed.error}${hint401(status, secret)}`;
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
catch {
|
|
580
|
+
// fall back to raw response text
|
|
581
|
+
}
|
|
573
582
|
return responseText;
|
|
574
583
|
}
|
|
575
584
|
return `Failed to start upload for "${videoName}": ${status}${hint401(status, secret)}`;
|
|
@@ -973,11 +982,10 @@ export default defineConfig({
|
|
|
973
982
|
})
|
|
974
983
|
`;
|
|
975
984
|
}
|
|
976
|
-
function generatePackageJson(projectName, includePlaywrightCli = false) {
|
|
985
|
+
function generatePackageJson(projectName, includePlaywrightCli = false, screenciDependency = 'latest') {
|
|
977
986
|
const npmName = projectName.toLowerCase().replace(/[^a-z0-9-]/g, '-');
|
|
978
987
|
const devDependencies = {
|
|
979
988
|
'@types/node': '^25.0.0',
|
|
980
|
-
tsx: '^4.21.0',
|
|
981
989
|
};
|
|
982
990
|
if (includePlaywrightCli) {
|
|
983
991
|
devDependencies['@playwright/cli'] = 'latest';
|
|
@@ -993,7 +1001,7 @@ function generatePackageJson(projectName, includePlaywrightCli = false) {
|
|
|
993
1001
|
test: 'screenci test',
|
|
994
1002
|
},
|
|
995
1003
|
dependencies: {
|
|
996
|
-
screenci:
|
|
1004
|
+
screenci: screenciDependency,
|
|
997
1005
|
},
|
|
998
1006
|
devDependencies,
|
|
999
1007
|
}, null, 2) + '\n');
|
|
@@ -1011,13 +1019,17 @@ Learn more: https://screenci.com/docs/intro/
|
|
|
1011
1019
|
|
|
1012
1020
|
## Quick start
|
|
1013
1021
|
|
|
1014
|
-
1.
|
|
1022
|
+
1. Create your own videos in \`videos/*.video.ts\`, either manually or with an AI agent using your source code or a URL.
|
|
1015
1023
|
|
|
1016
|
-
|
|
1024
|
+
2. Run videos locally to test the script working:
|
|
1017
1025
|
|
|
1018
|
-
|
|
1026
|
+
\`npx screenci test\` or with UI mode: \`npx screenci test --ui\`
|
|
1027
|
+
|
|
1028
|
+
3. Record videos:
|
|
1019
1029
|
|
|
1020
1030
|
\`npx screenci record\`
|
|
1031
|
+
|
|
1032
|
+
4. View results on screenci.com and optionally enable a public URL to embed the video on your site.
|
|
1021
1033
|
`;
|
|
1022
1034
|
}
|
|
1023
1035
|
function generateDockerfile() {
|
|
@@ -1053,25 +1065,23 @@ jobs:
|
|
|
1053
1065
|
SCREENCI_SECRET: \${{ secrets.SCREENCI_SECRET }}
|
|
1054
1066
|
run: |
|
|
1055
1067
|
if [ -z "$SCREENCI_SECRET" ]; then
|
|
1056
|
-
echo "::error::SCREENCI_SECRET is not set.
|
|
1068
|
+
echo "::error::SCREENCI_SECRET is not set. Copy it from https://app.screenci.com/secrets and add it under Settings → Secrets and variables → Actions → Repository secrets."
|
|
1057
1069
|
exit 1
|
|
1058
1070
|
fi
|
|
1059
1071
|
|
|
1060
1072
|
- uses: actions/checkout@v4
|
|
1061
1073
|
|
|
1062
|
-
-
|
|
1063
|
-
|
|
1074
|
+
- uses: actions/setup-node@v6
|
|
1075
|
+
with:
|
|
1076
|
+
node-version: latest
|
|
1077
|
+
|
|
1078
|
+
- name: Install dependencies
|
|
1079
|
+
run: npm install
|
|
1064
1080
|
|
|
1065
1081
|
- name: Record
|
|
1066
1082
|
env:
|
|
1067
1083
|
SCREENCI_SECRET: \${{ secrets.SCREENCI_SECRET }}
|
|
1068
|
-
run:
|
|
1069
|
-
docker run --rm \\
|
|
1070
|
-
-e SCREENCI_SECRET \\
|
|
1071
|
-
-e SCREENCI_IN_CONTAINER=true \\
|
|
1072
|
-
-e SCREENCI_RECORD=true \\
|
|
1073
|
-
screenci-project \\
|
|
1074
|
-
npm run record
|
|
1084
|
+
run: npm run record
|
|
1075
1085
|
`;
|
|
1076
1086
|
}
|
|
1077
1087
|
function openBrowser(url) {
|
|
@@ -1090,7 +1100,7 @@ async function performBrowserLogin(appUrl) {
|
|
|
1090
1100
|
const secret = reqUrl.searchParams.get('secret');
|
|
1091
1101
|
if (secret) {
|
|
1092
1102
|
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
1093
|
-
res.end('<html><body style="font-family:sans-serif;display:flex;align-items:center;justify-content:center;height:100vh;margin:0"><p style="font-size:1.2rem">
|
|
1103
|
+
res.end('<html><body style="font-family:sans-serif;display:flex;align-items:center;justify-content:center;height:100vh;margin:0"><p style="font-size:1.2rem">Setup complete! You can close this tab.</p></body></html>');
|
|
1094
1104
|
server.close();
|
|
1095
1105
|
resolve(secret);
|
|
1096
1106
|
}
|
|
@@ -1120,28 +1130,115 @@ async function performBrowserLogin(appUrl) {
|
|
|
1120
1130
|
const timeout = setTimeout(() => {
|
|
1121
1131
|
server.close();
|
|
1122
1132
|
reject(new Error('Authentication timed out after 5 minutes'));
|
|
1123
|
-
},
|
|
1133
|
+
}, 15 * 60 * 1000);
|
|
1124
1134
|
server.on('close', () => clearTimeout(timeout));
|
|
1125
1135
|
});
|
|
1126
1136
|
}
|
|
1127
1137
|
function generateExampleVideo() {
|
|
1128
|
-
return `import { video } from 'screenci'
|
|
1138
|
+
return `import { createNarration, hide, video, voices } from 'screenci'
|
|
1139
|
+
|
|
1140
|
+
const narration = createNarration({
|
|
1141
|
+
voice: { name: voices.Sophie, style: 'Clear, friendly product walkthrough' },
|
|
1142
|
+
languages: {
|
|
1143
|
+
en: {
|
|
1144
|
+
cues: {
|
|
1145
|
+
intro:
|
|
1146
|
+
'Here is how to find instructions for starting to create your own ScreenCI [pronounce: screen see eye] videos. [short pause] Start on the homepage, then open the documentation from the hero section.',
|
|
1147
|
+
docs: 'The documentation opens with the guide sidebar on the left. In the Guides group, choose AI-Supported Editing.',
|
|
1148
|
+
},
|
|
1149
|
+
},
|
|
1150
|
+
es: {
|
|
1151
|
+
cues: {
|
|
1152
|
+
intro:
|
|
1153
|
+
'Aqui se muestra como encontrar instrucciones para empezar a crear tus propios videos de ScreenCI [pronounce: screen see eye]. [short pause] Comienza en la pagina principal y abre la documentacion desde la seccion principal.',
|
|
1154
|
+
docs: 'La documentacion se abre con la barra lateral de guias a la izquierda. En el grupo Guias, elige Edicion asistida por IA.',
|
|
1155
|
+
},
|
|
1156
|
+
},
|
|
1157
|
+
},
|
|
1158
|
+
})
|
|
1159
|
+
|
|
1160
|
+
video('Navigate to AI editing documentation', async ({ page }) => {
|
|
1161
|
+
await hide(async () => {
|
|
1162
|
+
await page.goto('https://screenci.com/')
|
|
1163
|
+
await page.getByText('ScreenCI', { exact: true }).first().waitFor()
|
|
1164
|
+
})
|
|
1129
1165
|
|
|
1130
|
-
|
|
1131
|
-
await page.
|
|
1132
|
-
|
|
1166
|
+
await narration.intro
|
|
1167
|
+
await page.getByRole('link', { name: 'View Documentation' }).click()
|
|
1168
|
+
|
|
1169
|
+
await narration.docs
|
|
1170
|
+
await page
|
|
1171
|
+
.getByRole('link', { name: 'AI-Supported Editing', exact: true })
|
|
1172
|
+
.click()
|
|
1133
1173
|
})
|
|
1134
1174
|
`;
|
|
1135
1175
|
}
|
|
1136
1176
|
async function promptProjectName() {
|
|
1137
1177
|
return input({ message: 'Project name:' });
|
|
1138
1178
|
}
|
|
1179
|
+
async function promptInitGitRepository() {
|
|
1180
|
+
return confirm({
|
|
1181
|
+
message: 'Initialize a git repository? (Y/n)',
|
|
1182
|
+
default: true,
|
|
1183
|
+
});
|
|
1184
|
+
}
|
|
1185
|
+
async function promptInitDependencies() {
|
|
1186
|
+
return confirm({
|
|
1187
|
+
message: 'Install dependencies now, including Chromium for Playwright? (Y/n)',
|
|
1188
|
+
default: true,
|
|
1189
|
+
});
|
|
1190
|
+
}
|
|
1191
|
+
async function promptInitAiAuthoring() {
|
|
1192
|
+
return confirm({
|
|
1193
|
+
message: 'Do you want to write videos with an AI agent based on a URL and not just source code? If yes, playwright-cli will be also installed.',
|
|
1194
|
+
default: true,
|
|
1195
|
+
});
|
|
1196
|
+
}
|
|
1197
|
+
async function promptInitGithubActionCi() {
|
|
1198
|
+
return confirm({
|
|
1199
|
+
message: 'Do you want to add Github Action CI? (Y/n)',
|
|
1200
|
+
default: true,
|
|
1201
|
+
});
|
|
1202
|
+
}
|
|
1139
1203
|
function getProjectDirName(name) {
|
|
1140
1204
|
return name.toLowerCase().replace(/\s+/g, '-');
|
|
1141
1205
|
}
|
|
1142
1206
|
function getInitProjectRoot() {
|
|
1143
1207
|
return process.env['SCREENCI_INIT_CWD'] ?? process.cwd();
|
|
1144
1208
|
}
|
|
1209
|
+
function isSourceCliEntrypoint(entrypoint) {
|
|
1210
|
+
return (entrypoint?.endsWith('/cli.ts') === true ||
|
|
1211
|
+
entrypoint?.endsWith('\\cli.ts') === true);
|
|
1212
|
+
}
|
|
1213
|
+
function getDevScreenciPackageRoot() {
|
|
1214
|
+
const explicitRoot = process.env.SCREENCI_DEV_PACKAGE_ROOT?.trim();
|
|
1215
|
+
if (explicitRoot)
|
|
1216
|
+
return resolve(explicitRoot);
|
|
1217
|
+
if (!isSourceCliEntrypoint(process.argv[1]))
|
|
1218
|
+
return undefined;
|
|
1219
|
+
return dirname(fileURLToPath(import.meta.url));
|
|
1220
|
+
}
|
|
1221
|
+
function getLocalScreenciDependency(packageRoot, projectDir) {
|
|
1222
|
+
const relativePath = pathRelative(projectDir, packageRoot) || '.';
|
|
1223
|
+
const normalizedPath = relativePath.replace(/\\/g, '/');
|
|
1224
|
+
return `file:${normalizedPath.startsWith('.') ? normalizedPath : `./${normalizedPath}`}`;
|
|
1225
|
+
}
|
|
1226
|
+
async function buildLocalScreenciPackage(packageRoot) {
|
|
1227
|
+
logger.info(`Using local screenci package: ${packageRoot}`);
|
|
1228
|
+
logger.info("Running 'npm run build' for local screenci package...");
|
|
1229
|
+
await spawnInherited('npm', ['run', 'build'], packageRoot, 'screenci dev build');
|
|
1230
|
+
}
|
|
1231
|
+
async function installLocalScreenciPackage(projectDir, packageRoot) {
|
|
1232
|
+
const packageJsonPath = resolve(projectDir, 'package.json');
|
|
1233
|
+
const packageJson = JSON.parse(await readFile(packageJsonPath, 'utf-8'));
|
|
1234
|
+
packageJson.dependencies = {
|
|
1235
|
+
...packageJson.dependencies,
|
|
1236
|
+
screenci: getLocalScreenciDependency(packageRoot, projectDir),
|
|
1237
|
+
};
|
|
1238
|
+
await writeFile(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\n`);
|
|
1239
|
+
logger.info('Installing local screenci package into this project...');
|
|
1240
|
+
await spawnInherited('npm', ['install', '--install-links'], projectDir, 'screenci dev install');
|
|
1241
|
+
}
|
|
1145
1242
|
function buildChildEnv() {
|
|
1146
1243
|
const { PATH, HOME, USER, LOGNAME, TMPDIR, TEMP, TMP } = process.env;
|
|
1147
1244
|
return {
|
|
@@ -1160,6 +1257,7 @@ function buildChildEnv() {
|
|
|
1160
1257
|
SCREENCI_IN_CONTAINER: process.env.SCREENCI_IN_CONTAINER,
|
|
1161
1258
|
SCREENCI_RECORD: process.env.SCREENCI_RECORD,
|
|
1162
1259
|
SCREENCI_SIGNAL_LOGGING: process.env.SCREENCI_SIGNAL_LOGGING,
|
|
1260
|
+
SCREENCI_DEV_PACKAGE_ROOT: process.env.SCREENCI_DEV_PACKAGE_ROOT,
|
|
1163
1261
|
};
|
|
1164
1262
|
}
|
|
1165
1263
|
function requireContainerRuntime() {
|
|
@@ -1211,7 +1309,8 @@ function checkNodeVersion() {
|
|
|
1211
1309
|
process.exit(1);
|
|
1212
1310
|
}
|
|
1213
1311
|
}
|
|
1214
|
-
async function runInit(projectNameArg,
|
|
1312
|
+
async function runInit(projectNameArg, options) {
|
|
1313
|
+
const { verbose, git, install, yes, skill, ci } = options;
|
|
1215
1314
|
checkNodeVersion();
|
|
1216
1315
|
checkContainerRuntimeForInit();
|
|
1217
1316
|
const initCwd = getInitProjectRoot();
|
|
@@ -1229,10 +1328,26 @@ async function runInit(projectNameArg, verbose = false) {
|
|
|
1229
1328
|
logger.error(`Error: Directory "${dirName}" already exists`);
|
|
1230
1329
|
process.exit(1);
|
|
1231
1330
|
}
|
|
1232
|
-
const
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1331
|
+
const shouldInitializeGitRepository = yes
|
|
1332
|
+
? true
|
|
1333
|
+
: git
|
|
1334
|
+
? true
|
|
1335
|
+
: await promptInitGitRepository();
|
|
1336
|
+
const shouldInstallDependencies = yes
|
|
1337
|
+
? true
|
|
1338
|
+
: install
|
|
1339
|
+
? true
|
|
1340
|
+
: await promptInitDependencies();
|
|
1341
|
+
const shouldAddPlaywrightCli = yes
|
|
1342
|
+
? true
|
|
1343
|
+
: skill
|
|
1344
|
+
? true
|
|
1345
|
+
: await promptInitAiAuthoring();
|
|
1346
|
+
const shouldAddGithubActionCi = yes
|
|
1347
|
+
? true
|
|
1348
|
+
: ci
|
|
1349
|
+
? true
|
|
1350
|
+
: await promptInitGithubActionCi();
|
|
1236
1351
|
const skillsArgs = [
|
|
1237
1352
|
'--yes',
|
|
1238
1353
|
'skills',
|
|
@@ -1244,15 +1359,28 @@ async function runInit(projectNameArg, verbose = false) {
|
|
|
1244
1359
|
'-y',
|
|
1245
1360
|
];
|
|
1246
1361
|
const skillsCommand = `npx ${skillsArgs.join(' ')}`;
|
|
1362
|
+
const devScreenciPackageRoot = getDevScreenciPackageRoot();
|
|
1363
|
+
const screenciDependency = devScreenciPackageRoot
|
|
1364
|
+
? getLocalScreenciDependency(devScreenciPackageRoot, projectDir)
|
|
1365
|
+
: 'latest';
|
|
1366
|
+
if (devScreenciPackageRoot) {
|
|
1367
|
+
await buildLocalScreenciPackage(devScreenciPackageRoot);
|
|
1368
|
+
}
|
|
1247
1369
|
await mkdir(resolve(projectDir, 'videos'), { recursive: true });
|
|
1248
|
-
|
|
1370
|
+
if (shouldAddGithubActionCi) {
|
|
1371
|
+
await mkdir(resolve(projectDir, '.github', 'workflows'), {
|
|
1372
|
+
recursive: true,
|
|
1373
|
+
});
|
|
1374
|
+
}
|
|
1249
1375
|
await writeFile(resolve(projectDir, 'screenci.config.ts'), generateConfig(projectName));
|
|
1250
|
-
await writeFile(resolve(projectDir, 'package.json'), generatePackageJson(dirName, shouldAddPlaywrightCli));
|
|
1376
|
+
await writeFile(resolve(projectDir, 'package.json'), generatePackageJson(dirName, shouldAddPlaywrightCli, screenciDependency));
|
|
1251
1377
|
await writeFile(resolve(projectDir, 'README.md'), generateReadme(projectName));
|
|
1252
1378
|
await writeFile(resolve(projectDir, 'Dockerfile'), generateDockerfile());
|
|
1253
1379
|
await writeFile(resolve(projectDir, '.gitignore'), generateGitignore());
|
|
1254
1380
|
await writeFile(resolve(projectDir, 'videos', 'example.video.ts'), generateExampleVideo());
|
|
1255
|
-
|
|
1381
|
+
if (shouldAddGithubActionCi) {
|
|
1382
|
+
await writeFile(resolve(projectDir, '.github', 'workflows', 'record.yml'), generateGithubAction());
|
|
1383
|
+
}
|
|
1256
1384
|
await writeFile(resolve(projectDir, '.env'), '');
|
|
1257
1385
|
logger.info(`Initialized screenci project "${projectName}" in ${projectDir}/`);
|
|
1258
1386
|
logger.info('Files created:');
|
|
@@ -1262,44 +1390,77 @@ async function runInit(projectNameArg, verbose = false) {
|
|
|
1262
1390
|
logger.info(' Dockerfile');
|
|
1263
1391
|
logger.info(' .gitignore');
|
|
1264
1392
|
logger.info(' videos/example.video.ts');
|
|
1265
|
-
|
|
1393
|
+
if (shouldAddGithubActionCi) {
|
|
1394
|
+
logger.info(' .github/workflows/record.yml');
|
|
1395
|
+
}
|
|
1266
1396
|
logger.info(' .env (empty placeholder)');
|
|
1267
1397
|
logger.info('');
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
}
|
|
1273
|
-
else {
|
|
1274
|
-
const spinner = ora('Adding ScreenCI skills...').start();
|
|
1275
|
-
try {
|
|
1276
|
-
await spawnSilent('npx', skillsArgs, projectDir);
|
|
1277
|
-
spinner.succeed('ScreenCI skills added');
|
|
1398
|
+
if (shouldInitializeGitRepository) {
|
|
1399
|
+
if (verbose) {
|
|
1400
|
+
logger.info("Running 'git init'...");
|
|
1401
|
+
await spawnInherited('git', ['init'], projectDir, 'screenci init');
|
|
1278
1402
|
}
|
|
1279
|
-
|
|
1280
|
-
spinner
|
|
1281
|
-
|
|
1403
|
+
else {
|
|
1404
|
+
const spinner = ora('Initializing git repository...').start();
|
|
1405
|
+
try {
|
|
1406
|
+
await spawnSilent('git', ['init'], projectDir);
|
|
1407
|
+
spinner.succeed('Git repository initialized');
|
|
1408
|
+
}
|
|
1409
|
+
catch (err) {
|
|
1410
|
+
spinner.fail('git init failed');
|
|
1411
|
+
throw err;
|
|
1412
|
+
}
|
|
1282
1413
|
}
|
|
1283
1414
|
}
|
|
1284
|
-
if (
|
|
1285
|
-
logger.info(
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
const spinner = ora('Running npm install...').start();
|
|
1290
|
-
try {
|
|
1291
|
-
await spawnSilent('npm', ['install', '--prefix', projectDir]);
|
|
1292
|
-
spinner.succeed('npm install complete');
|
|
1415
|
+
if (shouldInstallDependencies) {
|
|
1416
|
+
logger.info('screenci requires dependencies to be installed.');
|
|
1417
|
+
if (verbose) {
|
|
1418
|
+
logger.info(`Running '${skillsCommand}'...`);
|
|
1419
|
+
await spawnInherited('npx', skillsArgs, projectDir, 'screenci init');
|
|
1293
1420
|
}
|
|
1294
|
-
|
|
1295
|
-
spinner
|
|
1296
|
-
|
|
1421
|
+
else {
|
|
1422
|
+
const spinner = ora('Adding ScreenCI skills...').start();
|
|
1423
|
+
try {
|
|
1424
|
+
await spawnSilent('npx', skillsArgs, projectDir);
|
|
1425
|
+
spinner.succeed('ScreenCI skills added');
|
|
1426
|
+
}
|
|
1427
|
+
catch (err) {
|
|
1428
|
+
spinner.fail('ScreenCI skills install failed');
|
|
1429
|
+
throw err;
|
|
1430
|
+
}
|
|
1297
1431
|
}
|
|
1432
|
+
if (verbose) {
|
|
1433
|
+
const installArgs = devScreenciPackageRoot
|
|
1434
|
+
? ['install', '--install-links']
|
|
1435
|
+
: ['install'];
|
|
1436
|
+
logger.info(`Running 'npm ${installArgs.join(' ')}'...`);
|
|
1437
|
+
await spawnInherited('npm', installArgs, projectDir, 'screenci init');
|
|
1438
|
+
}
|
|
1439
|
+
else {
|
|
1440
|
+
const spinner = ora('Running npm install...').start();
|
|
1441
|
+
try {
|
|
1442
|
+
const installArgs = devScreenciPackageRoot
|
|
1443
|
+
? ['install', '--install-links', '--prefix', projectDir]
|
|
1444
|
+
: ['install', '--prefix', projectDir];
|
|
1445
|
+
await spawnSilent('npm', installArgs);
|
|
1446
|
+
spinner.succeed('npm install complete');
|
|
1447
|
+
}
|
|
1448
|
+
catch (err) {
|
|
1449
|
+
spinner.fail('npm install failed');
|
|
1450
|
+
throw err;
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1453
|
+
logger.info("Local development requires Chromium for Playwright, running 'npx playwright install chromium --with-deps'...");
|
|
1454
|
+
await spawnInherited('npx', ['playwright', 'install', 'chromium', '--with-deps'], projectDir, 'screenci init');
|
|
1455
|
+
logger.info(`${pc.green('✔')} Playwright installed successfully`);
|
|
1456
|
+
}
|
|
1457
|
+
else {
|
|
1458
|
+
logger.info('Dependencies were not installed automatically.');
|
|
1459
|
+
logger.info('Run these commands when you are ready:');
|
|
1460
|
+
logger.info(` ${skillsCommand}`);
|
|
1461
|
+
logger.info(' npm install');
|
|
1462
|
+
logger.info(' npx playwright install chromium --with-deps');
|
|
1298
1463
|
}
|
|
1299
|
-
logger.info('Local development requires Chromium for Playwright.');
|
|
1300
|
-
logger.info("Running 'npx playwright install chromium --with-deps'...");
|
|
1301
|
-
await spawnInherited('npx', ['playwright', 'install', 'chromium', '--with-deps'], projectDir, 'screenci init');
|
|
1302
|
-
logger.info('Warming the container image for faster first record...');
|
|
1303
1464
|
const cliDir = dirname(fileURLToPath(import.meta.url));
|
|
1304
1465
|
await buildRecordImages(requireContainerRuntime(), cliDir, resolve(projectDir, 'Dockerfile'), projectDir, verbose);
|
|
1305
1466
|
logger.info('');
|
|
@@ -1356,6 +1517,9 @@ export async function main() {
|
|
|
1356
1517
|
}
|
|
1357
1518
|
}
|
|
1358
1519
|
}
|
|
1520
|
+
if (useContainer) {
|
|
1521
|
+
await ensureScreenciSecret();
|
|
1522
|
+
}
|
|
1359
1523
|
if (useContainer) {
|
|
1360
1524
|
await runWithContainer(parsed.otherArgs, parsed.configPath, parsed.imageTag, parsed.verbose, parsed.forcedRuntime);
|
|
1361
1525
|
}
|
|
@@ -1416,6 +1580,9 @@ export async function main() {
|
|
|
1416
1580
|
.action(async () => {
|
|
1417
1581
|
const parsed = parseConfigCliArgs(getSubcommandArgv('test'));
|
|
1418
1582
|
await run('test', parsed.otherArgs, parsed.configPath);
|
|
1583
|
+
if (process.env.SCREENCI_IN_CONTAINER === 'true')
|
|
1584
|
+
return;
|
|
1585
|
+
logger.info('Tests passed. Run `npx screenci record` to render the videos.');
|
|
1419
1586
|
});
|
|
1420
1587
|
program
|
|
1421
1588
|
.command('info')
|
|
@@ -1451,13 +1618,25 @@ export async function main() {
|
|
|
1451
1618
|
program
|
|
1452
1619
|
.command('init [name]')
|
|
1453
1620
|
.description('Initialize a new screenci project')
|
|
1621
|
+
.option('--git', 'initialize a git repository without prompting')
|
|
1622
|
+
.option('--install', 'install skills, dependencies, and Chromium without prompting')
|
|
1623
|
+
.option('--ci', 'add GitHub Action CI without prompting')
|
|
1624
|
+
.option('--skill', 'enable playwright-cli without prompting')
|
|
1625
|
+
.option('-y, --yes', 'answer yes to all init prompts')
|
|
1454
1626
|
.option('-v, --verbose', 'verbose output')
|
|
1455
1627
|
.action(async (name, options) => {
|
|
1456
1628
|
if (name === 'auth') {
|
|
1457
1629
|
await runInitAuth();
|
|
1458
1630
|
}
|
|
1459
1631
|
else {
|
|
1460
|
-
await runInit(name,
|
|
1632
|
+
await runInit(name, {
|
|
1633
|
+
verbose: options['verbose'] ?? false,
|
|
1634
|
+
git: options['git'] ?? false,
|
|
1635
|
+
install: options['install'] ?? false,
|
|
1636
|
+
yes: options['yes'] ?? false,
|
|
1637
|
+
skill: options['skill'] ?? false,
|
|
1638
|
+
ci: options['ci'] ?? false,
|
|
1639
|
+
});
|
|
1461
1640
|
}
|
|
1462
1641
|
});
|
|
1463
1642
|
try {
|
|
@@ -1810,9 +1989,9 @@ async function run(command, additionalArgs, customConfigPath) {
|
|
|
1810
1989
|
logger.error(errorMsg);
|
|
1811
1990
|
process.exit(1);
|
|
1812
1991
|
}
|
|
1813
|
-
await ensureScreenciSecret();
|
|
1814
1992
|
// Only validate args for record command
|
|
1815
1993
|
if (command === 'record') {
|
|
1994
|
+
await ensureScreenciSecret();
|
|
1816
1995
|
validateArgs(additionalArgs);
|
|
1817
1996
|
const screenciDir = resolve(dirname(configPath), '.screenci');
|
|
1818
1997
|
clearDirectory(screenciDir);
|
|
@@ -1822,6 +2001,12 @@ async function run(command, additionalArgs, customConfigPath) {
|
|
|
1822
2001
|
logger.info(`Running ScreenCI ${mode} with npx...`);
|
|
1823
2002
|
logger.info(`Using config: ${configPath}`);
|
|
1824
2003
|
}
|
|
2004
|
+
const devScreenciPackageRoot = getDevScreenciPackageRoot();
|
|
2005
|
+
if (devScreenciPackageRoot) {
|
|
2006
|
+
const configDir = dirname(configPath);
|
|
2007
|
+
await buildLocalScreenciPackage(devScreenciPackageRoot);
|
|
2008
|
+
await installLocalScreenciPackage(configDir, devScreenciPackageRoot);
|
|
2009
|
+
}
|
|
1825
2010
|
const playwrightArgs = [
|
|
1826
2011
|
'playwright',
|
|
1827
2012
|
'test',
|