screenci 0.0.67 → 0.0.69

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 (90) hide show
  1. package/README.md +1 -1
  2. package/dist/cli.d.ts +3 -18
  3. package/dist/cli.d.ts.map +1 -1
  4. package/dist/cli.js +105 -154
  5. package/dist/cli.js.map +1 -1
  6. package/dist/docs/manifest.d.ts +13 -13
  7. package/dist/docs/manifest.js +4 -4
  8. package/dist/docs/manifest.js.map +1 -1
  9. package/dist/docs/video-sources/studio.video.d.ts +2 -0
  10. package/dist/docs/video-sources/studio.video.d.ts.map +1 -0
  11. package/dist/docs/video-sources/studio.video.js +57 -0
  12. package/dist/docs/video-sources/studio.video.js.map +1 -0
  13. package/dist/e2e/htmlRasterizer.e2e.d.ts +2 -0
  14. package/dist/e2e/htmlRasterizer.e2e.d.ts.map +1 -0
  15. package/dist/e2e/htmlRasterizer.e2e.js +107 -0
  16. package/dist/e2e/htmlRasterizer.e2e.js.map +1 -0
  17. package/dist/index.d.ts +5 -3
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +2 -1
  20. package/dist/index.js.map +1 -1
  21. package/dist/src/asset.d.ts +141 -53
  22. package/dist/src/asset.d.ts.map +1 -1
  23. package/dist/src/asset.js +533 -78
  24. package/dist/src/asset.js.map +1 -1
  25. package/dist/src/audio.d.ts +113 -0
  26. package/dist/src/audio.d.ts.map +1 -0
  27. package/dist/src/audio.js +204 -0
  28. package/dist/src/audio.js.map +1 -0
  29. package/dist/src/changeFocus.d.ts.map +1 -1
  30. package/dist/src/changeFocus.js +17 -3
  31. package/dist/src/changeFocus.js.map +1 -1
  32. package/dist/src/defaults.d.ts +7 -1
  33. package/dist/src/defaults.d.ts.map +1 -1
  34. package/dist/src/defaults.js +9 -2
  35. package/dist/src/defaults.js.map +1 -1
  36. package/dist/src/events.d.ts +138 -6
  37. package/dist/src/events.d.ts.map +1 -1
  38. package/dist/src/events.js +80 -2
  39. package/dist/src/events.js.map +1 -1
  40. package/dist/src/htmlRasterizer.d.ts +110 -0
  41. package/dist/src/htmlRasterizer.d.ts.map +1 -0
  42. package/dist/src/htmlRasterizer.js +364 -0
  43. package/dist/src/htmlRasterizer.js.map +1 -0
  44. package/dist/src/init.d.ts +17 -0
  45. package/dist/src/init.d.ts.map +1 -1
  46. package/dist/src/init.js +127 -7
  47. package/dist/src/init.js.map +1 -1
  48. package/dist/src/instrument.d.ts.map +1 -1
  49. package/dist/src/instrument.js +5 -3
  50. package/dist/src/instrument.js.map +1 -1
  51. package/dist/src/linkSession.d.ts +36 -0
  52. package/dist/src/linkSession.d.ts.map +1 -0
  53. package/dist/src/linkSession.js +122 -0
  54. package/dist/src/linkSession.js.map +1 -0
  55. package/dist/src/mouse.d.ts +4 -0
  56. package/dist/src/mouse.d.ts.map +1 -1
  57. package/dist/src/mouse.js +31 -10
  58. package/dist/src/mouse.js.map +1 -1
  59. package/dist/src/performance.d.ts +41 -0
  60. package/dist/src/performance.d.ts.map +1 -0
  61. package/dist/src/performance.js +49 -0
  62. package/dist/src/performance.js.map +1 -0
  63. package/dist/src/recording.d.ts +1 -1
  64. package/dist/src/recording.d.ts.map +1 -1
  65. package/dist/src/recordingData.d.ts +2 -2
  66. package/dist/src/recordingData.d.ts.map +1 -1
  67. package/dist/src/runtimeContext.d.ts +38 -0
  68. package/dist/src/runtimeContext.d.ts.map +1 -1
  69. package/dist/src/runtimeContext.js +25 -0
  70. package/dist/src/runtimeContext.js.map +1 -1
  71. package/dist/src/runtimeMode.d.ts +7 -0
  72. package/dist/src/runtimeMode.d.ts.map +1 -1
  73. package/dist/src/runtimeMode.js +10 -0
  74. package/dist/src/runtimeMode.js.map +1 -1
  75. package/dist/src/types.d.ts +40 -0
  76. package/dist/src/types.d.ts.map +1 -1
  77. package/dist/src/types.js.map +1 -1
  78. package/dist/src/video.d.ts +34 -1
  79. package/dist/src/video.d.ts.map +1 -1
  80. package/dist/src/video.js +120 -19
  81. package/dist/src/video.js.map +1 -1
  82. package/dist/src/voices.d.ts +22 -9
  83. package/dist/src/voices.d.ts.map +1 -1
  84. package/dist/src/voices.js +6 -24
  85. package/dist/src/voices.js.map +1 -1
  86. package/dist/tsconfig.tsbuildinfo +1 -1
  87. package/package.json +16 -2
  88. package/skills/screenci/SKILL.md +27 -5
  89. package/skills/screenci/references/init.md +2 -0
  90. package/skills/screenci/references/record.md +6 -1
