screenci 0.0.74 → 0.0.76

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 (127) hide show
  1. package/dist/cli.d.ts +11 -0
  2. package/dist/cli.d.ts.map +1 -1
  3. package/dist/cli.js +150 -37
  4. package/dist/cli.js.map +1 -1
  5. package/dist/docs/manifest.d.ts +42 -11
  6. package/dist/docs/manifest.d.ts.map +1 -1
  7. package/dist/docs/manifest.js +14 -3
  8. package/dist/docs/manifest.js.map +1 -1
  9. package/dist/docs/video-sources/installation.screenci.js +7 -9
  10. package/dist/docs/video-sources/installation.screenci.js.map +1 -1
  11. package/dist/docs/video-sources/landing.screenci.js +6 -8
  12. package/dist/docs/video-sources/landing.screenci.js.map +1 -1
  13. package/dist/docs/video-sources/locator-highlight-animated.screenci.js +3 -4
  14. package/dist/docs/video-sources/locator-highlight-animated.screenci.js.map +1 -1
  15. package/dist/docs/video-sources/locator-highlight-still.screenci.js +3 -4
  16. package/dist/docs/video-sources/locator-highlight-still.screenci.js.map +1 -1
  17. package/dist/docs/video-sources/studio.screenci.js +8 -10
  18. package/dist/docs/video-sources/studio.screenci.js.map +1 -1
  19. package/dist/e2e/instrument.e2e.js +52 -1
  20. package/dist/e2e/instrument.e2e.js.map +1 -1
  21. package/dist/index.d.ts +4 -5
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +1 -2
  24. package/dist/index.js.map +1 -1
  25. package/dist/src/asset.d.ts +20 -1
  26. package/dist/src/asset.d.ts.map +1 -1
  27. package/dist/src/asset.js +58 -7
  28. package/dist/src/asset.js.map +1 -1
  29. package/dist/src/audio.d.ts +16 -0
  30. package/dist/src/audio.d.ts.map +1 -1
  31. package/dist/src/audio.js +78 -1
  32. package/dist/src/audio.js.map +1 -1
  33. package/dist/src/autoZoom.d.ts.map +1 -1
  34. package/dist/src/autoZoom.js +3 -1
  35. package/dist/src/autoZoom.js.map +1 -1
  36. package/dist/src/browserLaunchOptions.d.ts +24 -2
  37. package/dist/src/browserLaunchOptions.d.ts.map +1 -1
  38. package/dist/src/browserLaunchOptions.js +57 -3
  39. package/dist/src/browserLaunchOptions.js.map +1 -1
  40. package/dist/src/builder.d.ts +135 -142
  41. package/dist/src/builder.d.ts.map +1 -1
  42. package/dist/src/builder.js +218 -77
  43. package/dist/src/builder.js.map +1 -1
  44. package/dist/src/callerFile.d.ts +12 -0
  45. package/dist/src/callerFile.d.ts.map +1 -0
  46. package/dist/src/callerFile.js +46 -0
  47. package/dist/src/callerFile.js.map +1 -0
  48. package/dist/src/changeFocus.d.ts.map +1 -1
  49. package/dist/src/changeFocus.js +6 -2
  50. package/dist/src/changeFocus.js.map +1 -1
  51. package/dist/src/config.d.ts.map +1 -1
  52. package/dist/src/config.js +15 -0
  53. package/dist/src/config.js.map +1 -1
  54. package/dist/src/declare.d.ts +59 -0
  55. package/dist/src/declare.d.ts.map +1 -0
  56. package/dist/src/declare.js +93 -0
  57. package/dist/src/declare.js.map +1 -0
  58. package/dist/src/defaults.d.ts.map +1 -1
  59. package/dist/src/defaults.js +4 -3
  60. package/dist/src/defaults.js.map +1 -1
  61. package/dist/src/events.d.ts +51 -1
  62. package/dist/src/events.d.ts.map +1 -1
  63. package/dist/src/events.js +107 -15
  64. package/dist/src/events.js.map +1 -1
  65. package/dist/src/init.d.ts.map +1 -1
  66. package/dist/src/init.js +32 -30
  67. package/dist/src/init.js.map +1 -1
  68. package/dist/src/instrument.d.ts.map +1 -1
  69. package/dist/src/instrument.js +139 -1
  70. package/dist/src/instrument.js.map +1 -1
  71. package/dist/src/localize.d.ts +1 -0
  72. package/dist/src/localize.d.ts.map +1 -1
  73. package/dist/src/localize.js +1 -1
  74. package/dist/src/localize.js.map +1 -1
  75. package/dist/src/localizeRuntime.d.ts +17 -26
  76. package/dist/src/localizeRuntime.d.ts.map +1 -1
  77. package/dist/src/localizeRuntime.js +63 -48
  78. package/dist/src/localizeRuntime.js.map +1 -1
  79. package/dist/src/manualZoom.d.ts.map +1 -1
  80. package/dist/src/manualZoom.js +8 -5
  81. package/dist/src/manualZoom.js.map +1 -1
  82. package/dist/src/openBrowser.d.ts +34 -0
  83. package/dist/src/openBrowser.d.ts.map +1 -0
  84. package/dist/src/openBrowser.js +62 -0
  85. package/dist/src/openBrowser.js.map +1 -0
  86. package/dist/src/recordingData.d.ts +20 -0
  87. package/dist/src/recordingData.d.ts.map +1 -1
  88. package/dist/src/screenAudio.d.ts +58 -0
  89. package/dist/src/screenAudio.d.ts.map +1 -0
  90. package/dist/src/screenAudio.js +145 -0
  91. package/dist/src/screenAudio.js.map +1 -0
  92. package/dist/src/screenAudioSink.d.ts +34 -0
  93. package/dist/src/screenAudioSink.d.ts.map +1 -0
  94. package/dist/src/screenAudioSink.js +47 -0
  95. package/dist/src/screenAudioSink.js.map +1 -0
  96. package/dist/src/screenshot.d.ts +20 -24
  97. package/dist/src/screenshot.d.ts.map +1 -1
  98. package/dist/src/screenshot.js +34 -19
  99. package/dist/src/screenshot.js.map +1 -1
  100. package/dist/src/stillCapture.d.ts +5 -0
  101. package/dist/src/stillCapture.d.ts.map +1 -1
  102. package/dist/src/stillCapture.js +11 -1
  103. package/dist/src/stillCapture.js.map +1 -1
  104. package/dist/src/studio.d.ts +3 -59
  105. package/dist/src/studio.d.ts.map +1 -1
  106. package/dist/src/studio.js +1 -41
  107. package/dist/src/studio.js.map +1 -1
  108. package/dist/src/types.d.ts +133 -2
  109. package/dist/src/types.d.ts.map +1 -1
  110. package/dist/src/types.js.map +1 -1
  111. package/dist/src/video.d.ts +63 -52
  112. package/dist/src/video.d.ts.map +1 -1
  113. package/dist/src/video.js +143 -32
  114. package/dist/src/video.js.map +1 -1
  115. package/dist/src/zoom.d.ts +1 -0
  116. package/dist/src/zoom.d.ts.map +1 -1
  117. package/dist/src/zoom.js +3 -0
  118. package/dist/src/zoom.js.map +1 -1
  119. package/dist/tsconfig.tsbuildinfo +1 -1
  120. package/package.json +1 -1
  121. package/skills/screenci/SKILL.md +2 -2
  122. package/skills/screenci/references/init.md +4 -3
  123. package/skills/screenci/references/record.md +1 -1
  124. package/dist/docs/video-sources/public-urls.screenci.d.ts +0 -2
  125. package/dist/docs/video-sources/public-urls.screenci.d.ts.map +0 -1
  126. package/dist/docs/video-sources/public-urls.screenci.js +0 -41
  127. package/dist/docs/video-sources/public-urls.screenci.js.map +0 -1