package/README.md CHANGED
@@ -149,7 +149,7 @@ That is also the right place for BYOK-style secrets such as
149
149
  | `autoZoom` | Smooth camera follow for an interaction block |
150
150
  | `zoomTo` | Manual camera framing for a locator or point |
151
151
  | `resetZoom` | Returns from manual framing to the full viewport |
152
- | `createAssets` | Inserts timed media overlays into the recording timeline |
152
+ | `createOverlays` | Inserts timed media overlays into the recording timeline |
153
153
  | `voices` | Available voice constants such as `voices.Ava` |
154
154
  | `modelTypes` | Narration model constants such as `modelTypes.consistent` |
155
155
 
package/dist/cli.d.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  import type { RecordingCustomVoiceRef, RecordingData } from './src/events.js';
2
2
  import type { VoiceKey } from './src/voices.js';
3
3
  import type { RecordUploadPolicy } from './src/types.js';
4
+ import { getCliLinkSessionApiUrl, getDevBackendUrl, getDevFrontendUrl } from './src/linkSession.js';
5
+ export { getCliLinkSessionApiUrl, getDevBackendUrl, getDevFrontendUrl };
4
6
  type PlaywrightListReportSuite = {
5
7
  title?: string;
6
8
  specs?: Array<{
@@ -31,29 +33,16 @@ type PreparedUploadAsset = {
31
33
  fileBuffer?: Buffer;
32
34
  contentType?: string;
33
35
  };
34
- export type StudioAppliedChange = {
35
- kind?: string;
36
- label?: string;
37
- cue?: string;
38
- language?: string;
39
- from?: string;
40
- to?: string;
41
- };
42
36
  export type UploadStudioInfo = {
43
37
  held: true;
44
38
  } | {
45
- appliedChanges: StudioAppliedChange[];
39
+ applied: true;
46
40
  };
47
41
  export type StudioUploadNotice = {
48
42
  videoName: string;
49
43
  videoId: string | null;
50
44
  studio: UploadStudioInfo;
51
45
  };
52
- /**
53
- * One-line summary of Studio overrides for CLI output, e.g.
54
- * `recording.size (1 → 0.8), narration "intro" (en)`.
55
- */
56
- export declare function formatStudioChangeSummary(changes: StudioAppliedChange[]): string;
57
46
  export declare function formatStudioUrl(appUrl: string, projectId: string, videoId: string): string;
58
47
  type OrgPlan = 'free' | 'starter' | 'business';
59
48
  export declare function withUploadRetry<T>(fn: () => Promise<T>, signal: AbortSignal | undefined, maxAttempts?: number): Promise<T>;
@@ -76,9 +65,6 @@ export declare function uploadRecordings(screenciDir: string, projectName: strin
76
65
  studioNotices: StudioUploadNotice[];
77
66
  plan: OrgPlan | null;
78
67
  }>;
79
- export declare function getDevBackendUrl(): string;
80
- export declare function getDevFrontendUrl(): string;
81
- export declare function getCliLinkSessionApiUrl(): string;
82
68
  export declare function extractConfigStringLiteral(configSource: string, property: 'projectName' | 'envFile'): string | undefined;
83
69
  export declare function extractRecordUploadPolicyLiteral(configSource: string): RecordUploadPolicy | undefined;
84
70
  export declare function extractMockRecordLiteral(configSource: string): boolean | undefined;
@@ -89,5 +75,4 @@ export declare function ensureScreenciSecret(resolvedConfigPath?: string, opts?:
89
75
  }): Promise<string | undefined>;
90
76
  export declare function main(): Promise<void>;
91
77
  export declare function logCliError(error: unknown): void;
92
- export {};
93
78
  //# sourceMappingURL=cli.d.ts.map
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":"AAwBA,OAAO,KAAK,EACV,uBAAuB,EACvB,aAAa,EAEd,MAAM,iBAAiB,CAAA;AAMxB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAC/C,OAAO,KAAK,EAAE,kBAAkB,EAAkB,MAAM,gBAAgB,CAAA;AAKxE,OAAO,EAGL,uBAAuB,EACvB,gBAAgB,EAChB,iBAAiB,EAOlB,MAAM,sBAAsB,CAAA;AAQ7B,OAAO,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,CAAA;AAqBvE,KAAK,yBAAyB,GAAG;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,KAAK,CAAC,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAChC,MAAM,CAAC,EAAE,yBAAyB,EAAE,CAAA;CACrC,CAAA;AAUD;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CACtC,GAAG,GAAE,MAAM,CAAC,UAAwB,EACpC,MAAM,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAmB,EAC5C,KAAK,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAkB,GACzC,OAAO,CAIT;AAED,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,SAAS,yBAAyB,EAAE,GAC3C,MAAM,EAAE,CAiBV;AAiND,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;AASD,MAAM,MAAM,gBAAgB,GAAG;IAAE,IAAI,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,OAAO,EAAE,IAAI,CAAA;CAAE,CAAA;AAEjE,MAAM,MAAM,kBAAkB,GAAG;IAC/B,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,MAAM,EAAE,gBAAgB,CAAA;CACzB,CAAA;AAED,wBAAgB,eAAe,CAC7B,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,GACd,MAAM,CAER;AAED,KAAK,OAAO,GAAG,MAAM,GAAG,SAAS,GAAG,UAAU,CAAA;AAkE9C,wBAAsB,eAAe,CAAC,CAAC,EACrC,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,MAAM,EAAE,WAAW,GAAG,SAAS,EAC/B,WAAW,SAAI,GACd,OAAO,CAAC,CAAC,CAAC,CAWZ;AA4SD,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;AAkaD,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,aAAa,EACnB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAiGhC;AAED,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;AAKD,wBAAgB,wBAAwB,CACtC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,GACd,MAAM,CASR;AAED,wBAAgB,8BAA8B,CAC5C,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,MAAM,GACb,IAAI,CAcN;AA8GD,wBAAsB,gBAAgB,CACpC,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,aAAa,CAAC,EAAE,MAAM,EACtB,OAAO,UAAQ,GACd,OAAO,CAAC;IACT,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,WAAW,EAAE,OAAO,CAAA;IACpB,gBAAgB,EAAE,MAAM,EAAE,CAAA;IAC1B,mBAAmB,EAAE,KAAK,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAClE,aAAa,EAAE,kBAAkB,EAAE,CAAA;IACnC,IAAI,EAAE,OAAO,GAAG,IAAI,CAAA;CACrB,CAAC,CA8GD;AA8LD,wBAAgB,0BAA0B,CACxC,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,aAAa,GAAG,SAAS,GAClC,MAAM,GAAG,SAAS,CAepB;AAED,wBAAgB,gCAAgC,CAC9C,YAAY,EAAE,MAAM,GACnB,kBAAkB,GAAG,SAAS,CAmBhC;AAED,wBAAgB,wBAAwB,CACtC,YAAY,EAAE,MAAM,GACnB,OAAO,GAAG,SAAS,CAQrB;AAkCD,wBAAgB,wBAAwB,CAAC,kBAAkB,EAAE,MAAM,GAAG,MAAM,CAS3E;AA0QD,wBAAsB,oBAAoB,CACxC,kBAAkB,CAAC,EAAE,MAAM,EAC3B,IAAI,GAAE;IAAE,WAAW,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAO,GACvD,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CA8I7B;AAED,wBAAsB,IAAI,kBAuWzB;AA8ND,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAchD"}
package/dist/cli.js CHANGED
@@ -13,33 +13,26 @@ import { determinePackageManager, parsePackageManager, runInit, } from './src/in
13
13
  import { SCREENCI_DISABLE_RECORDING_TIMINGS_ENV, SCREENCI_MOCK_RECORD_ENV, } from './src/runtimeMode.js';
14
14
  import { DEFAULT_RECORD_UPLOAD_POLICY } from './src/defaults.js';
15
15
  import { findDuplicateTitles, formatDuplicateTitlesMessage, } from './src/titleValidation.js';
16
+ import { createLinkSessionSpec, deletePersistedLinkSessionSpec, getCliLinkSessionApiUrl, getDevBackendUrl, getDevFrontendUrl, getLinkSessionFilePath, getScreenCIEnvironment, isStoredLinkSessionReusable, readPersistedLinkSessionSpec, SCREENCI_LINK_SESSION_FILE, writePersistedLinkSessionSpec, } from './src/linkSession.js';
17
+ // Re-export the environment-aware URL helpers so existing importers (and tests)
18
+ // can keep importing them from the CLI entrypoint.
19
+ export { getCliLinkSessionApiUrl, getDevBackendUrl, getDevFrontendUrl };
16
20
  const SCREENCI_MOCK_RECORD_DOCS_URL = 'https://screenci.com/docs/reference/cli/#--mock-record';
17
21
  const SCREENCI_RECORD_DOCS_URL = 'https://screenci.com/docs/reference/cli/#screenci-record';
18
- const SCREENCI_ENVIRONMENT_VARIABLE = 'SCREENCI_ENVIRONMENT';
19
- const SCREENCI_ENVIRONMENT_OPTION_VALUES = ['local', 'dev', 'prod'];
20
- const SCREENCI_PRODUCTION_BACKEND_URL = 'https://api.screenci.com';
21
- const SCREENCI_PRODUCTION_FRONTEND_URL = 'https://app.screenci.com';
22
- const SCREENCI_DEVELOPMENT_BACKEND_URL = 'https://dev.api.screenci.com';
23
- const SCREENCI_DEVELOPMENT_FRONTEND_URL = 'https://dev.app.screenci.com';
24
- const SCREENCI_LINK_SESSION_FILE = 'link-session.json';
22
+ // Records the recordId of the most recent `screenci record` upload so
23
+ // `screenci info` can report exactly the run that was just made.
24
+ const SCREENCI_LAST_RECORD_FILE = 'last-record.json';
25
25
  const SCREENCI_LINK_SESSION_POLL_INTERVAL_MS = 2_000;
26
26
  // `record --poll` keeps a non-interactive session (an agent or CI) waiting for
27
27
  // sign-in. We poll on a slower cadence than the interactive loop so a long wait
28
28
  // for a human to click the link does not hammer the backend.
29
29
  const SCREENCI_LINK_SESSION_POLL_FLAG_INTERVAL_MS = 5_000;
30
+ // `record --poll-auth` does not wait forever: after this long without a
31
+ // completed sign-in we stop polling and exit cleanly so an agent or CI step does
32
+ // not hang indefinitely. The link stays valid, so the command can be rerun. The
33
+ // default can be overridden with SCREENCI_POLL_AUTH_TIMEOUT_MS (milliseconds).
34
+ const SCREENCI_LINK_SESSION_POLL_FLAG_TIMEOUT_MS = 5 * 60 * 1_000;
30
35
  const require = createRequire(import.meta.url);
31
- function parseScreenCIEnvironment(value) {
32
- if (value === undefined)
33
- return undefined;
34
- if (SCREENCI_ENVIRONMENT_OPTION_VALUES.includes(value)) {
35
- return value;
36
- }
37
- throw new Error(`Invalid ${SCREENCI_ENVIRONMENT_VARIABLE} "${value}". Expected one of: ${SCREENCI_ENVIRONMENT_OPTION_VALUES.join(', ')}`);
38
- }
39
- function getScreenCIEnvironment() {
40
- const parsed = parseScreenCIEnvironment(process.env[SCREENCI_ENVIRONMENT_VARIABLE]);
41
- return parsed ?? 'prod';
42
- }
43
36
  /**
44
37
  * Reports whether the current session can complete an interactive browser
45
38
  * sign-in. A session is interactive only when both stdin and stdout are
@@ -221,25 +214,6 @@ function contentTypeForPath(filePath) {
221
214
  };
222
215
  return contentTypeMap[ext] ?? 'application/octet-stream';
223
216
  }
224
- /**
225
- * One-line summary of Studio overrides for CLI output, e.g.
226
- * `recording.size (1 → 0.8), narration "intro" (en)`.
227
- */
228
- export function formatStudioChangeSummary(changes) {
229
- return changes
230
- .map((change) => {
231
- const label = change.label ?? 'selection';
232
- if (change.kind === 'narration') {
233
- return change.language !== undefined
234
- ? `${label} (${change.language})`
235
- : label;
236
- }
237
- return change.from !== undefined && change.to !== undefined
238
- ? `${label} (${change.from} → ${change.to})`
239
- : label;
240
- })
241
- .join(', ');
242
- }
243
217
  export function formatStudioUrl(appUrl, projectId, videoId) {
244
218
  return `${appUrl}/project/${projectId}/video/${videoId}/studio`;
245
219
  }
@@ -1201,43 +1175,6 @@ async function countCompletedRecordings(screenciDir) {
1201
1175
  }
1202
1176
  return entries.filter((entry) => existsSync(resolve(screenciDir, entry, 'data.json'))).length;
1203
1177
  }