package/dist/cli.d.ts CHANGED
@@ -32,6 +32,7 @@ type PreparedUploadAsset = {
32
32
  name?: string;
33
33
  fileBuffer?: Buffer;
34
34
  contentType?: string;
35
+ alwaysUpload?: boolean;
35
36
  };
36
37
  export type UploadStudioInfo = {
37
38
  held: true;
@@ -79,6 +80,16 @@ export declare function ensureScreenciSecret(resolvedConfigPath?: string, opts?:
79
80
  pollAuth?: boolean;
80
81
  noPollAuth?: boolean;
81
82
  }): Promise<string | undefined>;
83
+ /**
84
+ * `screenci login`: get a sign-in link in front of a human as early as
85
+ * possible. It reuses the link `init` (or a prior run) persisted, or creates a
86
+ * fresh one, tries to open it in the browser best-effort, and always prints it
87
+ * as a fallback. It does NOT wait for sign-in to finish: the agent can run this,
88
+ * keep authoring and `test`ing, then `record` (which blocks for sign-in) once
89
+ * the script is green. If sign-in already completed, it saves the secret and
90
+ * exits.
91
+ */
92
+ export declare function runLogin(configPath?: string): Promise<void>;
82
93
  export declare function main(): Promise<void>;
83
94
  export declare function parseRecordCliArgs(args: string[]): {
84
95
  configPath: string | undefined;
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../cli.ts"],"names":[],"mappings":"AA8BA,OAAO,KAAK,EACV,uBAAuB,EACvB,aAAa,EAEd,MAAM,iBAAiB,CAAA;AASxB,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;AAyBvE,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,CAIR;AAED,KAAK,OAAO,GAAG,MAAM,GAAG,SAAS,GAAG,UAAU,CAAA;AAmD9C,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,OAAO,EAAE,MAAM;CAI5B;AAsBD,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;AAoTD,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;AAED,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAM1D;AAkHD,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;AA8GD,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,SAAS,CAY9D;AAyND,wBAAsB,oBAAoB,CACxC,kBAAkB,CAAC,EAAE,MAAM,EAC3B,IAAI,GAAE;IACJ,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,UAAU,CAAC,EAAE,OAAO,CAAA;CAChB,GACL,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAoJ7B;AAED,wBAAsB,IAAI,kBAmYzB;AAQD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG;IAClD,UAAU,EAAE,MAAM,GAAG,SAAS,CAAA;IAC9B,OAAO,EAAE,OAAO,CAAA;IAChB,QAAQ,EAAE,OAAO,CAAA;IACjB,UAAU,EAAE,OAAO,CAAA;IACnB,MAAM,EAAE,OAAO,CAAA;IACf,SAAS,EAAE,MAAM,GAAG,SAAS,CAAA;IAC7B,SAAS,EAAE,MAAM,EAAE,CAAA;CACpB,CAwDA;AAkUD,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAchD"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../cli.ts"],"names":[],"mappings":"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"}
package/dist/cli.js CHANGED
@@ -14,6 +14,7 @@ import { SCREENCI_DISABLE_RECORDING_TIMINGS_ENV, SCREENCI_MOCK_RECORD_ENV, SCREE
14
14
  import { DEFAULT_RECORD_UPLOAD_POLICY } from './src/defaults.js';
15
15
  import { findDuplicateTitles, formatDuplicateTitlesMessage, } from './src/titleValidation.js';
16
16
  import { createLinkSessionSpec, deletePersistedLinkSessionSpec, getCliLinkSessionApiUrl, getDevBackendUrl, getDevFrontendUrl, getLinkSessionFilePath, getScreenCIEnvironment, isStoredLinkSessionReusable, readPersistedLinkSessionSpec, SCREENCI_LINK_SESSION_FILE, writePersistedLinkSessionSpec, } from './src/linkSession.js';
17
+ import { openUrlInBrowser } from './src/openBrowser.js';
17
18
  // Re-export the environment-aware URL helpers so existing importers (and tests)
18
19
  // can keep importing them from the CLI entrypoint.
19
20
  export { getCliLinkSessionApiUrl, getDevBackendUrl, getDevFrontendUrl };
@@ -214,6 +215,9 @@ function contentTypeForPath(filePath) {
214
215
  mp4: 'video/mp4',
215
216
  webm: 'video/webm',
216
217
  mp3: 'audio/mpeg',
218
+ wav: 'audio/wav',
219
+ m4a: 'audio/mp4',
220
+ aac: 'audio/aac',
217
221
  svg: 'image/svg+xml',
218
222
  };
219
223
  return contentTypeMap[ext] ?? 'application/octet-stream';
@@ -352,7 +356,7 @@ async function loadUploadCandidate(screenciDir, entry, verbose) {
352
356
  preparedUploadAssets,
353
357
  };
354
358
  }
355
- async function uploadRecordingCandidate(candidate, screenciDir, projectName, apiUrl, secret, elevenLabsApiKey, verbose, uploadAbort, progressReporter, progressIndex, recordId) {
359
+ async function uploadRecordingCandidate(candidate, screenciDir, projectName, apiUrl, secret, elevenLabsApiKey, verbose, uploadAbort, progressReporter, progressIndex, recordId, expectedScreenshotCount) {
356
360
  const { entry, videoName, data, preparedUploadAssets } = candidate;
357
361
  let projectId = null;
358
362
  let videoId = null;
@@ -395,6 +399,7 @@ async function uploadRecordingCandidate(candidate, screenciDir, projectName, api
395
399
  data,
396
400
  recordingHash,
397
401
  recordId,
402
+ ...(isScreenshot ? { expectedScreenshotCount } : {}),
398
403
  expectedAssets: preparedUploadAssets.map((asset) => ({
399
404
  fileHash: asset.fileHash,
400
405
  size: asset.size,
@@ -879,6 +884,27 @@ export async function collectUploadAssets(data, configDir) {
879
884
  });
880
885
  continue;
881
886
  }
887
+ if (event.type === 'audioStart') {
888
+ // Studio audio tracks have no local file.
889
+ if ('studio' in event && event.studio === true)
890
+ continue;
891
+ if (!event.fileHash || assets.has(`hash:${event.fileHash}`))
892
+ continue;
893
+ const resolvedFile = await readRecordingFile(event.path, configDir, sourceFilePath);
894
+ if (resolvedFile === null) {
895
+ logger.warn(`Audio file not found, skipping upload: ${event.path}`);
896
+ continue;
897
+ }
898
+ assets.set(`hash:${event.fileHash}`, {
899
+ fileHash: event.fileHash,
900
+ path: event.path,
901
+ size: resolvedFile.buffer.byteLength,
902
+ fileBuffer: resolvedFile.buffer,
903
+ contentType: contentTypeForPath(resolvedFile.resolvedPath),
904
+ ...(event.name === '__screen' && { alwaysUpload: true }),
905
+ });
906
+ continue;
907
+ }
882
908
  if (event.type === 'videoCueStart') {
883
909
  // Single-language: hash already computed during recording, use assetPath to read file
884
910
  if (typeof event.assetHash === 'string' &&
@@ -999,7 +1025,7 @@ function hint401(status, secret) {
999
1025
  if (status !== 401 || !secret)
1000
1026
  return '';
1001
1027
  const frontendUrl = getDevFrontendUrl();
1002
- return `\nThe secret may have been deleted or belongs to a different organisation. Check your secrets at ${frontendUrl}/secrets`;
1028
+ return `\nThe secret may have been deleted. Check your secrets at ${frontendUrl}/secrets`;
1003
1029
  }
1004
1030
  export function formatUploadStartFailureMessage(videoName, status, responseText, secret) {
1005
1031
  if (responseText.trim().length > 0) {
@@ -1053,49 +1079,53 @@ async function uploadAssets(assets, apiUrl, secret, recordingId, signal, throwIf
1053
1079
  for (const asset of assets) {
1054
1080
  throwIfAborted();
1055
1081
  try {
1056
- const checkRes = await withUploadRetry(() => fetch(`${apiUrl}/cli/upload/${recordingId}/asset/check`, {
1057
- method: 'POST',
1058
- headers: {
1059
- 'Content-Type': 'application/json',
1060
- 'X-ScreenCI-Secret': secret,
1061
- },
1062
- body: JSON.stringify({
1063
- fileHash: asset.fileHash,
1064
- contentType: asset.contentType,
1065
- size: asset.size,
1066
- path: asset.path,
1067
- ...(typeof asset.name === 'string' ? { name: asset.name } : {}),
1068
- }),
1069
- signal,
1070
- }), signal);
1071
- if (!checkRes.ok) {
1072
- const text = await checkRes.text();
1073
- throw new UploadAssetError(`Failed to check asset ${asset.path}: ${checkRes.status} ${text}${hint401(checkRes.status, secret)}`);
1074
- }
1075
- const checkBody = (await checkRes.json());
1076
- if (checkBody.exists) {
1077
- logInfo(`${pc.green('✔')} Asset already exists: ${displayAssetPath(asset.path)}`);
1078
- continue;
1082
+ if (!asset.alwaysUpload) {
1083
+ const checkRes = await withUploadRetry(() => fetch(`${apiUrl}/cli/upload/${recordingId}/asset/check`, {
1084
+ method: 'POST',
1085
+ headers: {
1086
+ 'Content-Type': 'application/json',
1087
+ 'X-ScreenCI-Secret': secret,
1088
+ },
1089
+ body: JSON.stringify({
1090
+ fileHash: asset.fileHash,
1091
+ contentType: asset.contentType,
1092
+ size: asset.size,
1093
+ path: asset.path,
1094
+ ...(typeof asset.name === 'string' ? { name: asset.name } : {}),
1095
+ }),
1096
+ signal,
1097
+ }), signal);
1098
+ if (!checkRes.ok) {
1099
+ const text = await checkRes.text();
1100
+ throw new UploadAssetError(`Failed to check asset ${asset.path}: ${checkRes.status} ${text}${hint401(checkRes.status, secret)}`);
1101
+ }
1102
+ const checkBody = (await checkRes.json());
1103
+ if (checkBody.exists) {
1104
+ logInfo(`${pc.green('✔')} Asset already exists: ${displayAssetPath(asset.path)}`);
1105
+ continue;
1106
+ }
1079
1107
  }
1080
1108
  if (!asset.fileBuffer || !asset.contentType) {
1081
1109
  throw new UploadAssetError(`Asset bytes not available for upload and backend does not have it yet: ${asset.path}`);
1082
1110
  }
1083
1111
  const fileBuffer = asset.fileBuffer;
1112
+ const contentType = asset.contentType;
1084
1113
  throwIfAborted();
1085
- const res = await withUploadRetry(() => fetch(`${apiUrl}/cli/upload/${recordingId}/asset`, {
1114
+ const res = await withUploadRetry(() =>
1115
+ // Send the raw bytes, not base64-in-JSON: base64 builds a string that
1116
+ // exceeds Node's max string length (~536MB) for large assets. Metadata
1117
+ // travels in headers (URI-encoded so non-ASCII paths stay header-safe).
1118
+ fetch(`${apiUrl}/cli/upload/${recordingId}/asset/stream`, {
1086
1119
  method: 'PUT',
1087
1120
  headers: {
1088
- 'Content-Type': 'application/json',
1121
+ 'Content-Type': contentType,
1122
+ 'Content-Length': String(fileBuffer.byteLength),
1089
1123
  'X-ScreenCI-Secret': secret,
1124
+ 'X-ScreenCI-File-Hash': asset.fileHash,
1125
+ 'X-ScreenCI-Asset-Size': String(asset.size),
1126
+ 'X-ScreenCI-Asset-Path': encodeURIComponent(asset.path),
1090
1127
  },
1091
- body: JSON.stringify({
1092
- fileHash: asset.fileHash,
1093
- fileBase64: fileBuffer.toString('base64'),
1094
- contentType: asset.contentType,
1095
- size: asset.size,
1096
- path: asset.path,
1097
- ...(typeof asset.name === 'string' ? { name: asset.name } : {}),
1098
- }),
1128
+ body: fileBuffer,
1099
1129
  signal,
1100
1130
  }), signal);
1101
1131
  if (!res.ok) {
@@ -1163,7 +1193,11 @@ export async function uploadRecordings(screenciDir, projectName, apiUrl, secret,
1163
1193
  };
1164
1194
  }
1165
1195
  const progressReporter = createUploadProgressReporter(candidates.map((candidate) => candidate.videoName), verbose);
1166
- const results = await Promise.all(candidates.map(async (candidate, index) => await uploadRecordingCandidate(candidate, screenciDir, projectName, apiUrl, secret, elevenLabsApiKey, verbose, uploadAbort, progressReporter, index, recordId)));
1196
+ // Screenshots from this run render together on one machine; the backend
1197
+ // waits for all of them to land before dispatching the batch, so it needs
1198
+ // to know how many to expect.
1199
+ const screenshotCount = candidates.filter((candidate) => candidate.data.output === 'screenshot').length;
1200
+ const results = await Promise.all(candidates.map(async (candidate, index) => await uploadRecordingCandidate(candidate, screenciDir, projectName, apiUrl, secret, elevenLabsApiKey, verbose, uploadAbort, progressReporter, index, recordId, screenshotCount)));
1167
1201
  firstProjectId =
1168
1202
  results.find((result) => result.projectId !== null)?.projectId ?? null;
1169
1203
  const resolvedPlan = results.find((result) => result.plan !== undefined)?.plan ?? null;
@@ -1757,10 +1791,82 @@ export async function ensureScreenciSecret(resolvedConfigPath, opts = {}) {
1757
1791
  return undefined;
1758
1792
  }
1759
1793
  }
1794
+ /**
1795
+ * `screenci login`: get a sign-in link in front of a human as early as
1796
+ * possible. It reuses the link `init` (or a prior run) persisted, or creates a
1797
+ * fresh one, tries to open it in the browser best-effort, and always prints it
1798
+ * as a fallback. It does NOT wait for sign-in to finish: the agent can run this,
1799
+ * keep authoring and `test`ing, then `record` (which blocks for sign-in) once
1800
+ * the script is green. If sign-in already completed, it saves the secret and
1801
+ * exits.
1802
+ */
1803
+ export async function runLogin(configPath) {
1804
+ const { resolvedConfigPath } = await loadScreenCIConfigAndEnv(configPath);
1805
+ if (process.env.SCREENCI_SECRET) {
1806
+ logger.info(`Already signed in (SCREENCI_SECRET is set). Run ${pc.cyan(getSuggestedScreenciCommand('record'))} to record.`);
1807
+ return;
1808
+ }
1809
+ const environment = getScreenCIEnvironment();
1810
+ const apiUrl = getCliLinkSessionApiUrl();
1811
+ const appUrl = getDevFrontendUrl();
1812
+ const envFilePath = await resolveProjectEnvFilePath(resolvedConfigPath);
1813
+ const projectDir = dirname(resolvedConfigPath);
1814
+ const specPath = getLinkSessionFilePath(projectDir);
1815
+ const linkSessionContext = { environment, envFilePath, resolvedConfigPath };
1816
+ const createSpec = async () => {
1817
+ const created = await createLinkSessionSpec({
1818
+ apiUrl,
1819
+ appUrl,
1820
+ ...linkSessionContext,
1821
+ });
1822
+ await writePersistedLinkSessionSpec(specPath, created);
1823
+ return created;
1824
+ };
1825
+ try {
1826
+ // Reuse a still-valid persisted session so login and a later record share
1827
+ // the same link; otherwise create and persist a fresh one.
1828
+ const stored = await readPersistedLinkSessionSpec(specPath);
1829
+ let spec = stored && isStoredLinkSessionReusable(stored, linkSessionContext)
1830
+ ? stored
1831
+ : await createSpec();
1832
+ // If sign-in already completed (e.g. between runs) save the secret and stop:
1833
+ // there is nothing left to open. Recreate a stale session before opening so
1834
+ // the link we hand off is valid.
1835
+ const result = await pollLinkSessionOnce(spec);
1836
+ if (result.status === 'completed' && result.secret) {
1837
+ process.env.SCREENCI_SECRET = result.secret;
1838
+ await persistScreenCISecret(envFilePath, result.secret);
1839
+ deletePersistedLinkSessionSpec(specPath);
1840
+ logger.info(`Already signed in. Saved SCREENCI_SECRET to ${envFilePath}.`);
1841
+ return;
1842
+ }
1843
+ if (result.status === 'expired' ||
1844
+ result.status === 'consumed' ||
1845
+ result.status === 'invalid') {
1846
+ deletePersistedLinkSessionSpec(specPath);
1847
+ spec = await createSpec();
1848
+ }
1849
+ const opened = await openUrlInBrowser(spec.appUrl);
1850
+ if (opened.opened) {
1851
+ logger.info(`Opening the sign-in link in your browser:\n${pc.cyan(spec.appUrl)}`);
1852
+ }
1853
+ else {
1854
+ logger.info(`Could not open a browser automatically (${opened.reason}). Open this link to sign in:\n${pc.cyan(spec.appUrl)}`);
1855
+ }
1856
+ logger.info(`The link stays valid for 24 hours. You can keep authoring and run ${pc.cyan(getSuggestedScreenciCommand('test'))} while you sign in. Then run ${pc.cyan(getSuggestedScreenciCommand('record'))}, which waits for sign-in and records once you finish.`);
1857
+ }
1858
+ catch (err) {
1859
+ // Login is a best-effort convenience: never fail the run on a network or
1860
+ // browser hiccup. record can still create and wait on the link later.
1861
+ const msg = err instanceof Error ? err.message : String(err);
1862
+ logger.warn(`Could not start sign-in: ${msg}`);
1863
+ logger.info(`You can sign in later: ${pc.cyan(getSuggestedScreenciCommand('record'))} prints a link and waits for sign-in. In CI, set SCREENCI_SECRET (from ${getScreenCISecretsUrl()}).`);
1864
+ }
1865
+ }
1760
1866
  export async function main() {
1761
1867
  if (process.argv.length <= 2) {
1762
1868
  logger.error('Error: No command provided');
1763
- logger.error('Available commands: record, test, info, make-public, make-private, init');
1869
+ logger.error('Available commands: record, test, login, info, make-public, make-private, init');
1764
1870
  process.exit(1);
1765
1871
  }
1766
1872
  const program = new Command();
@@ -1948,6 +2054,13 @@ export async function main() {
1948
2054
  const recordCommand = getSuggestedScreenciCommand('record');
1949
2055
  logger.info(`Tests passed. Run ${pc.cyan(recordCommand)} to render the videos.`);
1950
2056
  });
2057
+ program
2058
+ .command('login')
2059
+ .description('Open the sign-in link in your browser (best-effort) so recording can finish later')
2060
+ .option('-c, --config <path>', 'path to screenci.config.ts')
2061
+ .action(async (options) => {
2062
+ await runLogin(options['config']);
2063
+ });
1951
2064
  program
1952
2065
  .command('info')
1953
2066
  .description("Print the latest record run's video URLs and render status as JSON")