1204
- export function getDevBackendUrl() {
1205
- switch (getScreenCIEnvironment()) {
1206
- case 'local': {
1207
- const devBackendPort = process.env.DEV_BACKEND_PORT;
1208
- return devBackendPort
1209
- ? `http://localhost:${devBackendPort}`
1210
- : 'http://localhost:8787';
1211
- }
1212
- case 'dev':
1213
- return SCREENCI_DEVELOPMENT_BACKEND_URL;
1214
- case 'prod':
1215
- return SCREENCI_PRODUCTION_BACKEND_URL;
1216
- }
1217
- }
1218
- export function getDevFrontendUrl() {
1219
- switch (getScreenCIEnvironment()) {
1220
- case 'local': {
1221
- const devFrontendPort = process.env.DEV_FRONTEND_PORT;
1222
- return devFrontendPort
1223
- ? `http://localhost:${devFrontendPort}`
1224
- : 'http://localhost:5173';
1225
- }
1226
- case 'dev':
1227
- return SCREENCI_DEVELOPMENT_FRONTEND_URL;
1228
- case 'prod':
1229
- return SCREENCI_PRODUCTION_FRONTEND_URL;
1230
- }
1231
- }
1232
- export function getCliLinkSessionApiUrl() {
1233
- // The `/cli-link/session` routes are Convex HTTP actions served from the same
1234
- // backend host as the `/cli/*` upload endpoints. We hit that host directly
1235
- // rather than the frontend: the frontend only forwards these routes via the
1236
- // vite dev-server proxy, which does not exist on the hosted dev/prod frontend
1237
- // (a POST there returns 405). The CLI runs under Node, so there is no CORS
1238
- // constraint that would require going through the frontend origin.
1239
- return getDevBackendUrl();
1240
- }
1241
1178
  function getScreenCISecretsUrl() {
1242
1179
  return `${getDevFrontendUrl()}/secrets`;
1243
1180
  }
@@ -1463,25 +1400,6 @@ async function requireScreenCISecret(configPath, opts = {}) {
1463
1400
  apiUrl: getDevBackendUrl(),
1464
1401
  };
1465
1402
  }
1466
- async function fetchProjectInfo(configPath) {
1467
- const { screenciConfig, secret, apiUrl } = await requireScreenCISecret(configPath, { interactive: detectInteractiveSession() });
1468
- const url = new URL(`${apiUrl}/cli/project-info`);
1469
- url.searchParams.set('projectName', screenciConfig.projectName);
1470
- const res = await fetch(url.toString(), {
1471
- headers: {
1472
- 'X-ScreenCI-Secret': secret,
1473
- },
1474
- });
1475
- if (!res.ok) {
1476
- const text = await res.text();
1477
- throw new Error(`Failed to fetch project info: ${res.status} ${text}${hint401(res.status, secret)}`);
1478
- }
1479
- return (await res.json());
1480
- }
1481
- async function printProjectInfo(configPath) {
1482
- const info = await fetchProjectInfo(configPath);
1483
- process.stdout.write(`${JSON.stringify(info, null, 2)}\n`);
1484
- }
1485
1403
  async function updateVideoVisibility(videoId, isPublic, configPath) {
1486
1404
  const { secret, apiUrl } = await requireScreenCISecret(configPath, {
1487
1405
  interactive: detectInteractiveSession(),
@@ -1499,6 +1417,62 @@ async function updateVideoVisibility(videoId, isPublic, configPath) {
1499
1417
  }
1500
1418
  logger.info(`${isPublic ? 'Made public' : 'Made private'}: ${videoId}`);
1501
1419
  }
1420
+ function getLastRecordFilePath(screenciDir) {
1421
+ return resolve(screenciDir, SCREENCI_LAST_RECORD_FILE);
1422
+ }
1423
+ /**
1424
+ * Persists the recordId of the just-completed `screenci record` upload so a
1425
+ * later `screenci info` can report exactly that run. Best-effort: a
1426
+ * failure to write must not fail the record command.
1427
+ */
1428
+ async function saveLastRecordId(screenciDir, recordId) {
1429
+ try {
1430
+ mkdirSync(screenciDir, { recursive: true });
1431
+ await writeFile(getLastRecordFilePath(screenciDir), `${JSON.stringify({ recordId, savedAt: new Date().toISOString() }, null, 2)}\n`);
1432
+ }
1433
+ catch (err) {
1434
+ logger.warn(`Failed to record run id for info: ${err instanceof Error ? err.message : String(err)}`);
1435
+ }
1436
+ }
1437
+ async function readLastRecordId(screenciDir) {
1438
+ try {
1439
+ const raw = await readFile(getLastRecordFilePath(screenciDir), 'utf-8');
1440
+ const parsed = JSON.parse(raw);
1441
+ return typeof parsed.recordId === 'string' ? parsed.recordId : null;
1442
+ }
1443
+ catch (err) {
1444
+ if (!isMissingFileError(err)) {
1445
+ logger.warn(`Ignoring invalid stored record at ${getLastRecordFilePath(screenciDir)}.`);
1446
+ }
1447
+ return null;
1448
+ }
1449
+ }
1450
+ // `screenci info` prints every project video and its public URLs as JSON. When
1451
+ // this machine has recorded a run (a recordId is stored in
1452
+ // .screenci/last-record.json), the backend also attaches, to the videos from
1453
+ // that run, a per-language `latestRecord` with render status and record-pinned
1454
+ // URLs. Without a local run, only the project-wide listing with `static` URLs is
1455
+ // returned. The server does the merge; the CLI just passes the recordId.
1456
+ async function printInfo(configPath) {
1457
+ const { resolvedConfigPath, screenciConfig, secret, apiUrl } = await requireScreenCISecret(configPath, {
1458
+ interactive: detectInteractiveSession(),
1459
+ });
1460
+ const screenciDir = resolve(dirname(resolvedConfigPath), '.screenci');
1461
+ const recordId = await readLastRecordId(screenciDir);
1462
+ const url = new URL(`${apiUrl}/cli/info`);
1463
+ url.searchParams.set('projectName', screenciConfig.projectName);
1464
+ if (recordId)
1465
+ url.searchParams.set('record', recordId);
1466
+ const res = await fetch(url.toString(), {
1467
+ headers: { 'X-ScreenCI-Secret': secret },
1468
+ });
1469
+ if (!res.ok) {
1470
+ const text = await res.text();
1471
+ throw new Error(`Failed to fetch info: ${res.status} ${text}${hint401(res.status, secret)}`);
1472
+ }
1473
+ const data = await res.json();
1474
+ process.stdout.write(`${JSON.stringify(data, null, 2)}\n`);
1475
+ }
1502
1476
  async function persistScreenCISecret(envFilePath, secret) {
1503
1477
  const nextLine = `SCREENCI_SECRET=${secret}`;
1504
1478
  try {
@@ -1525,57 +1499,6 @@ async function persistScreenCISecret(envFilePath, secret) {
1525
1499
  }
1526
1500
  await writeFile(envFilePath, `${nextLine}\n`);
1527
1501
  }
1528
- function getLinkSessionFilePath(projectDir) {
1529
- return resolve(projectDir, '.screenci', SCREENCI_LINK_SESSION_FILE);
1530
- }
1531
- async function readPersistedLinkSessionSpec(specPath) {
1532
- try {
1533
- const raw = await readFile(specPath, 'utf-8');
1534
- return JSON.parse(raw);
1535
- }
1536
- catch (error) {
1537
- if (!isMissingFileError(error)) {
1538
- logger.warn(`Ignoring invalid stored link session at ${specPath}.`);
1539
- rmSync(specPath, { force: true });
1540
- }
1541
- return null;
1542
- }
1543
- }
1544
- async function writePersistedLinkSessionSpec(specPath, spec) {
1545
- mkdirSync(dirname(specPath), { recursive: true });
1546
- await writeFile(specPath, `${JSON.stringify(spec, null, 2)}\n`);
1547
- }
1548
- function deletePersistedLinkSessionSpec(specPath) {
1549
- rmSync(specPath, { force: true });
1550
- }
1551
- function isStoredLinkSessionReusable(spec, options) {
1552
- return (spec.environment === options.environment &&
1553
- spec.envFilePath === options.envFilePath &&
1554
- spec.resolvedConfigPath === options.resolvedConfigPath &&
1555
- spec.expiresAt > new Date().toISOString());
1556
- }
1557
- async function createLinkSessionSpec(options) {
1558
- const response = await fetch(`${options.apiUrl}/cli-link/session`, {
1559
- method: 'POST',
1560
- });
1561
- if (!response.ok) {
1562
- const text = await response.text();
1563
- throw new Error(`Failed to create link session: ${response.status} ${text}`);
1564
- }
1565
- const body = (await response.json());
1566
- return {
1567
- token: body.token,
1568
- appUrl: `${options.appUrl}/cli-auth?session=${encodeURIComponent(body.token)}`,
1569
- pollUrl: `${options.apiUrl}/cli-link/session?token=${encodeURIComponent(body.token)}`,
1570
- createdAt: body.createdAt,
1571
- expiresAt: body.expiresAt,
1572
- environment: options.environment,
1573
- ...(options.resolvedConfigPath
1574
- ? { resolvedConfigPath: options.resolvedConfigPath }
1575
- : {}),
1576
- envFilePath: options.envFilePath,
1577
- };
1578
- }
1579
1502
  async function pollLinkSessionOnce(spec) {
1580
1503
  const response = await fetch(spec.pollUrl);
1581
1504
  const body = (await response.json());
@@ -1588,7 +1511,16 @@ async function pollLinkSessionOnce(spec) {
1588
1511
  }
1589
1512
  return { status };
1590
1513
  }
1591
- async function pollLinkSession(spec, pollIntervalMs = SCREENCI_LINK_SESSION_POLL_INTERVAL_MS) {
1514
+ function getPollAuthTimeoutMs() {
1515
+ const raw = process.env.SCREENCI_POLL_AUTH_TIMEOUT_MS;
1516
+ if (raw === undefined)
1517
+ return SCREENCI_LINK_SESSION_POLL_FLAG_TIMEOUT_MS;
1518
+ const parsed = Number(raw);
1519
+ return Number.isFinite(parsed) && parsed >= 0
1520
+ ? parsed
1521
+ : SCREENCI_LINK_SESSION_POLL_FLAG_TIMEOUT_MS;
1522
+ }
1523
+ async function pollLinkSession(spec, pollIntervalMs = SCREENCI_LINK_SESSION_POLL_INTERVAL_MS, deadlineEpochMs) {
1592
1524
  for (;;) {
1593
1525
  const result = await pollLinkSessionOnce(spec);
1594
1526
  if (result.status === 'completed' && result.secret) {
@@ -1599,6 +1531,11 @@ async function pollLinkSession(spec, pollIntervalMs = SCREENCI_LINK_SESSION_POLL
1599
1531
  result.status === 'invalid') {
1600
1532
  return result;
1601
1533
  }
1534
+ // Stop before sleeping again once the optional deadline has passed so
1535
+ // `--poll-auth` cannot block forever waiting for a human to sign in.
1536
+ if (deadlineEpochMs !== undefined && Date.now() >= deadlineEpochMs) {
1537
+ return { status: 'timed-out' };
1538
+ }
1602
1539
  await new Promise((resolveDelay) => setTimeout(resolveDelay, pollIntervalMs));
1603
1540
  }
1604
1541
  }
@@ -1672,14 +1609,24 @@ export async function ensureScreenciSecret(resolvedConfigPath, opts = {}) {
1672
1609
  // a slow cadence until the human finishes signing in, and continue
1673
1610
  // recording automatically once the secret lands. We re-print the link
1674
1611
  // on every (re)created session so the latest valid link is always
1675
- // visible, including after a stale session is recreated.
1612
+ // visible, including after a stale session is recreated. We do not wait
1613
+ // forever: after a default timeout we stop polling and exit cleanly so
1614
+ // an agent or CI step does not hang. The link stays valid for a rerun.
1615
+ const timeoutMs = getPollAuthTimeoutMs();
1616
+ const timeoutMinutes = Math.round(timeoutMs / 60_000);
1617
+ const deadlineEpochMs = Date.now() + timeoutMs;
1676
1618
  for (;;) {
1677
1619
  logger.info(`Sign-in required to record. Open this link to sign in:\n${pc.cyan(spec.appUrl)}\n` +
1678
- `Waiting for sign-in (checking every 5 seconds). Recording continues automatically once you finish.`);
1679
- const polled = await pollLinkSession(spec, SCREENCI_LINK_SESSION_POLL_FLAG_INTERVAL_MS);
1620
+ `Waiting for sign-in (checking every 5 seconds, up to ${timeoutMinutes} minutes). Recording continues automatically once you finish.`);
1621
+ const polled = await pollLinkSession(spec, SCREENCI_LINK_SESSION_POLL_FLAG_INTERVAL_MS, deadlineEpochMs);
1680
1622
  if (polled.status === 'completed' && polled.secret) {
1681
1623
  return await saveCompletedSecret(polled.secret);
1682
1624
  }
1625
+ if (polled.status === 'timed-out' || Date.now() >= deadlineEpochMs) {
1626
+ logger.info(`Timed out after ${timeoutMinutes} minutes waiting for sign-in. The link is still valid:\n${pc.cyan(spec.appUrl)}\n` +
1627
+ `After signing in, rerun ${pc.cyan(getSuggestedScreenciCommand('record'))} to continue, or ${pc.cyan(getSuggestedScreenciCommand('record', '--poll-auth'))} to wait again.`);
1628
+ return undefined;
1629
+ }
1683
1630
  deletePersistedLinkSessionSpec(specPath);
1684
1631
  spec = await ensureSpec();
1685
1632
  }
@@ -1721,7 +1668,7 @@ export async function main() {
1721
1668
  .command('record [playwrightArgs...]')
1722
1669
  .description('Record videos using Playwright')
1723
1670
  .option('-v, --verbose', 'verbose output')
1724
- .option('--poll-auth', 'wait for sign-in to complete (polling every 5s) instead of exiting, then continue recording')
1671
+ .option('--poll-auth', 'wait for sign-in to complete (polling every 5s, up to 5 minutes) instead of exiting, then continue recording')
1725
1672
  .allowUnknownOption(true)
1726
1673
  .action(async () => {
1727
1674
  const parsed = parseRecordCliArgs(getSubcommandArgv('record'));
@@ -1792,6 +1739,10 @@ export async function main() {
1792
1739
  throw err;
1793
1740
  }
1794
1741
  const { projectId, recordId, hadFailures, failedVideoNames, failedVideoMessages, studioNotices, plan, } = uploadResult;
1742
+ // Remember this run so `screenci info` can report exactly it.
1743
+ if (recordId !== null) {
1744
+ await saveLastRecordId(screenciDir, recordId);
1745
+ }
1795
1746
  if (recordId !== null && projectId !== null) {
1796
1747
  const recordUrl = `${appUrl}/record/${recordId}`;
1797
1748
  await writeGitHubProjectOutput(recordUrl);
@@ -1823,9 +1774,9 @@ export async function main() {
1823
1774
  logger.info(pc.cyan(formatStudioUrl(appUrl, projectId, notice.videoId)));
1824
1775
  }
1825
1776
  }
1826
- else if (notice.studio.appliedChanges.length > 0) {
1777
+ else if (notice.studio.applied) {
1827
1778
  logger.info('');
1828
- logger.info(`Selections were overridden in Studio for "${notice.videoName}": ${formatStudioChangeSummary(notice.studio.appliedChanges)}`);
1779
+ logger.info(`Studio configuration applied for "${notice.videoName}".`);
1829
1780
  }
1830
1781
  }
1831
1782
  if (hadFailures) {
@@ -1884,10 +1835,10 @@ export async function main() {
1884
1835
  });
1885
1836
  program
1886
1837
  .command('info')
1887
- .description('Print remote project info as JSON')
1838
+ .description("Print the latest record run's video URLs and render status as JSON")
1888
1839
  .option('-c, --config <path>', 'path to screenci.config.ts')
1889
1840
  .action(async (options) => {
1890
- await printProjectInfo(options['config']);
1841
+ await printInfo(options['config']);
1891
1842
  });
1892
1843
  program
1893
1844
  .command('make-public <id>')