@reshotdev/screenshot 0.0.1-beta.0

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 (59) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +388 -0
  3. package/package.json +64 -0
  4. package/src/commands/auth.js +259 -0
  5. package/src/commands/chrome.js +140 -0
  6. package/src/commands/ci-run.js +123 -0
  7. package/src/commands/ci-setup.js +288 -0
  8. package/src/commands/drifts.js +423 -0
  9. package/src/commands/import-tests.js +309 -0
  10. package/src/commands/ingest.js +458 -0
  11. package/src/commands/init.js +633 -0
  12. package/src/commands/publish.js +1721 -0
  13. package/src/commands/pull.js +303 -0
  14. package/src/commands/record.js +94 -0
  15. package/src/commands/run.js +476 -0
  16. package/src/commands/setup-wizard.js +740 -0
  17. package/src/commands/setup.js +137 -0
  18. package/src/commands/status.js +275 -0
  19. package/src/commands/sync.js +621 -0
  20. package/src/commands/ui.js +248 -0
  21. package/src/commands/validate-docs.js +529 -0
  22. package/src/index.js +462 -0
  23. package/src/lib/api-client.js +815 -0
  24. package/src/lib/capture-engine.js +1623 -0
  25. package/src/lib/capture-script-runner.js +3120 -0
  26. package/src/lib/ci-detect.js +137 -0
  27. package/src/lib/config.js +1240 -0
  28. package/src/lib/diff-engine.js +642 -0
  29. package/src/lib/hash.js +74 -0
  30. package/src/lib/image-crop.js +396 -0
  31. package/src/lib/matrix.js +89 -0
  32. package/src/lib/output-path-template.js +318 -0
  33. package/src/lib/playwright-runner.js +252 -0
  34. package/src/lib/polished-clip.js +553 -0
  35. package/src/lib/privacy-engine.js +408 -0
  36. package/src/lib/progress-tracker.js +142 -0
  37. package/src/lib/record-browser-injection.js +654 -0
  38. package/src/lib/record-cdp.js +612 -0
  39. package/src/lib/record-clip.js +343 -0
  40. package/src/lib/record-config.js +623 -0
  41. package/src/lib/record-screenshot.js +360 -0
  42. package/src/lib/record-terminal.js +123 -0
  43. package/src/lib/recorder-service.js +781 -0
  44. package/src/lib/secrets.js +51 -0
  45. package/src/lib/selector-strategies.js +859 -0
  46. package/src/lib/standalone-mode.js +400 -0
  47. package/src/lib/storage-providers.js +569 -0
  48. package/src/lib/style-engine.js +684 -0
  49. package/src/lib/ui-api.js +4677 -0
  50. package/src/lib/ui-assets.js +373 -0
  51. package/src/lib/ui-executor.js +587 -0
  52. package/src/lib/variant-injector.js +591 -0
  53. package/src/lib/viewport-presets.js +454 -0
  54. package/src/lib/worker-pool.js +118 -0
  55. package/web/cropper/index.html +436 -0
  56. package/web/manager/dist/assets/index--ZgioErz.js +507 -0
  57. package/web/manager/dist/assets/index-n468W0Wr.css +1 -0
  58. package/web/manager/dist/index.html +27 -0
  59. package/web/subtitle-editor/index.html +295 -0
@@ -0,0 +1,633 @@
1
+ const inquirer = require("inquirer");
2
+ const chalk = require("chalk");
3
+ const fs = require("fs-extra");
4
+ const path = require("path");
5
+ const config = require("../lib/config");
6
+ const {
7
+ validateStorageConfig,
8
+ getStorageSetupHelp,
9
+ isPlatformAvailable,
10
+ } = require("../lib/storage-providers");
11
+ const {
12
+ initStandaloneMode,
13
+ printModeStatus,
14
+ getConfigDefaults,
15
+ } = require("../lib/standalone-mode");
16
+
17
+ /**
18
+ * Auto-detect documentation directories
19
+ */
20
+ function detectDocumentationRoot() {
21
+ const commonPaths = [
22
+ "docs",
23
+ "documentation",
24
+ "content",
25
+ "doc",
26
+ "guides",
27
+ ".vitepress/content",
28
+ ".docusaurus/docs",
29
+ ];
30
+
31
+ for (const dir of commonPaths) {
32
+ const fullPath = path.join(process.cwd(), dir);
33
+ if (fs.existsSync(fullPath) && fs.statSync(fullPath).isDirectory()) {
34
+ // Check if it has markdown files
35
+ const files = fs.readdirSync(fullPath);
36
+ if (files.some((f) => f.endsWith(".md") || f.endsWith(".mdx"))) {
37
+ return dir;
38
+ }
39
+ }
40
+ }
41
+
42
+ return null;
43
+ }
44
+
45
+ /**
46
+ * Generate documentation block configuration
47
+ */
48
+ function generateDocumentationConfig(root = "./docs", strategy = "git_pr") {
49
+ return {
50
+ root,
51
+ include: ["**/*.md", "**/*.mdx"],
52
+ exclude: ["**/_*.mdx", "**/node_modules/**", "**/.next/**"],
53
+ strategy,
54
+ assetFormat: "markdown",
55
+ mappings: {},
56
+ };
57
+ }
58
+
59
+ /**
60
+ * Scaffold a basic documentation structure
61
+ */
62
+ async function scaffoldDocumentation() {
63
+ const docsDir = path.join(process.cwd(), "docs");
64
+
65
+ // Create docs directory
66
+ fs.ensureDirSync(docsDir);
67
+
68
+ // Create README
69
+ const readmeContent = `# Documentation
70
+
71
+ This directory contains documentation for your project.
72
+
73
+ ## Getting Started
74
+
75
+ Documentation files are automatically synced with your codebase using Reshot DocSync.
76
+
77
+ ### Linking Docs to Visuals
78
+
79
+ Add frontmatter to your markdown files to link them to visual journeys:
80
+
81
+ \`\`\`markdown
82
+ ---
83
+ title: "Feature Name"
84
+ reshot_journey: "feature/workflow-name"
85
+ ---
86
+
87
+ # Feature Documentation
88
+
89
+ Your content here...
90
+ \`\`\`
91
+
92
+ ## Commands
93
+
94
+ - \`reshot validate\` - Validate documentation configuration
95
+ - \`reshot ingest\` - Upload traces and documentation
96
+ - \`reshot drifts\` - View and manage documentation drifts
97
+
98
+ For more information, visit: https://docs.reshot.dev
99
+ `;
100
+
101
+ fs.writeFileSync(path.join(docsDir, "README.md"), readmeContent);
102
+
103
+ // Create example doc
104
+ const exampleContent = `---
105
+ title: "Getting Started"
106
+ reshot_journey: "onboarding/first-steps"
107
+ description: "Quick start guide for new users"
108
+ ---
109
+
110
+ # Getting Started
111
+
112
+ Welcome! This is an example documentation file.
113
+
114
+ ## Steps
115
+
116
+ 1. **Step 1**: Description of first step
117
+ 2. **Step 2**: Description of second step
118
+ 3. **Step 3**: Description of third step
119
+
120
+ ## Next Steps
121
+
122
+ Continue to the next guide...
123
+ `;
124
+
125
+ fs.writeFileSync(path.join(docsDir, "getting-started.md"), exampleContent);
126
+
127
+ return "docs";
128
+ }
129
+
130
+ async function initCommand() {
131
+ console.log(chalk.cyan("šŸš€ Initializing Reshot...\n"));
132
+
133
+ // Check if we already have a docsync.config.json with BYOS storage config
134
+ let existingConfig = null;
135
+ let isBYOSMode = false;
136
+
137
+ if (config.configExists()) {
138
+ try {
139
+ existingConfig = config.readConfig();
140
+ if (
141
+ existingConfig.storage &&
142
+ existingConfig.storage.type &&
143
+ existingConfig.storage.type !== "reshot"
144
+ ) {
145
+ isBYOSMode = true;
146
+ }
147
+ } catch (error) {
148
+ // Config exists but couldn't be read - we'll handle this later
149
+ }
150
+ }
151
+
152
+ // Try to read CLI settings (from auth)
153
+ let settings = null;
154
+ try {
155
+ settings = config.readSettings();
156
+ } catch (readError) {
157
+ // No settings file - that's okay if we're in BYOS mode
158
+ settings = null;
159
+ }
160
+
161
+ const hasPlatformAuth = settings?.projectId && settings?.apiKey;
162
+
163
+ // If no auth and no existing BYOS config, prompt user for their choice
164
+ if (!hasPlatformAuth && !isBYOSMode) {
165
+ console.log(
166
+ chalk.yellow("⚠ No Reshot account linked. Choose how to proceed:\n")
167
+ );
168
+
169
+ const { mode } = await inquirer.prompt([
170
+ {
171
+ type: "list",
172
+ name: "mode",
173
+ message: "How would you like to use Reshot?",
174
+ choices: [
175
+ {
176
+ name: `${chalk.cyan(
177
+ "Platform Mode"
178
+ )} - Full features: CDN, approval workflows, changelogs (requires auth)`,
179
+ value: "platform",
180
+ },
181
+ {
182
+ name: `${chalk.green(
183
+ "Standalone Mode"
184
+ )} - Local-only: capture, diff, and output templating - no account needed`,
185
+ value: "standalone",
186
+ },
187
+ {
188
+ name: `${chalk.blue(
189
+ "BYOS Mode"
190
+ )} - Bring Your Own Storage (S3, R2, or local) - works standalone`,
191
+ value: "byos",
192
+ },
193
+ ],
194
+ },
195
+ ]);
196
+
197
+ if (mode === "platform") {
198
+ console.log(chalk.yellow("\n⚠ Platform mode requires authentication."));
199
+ console.log(
200
+ `Run ${chalk.bold("reshot auth")} first, then run ${chalk.bold(
201
+ "reshot init"
202
+ )} again.\n`
203
+ );
204
+ process.exit(1);
205
+ }
206
+
207
+ if (mode === "standalone") {
208
+ // Initialize standalone mode with minimal prompts
209
+ const { projectName } = await inquirer.prompt([
210
+ {
211
+ type: "input",
212
+ name: "projectName",
213
+ message: "Project name:",
214
+ default: require("path").basename(process.cwd()),
215
+ },
216
+ ]);
217
+
218
+ const { baseUrl } = await inquirer.prompt([
219
+ {
220
+ type: "input",
221
+ name: "baseUrl",
222
+ message: "Application URL to capture:",
223
+ default: "http://localhost:3000",
224
+ },
225
+ ]);
226
+
227
+ // Use standalone mode initialization
228
+ const standaloneConfig = initStandaloneMode({ projectName, force: true });
229
+
230
+ // Update baseUrl if different from default
231
+ if (baseUrl !== standaloneConfig.baseUrl) {
232
+ standaloneConfig.baseUrl = baseUrl;
233
+ config.writeConfig(standaloneConfig);
234
+ }
235
+
236
+ printModeStatus();
237
+
238
+ console.log(chalk.cyan("\nNext steps:"));
239
+ console.log(
240
+ ` 1. Run ${chalk.bold("reshot ui")} to launch the Reshot Studio`
241
+ );
242
+ console.log(` 2. Record your first scenario using the recorder`);
243
+ console.log(
244
+ ` 3. Run ${chalk.bold("reshot run")} to capture screenshots\n`
245
+ );
246
+ console.log(
247
+ chalk.gray(
248
+ "Tip: Configure output templates to control where files are saved."
249
+ )
250
+ );
251
+ console.log(
252
+ chalk.gray(
253
+ ' Example: output.template = "./docs/{{locale}}/{{name}}.png"\n'
254
+ )
255
+ );
256
+ return;
257
+ }
258
+
259
+ // BYOS mode - prompt for storage configuration
260
+ const { storageType } = await inquirer.prompt([
261
+ {
262
+ type: "list",
263
+ name: "storageType",
264
+ message: "Select your storage provider:",
265
+ choices: [
266
+ { name: "AWS S3", value: "s3" },
267
+ { name: "Cloudflare R2", value: "r2" },
268
+ { name: "Local filesystem", value: "local" },
269
+ ],
270
+ },
271
+ ]);
272
+
273
+ // Create a basic BYOS config with sane defaults
274
+ const byosConfig = {
275
+ $schema: "https://reshot.dev/schemas/docsync-config.json",
276
+ version: "2.0",
277
+ baseUrl: "http://localhost:3000",
278
+ assetDir: ".reshot/output",
279
+ viewport: { width: 1280, height: 720 },
280
+ timeout: 30000,
281
+ headless: true,
282
+ storage: {
283
+ type: storageType,
284
+ },
285
+ scenarios: [],
286
+ };
287
+
288
+ if (storageType === "s3") {
289
+ const answers = await inquirer.prompt([
290
+ {
291
+ type: "input",
292
+ name: "bucket",
293
+ message: "S3 bucket name:",
294
+ validate: (input) => input.length > 0 || "Bucket name is required",
295
+ },
296
+ {
297
+ type: "input",
298
+ name: "region",
299
+ message: "AWS region (e.g., us-east-1):",
300
+ default: "us-east-1",
301
+ },
302
+ {
303
+ type: "input",
304
+ name: "pathPrefix",
305
+ message: "Path prefix for assets (optional):",
306
+ default: "reshot-assets/",
307
+ },
308
+ ]);
309
+ byosConfig.storage = { type: "s3", ...answers };
310
+ } else if (storageType === "r2") {
311
+ const answers = await inquirer.prompt([
312
+ {
313
+ type: "input",
314
+ name: "bucket",
315
+ message: "R2 bucket name:",
316
+ validate: (input) => input.length > 0 || "Bucket name is required",
317
+ },
318
+ {
319
+ type: "input",
320
+ name: "accountId",
321
+ message: "Cloudflare Account ID:",
322
+ validate: (input) => input.length > 0 || "Account ID is required",
323
+ },
324
+ {
325
+ type: "input",
326
+ name: "pathPrefix",
327
+ message: "Path prefix for assets (optional):",
328
+ default: "reshot-assets/",
329
+ },
330
+ ]);
331
+ byosConfig.storage = { type: "r2", ...answers };
332
+ } else if (storageType === "local") {
333
+ const answers = await inquirer.prompt([
334
+ {
335
+ type: "input",
336
+ name: "outputDir",
337
+ message: "Local output directory:",
338
+ default: "./.reshot/published",
339
+ },
340
+ ]);
341
+ byosConfig.storage = { type: "local", ...answers };
342
+ }
343
+
344
+ // Validate the storage configuration
345
+ const validation = validateStorageConfig(byosConfig.storage);
346
+ if (!validation.valid) {
347
+ console.log(chalk.red("\nāŒ Storage configuration errors:"));
348
+ validation.errors.forEach((e) => console.log(chalk.red(` - ${e}`)));
349
+ console.log(getStorageSetupHelp(storageType));
350
+ process.exit(1);
351
+ }
352
+ if (validation.warnings.length > 0) {
353
+ console.log(chalk.yellow("\n⚠ Warnings:"));
354
+ validation.warnings.forEach((w) => console.log(chalk.yellow(` - ${w}`)));
355
+ }
356
+
357
+ // Save the config
358
+ config.writeConfig(byosConfig);
359
+
360
+ console.log(
361
+ chalk.green(
362
+ "\nāœ” Created docsync.config.json with BYOS storage configuration"
363
+ )
364
+ );
365
+ console.log(chalk.cyan("\nNext steps:"));
366
+ console.log(
367
+ ` 1. Set required environment variables for ${storageType.toUpperCase()} storage`
368
+ );
369
+ if (storageType === "s3") {
370
+ console.log(chalk.gray(' export AWS_ACCESS_KEY_ID="..."'));
371
+ console.log(chalk.gray(' export AWS_SECRET_ACCESS_KEY="..."'));
372
+ } else if (storageType === "r2") {
373
+ console.log(chalk.gray(' export R2_ACCESS_KEY_ID="..."'));
374
+ console.log(chalk.gray(' export R2_SECRET_ACCESS_KEY="..."'));
375
+ }
376
+ console.log(
377
+ ` 2. Run ${chalk.bold(
378
+ 'reshot record "My Visual"'
379
+ )} to capture your first flow`
380
+ );
381
+ console.log(
382
+ ` 3. Run ${chalk.bold("reshot publish")} to upload to your storage\n`
383
+ );
384
+ return;
385
+ }
386
+
387
+ // Platform mode with auth OR updating existing config
388
+ if (hasPlatformAuth) {
389
+ const { projectId, apiKey } = settings;
390
+
391
+ let overwrite = false;
392
+ if (config.configExists()) {
393
+ const answer = await inquirer.prompt([
394
+ {
395
+ type: "confirm",
396
+ name: "overwrite",
397
+ default: false,
398
+ message:
399
+ "docsync.config.json already exists. Overwrite it with the latest blueprint?",
400
+ },
401
+ ]);
402
+ overwrite = answer.overwrite;
403
+
404
+ if (!overwrite) {
405
+ console.log(chalk.yellow("⚠ Existing docsync.config.json preserved."));
406
+ return;
407
+ }
408
+ }
409
+
410
+ try {
411
+ const blueprint = await config.initializeProject(projectId, apiKey, {
412
+ overwrite,
413
+ });
414
+
415
+ // Auto-detect and configure documentation
416
+ console.log(chalk.cyan("\nšŸ“š Configuring documentation sync..."));
417
+
418
+ let docsRoot = detectDocumentationRoot();
419
+ let shouldScaffold = false;
420
+
421
+ if (!docsRoot) {
422
+ console.log(chalk.yellow(" No documentation directory detected."));
423
+
424
+ const { action } = await inquirer.prompt([
425
+ {
426
+ type: "list",
427
+ name: "action",
428
+ message: "Would you like to:",
429
+ choices: [
430
+ {
431
+ name: "Create a new docs/ directory with examples",
432
+ value: "scaffold",
433
+ },
434
+ { name: "Specify an existing directory", value: "specify" },
435
+ { name: "Skip documentation setup (add later)", value: "skip" },
436
+ ],
437
+ },
438
+ ]);
439
+
440
+ if (action === "scaffold") {
441
+ console.log(chalk.cyan(" Creating docs/ directory..."));
442
+ docsRoot = await scaffoldDocumentation();
443
+ shouldScaffold = true;
444
+ console.log(chalk.green(" āœ” Created docs/ with example files"));
445
+ } else if (action === "specify") {
446
+ const { customPath } = await inquirer.prompt([
447
+ {
448
+ type: "input",
449
+ name: "customPath",
450
+ message: "Documentation directory path:",
451
+ default: "./docs",
452
+ validate: (input) => {
453
+ const fullPath = path.join(process.cwd(), input);
454
+ if (!fs.existsSync(fullPath)) {
455
+ return `Directory ${input} does not exist. Create it first or choose scaffold option.`;
456
+ }
457
+ return true;
458
+ },
459
+ },
460
+ ]);
461
+ docsRoot = customPath;
462
+ }
463
+ } else {
464
+ console.log(chalk.green(` āœ” Detected documentation at: ${docsRoot}`));
465
+ }
466
+
467
+ // Add documentation block to config
468
+ if (docsRoot) {
469
+ // Detect if this is a GitHub repo for strategy recommendation
470
+ const isGitRepo = fs.existsSync(path.join(process.cwd(), ".git"));
471
+ const hasGitHubRemote =
472
+ isGitRepo &&
473
+ (() => {
474
+ try {
475
+ const { execSync } = require("child_process");
476
+ const remote = execSync("git remote get-url origin", {
477
+ encoding: "utf-8",
478
+ });
479
+ return remote.includes("github.com");
480
+ } catch {
481
+ return false;
482
+ }
483
+ })();
484
+
485
+ let strategy = "git_pr";
486
+
487
+ if (hasGitHubRemote) {
488
+ console.log(chalk.cyan(" āœ” Detected GitHub repository"));
489
+ const { useGitPR } = await inquirer.prompt([
490
+ {
491
+ type: "confirm",
492
+ name: "useGitPR",
493
+ message:
494
+ "Automatically create Pull Requests for documentation updates?",
495
+ default: true,
496
+ },
497
+ ]);
498
+ strategy = useGitPR ? "git_pr" : "external_host";
499
+ } else {
500
+ const { strategyChoice } = await inquirer.prompt([
501
+ {
502
+ type: "list",
503
+ name: "strategyChoice",
504
+ message: "How should documentation updates be delivered?",
505
+ choices: [
506
+ {
507
+ name: "Git Pull Requests (recommended for GitHub/GitLab repos)",
508
+ value: "git_pr",
509
+ },
510
+ {
511
+ name: "Notifications only (for external CMS like ReadMe, GitBook)",
512
+ value: "external_host",
513
+ },
514
+ ],
515
+ },
516
+ ]);
517
+ strategy = strategyChoice;
518
+ }
519
+
520
+ blueprint.documentation = generateDocumentationConfig(
521
+ docsRoot,
522
+ strategy
523
+ );
524
+
525
+ // Save updated config
526
+ config.writeConfig(blueprint);
527
+
528
+ console.log(chalk.green(" āœ” Documentation sync configured"));
529
+ console.log(chalk.gray(` Root: ${docsRoot}`));
530
+ console.log(chalk.gray(` Strategy: ${strategy}`));
531
+
532
+ if (shouldScaffold) {
533
+ console.log(chalk.cyan("\n šŸ“ Example files created:"));
534
+ console.log(chalk.gray(" - docs/README.md"));
535
+ console.log(chalk.gray(" - docs/getting-started.md"));
536
+ }
537
+ }
538
+
539
+ if (blueprint._metadata?.projectName) {
540
+ console.log(chalk.green("āœ” Pulled docsync.config.json from Reshot"));
541
+ } else {
542
+ console.log(
543
+ chalk.yellow(
544
+ "⚠ Unable to fetch existing blueprint from Reshot. Using boilerplate template instead."
545
+ )
546
+ );
547
+ }
548
+ console.log(chalk.green("āœ” Saved docsync.config.json"));
549
+
550
+ const updatedSettings = config.readSettings();
551
+ console.log("");
552
+ console.log(
553
+ chalk.cyan(
554
+ `✨ Reshot initialized for ${
555
+ updatedSettings.projectName || "your project"
556
+ } (${projectId})`
557
+ )
558
+ );
559
+ console.log("\nNext steps:");
560
+ console.log(
561
+ ` 1. Review ${chalk.bold(
562
+ "docsync.config.json"
563
+ )} and commit it to your repo.`
564
+ );
565
+ console.log(
566
+ ` ${chalk.gray(
567
+ "Note: Only the JSON file is committed – binaries stream via CLI + API."
568
+ )}`
569
+ );
570
+
571
+ if (blueprint.documentation) {
572
+ console.log(
573
+ ` 2. Add ${chalk.bold("reshot_journey")} frontmatter to your docs:`
574
+ );
575
+ console.log(
576
+ chalk.gray(
577
+ ' ---\n title: "Feature Name"\n reshot_journey: "feature/workflow"\n ---'
578
+ )
579
+ );
580
+ console.log(
581
+ ` 3. Run ${chalk.bold(
582
+ "reshot validate"
583
+ )} to check your documentation setup.`
584
+ );
585
+ console.log(
586
+ ` 4. Run Playwright tests to generate traces, then ${chalk.bold(
587
+ "reshot ingest"
588
+ )}`
589
+ );
590
+ console.log(
591
+ ` 5. Push your branch - Reshot will detect drift and create PRs.`
592
+ );
593
+ } else {
594
+ console.log(
595
+ ` 2. Run ${chalk.bold(
596
+ 'reshot record "My Visual"'
597
+ )} to capture your first flow.`
598
+ );
599
+ console.log(
600
+ ` 3. Push your branch and open a PR. Reshot will post visual changes as a PR comment.`
601
+ );
602
+ }
603
+ console.log("");
604
+ } catch (error) {
605
+ console.error(chalk.red("Failed to initialize:"), error.message);
606
+ process.exit(1);
607
+ }
608
+ } else if (isBYOSMode) {
609
+ // Existing BYOS config - just validate and show info
610
+ console.log(chalk.green("āœ” Found existing BYOS configuration"));
611
+ const validation = validateStorageConfig(existingConfig.storage);
612
+ if (!validation.valid) {
613
+ console.log(chalk.red("\nāŒ Storage configuration errors:"));
614
+ validation.errors.forEach((e) => console.log(chalk.red(` - ${e}`)));
615
+ console.log(getStorageSetupHelp(existingConfig.storage.type));
616
+ } else {
617
+ console.log(chalk.cyan(` Storage type: ${existingConfig.storage.type}`));
618
+ if (existingConfig.storage.bucket) {
619
+ console.log(chalk.cyan(` Bucket: ${existingConfig.storage.bucket}`));
620
+ }
621
+ console.log(chalk.green("\nāœ” Configuration is valid"));
622
+ console.log(chalk.cyan("\nNext steps:"));
623
+ console.log(
624
+ ` 1. Run ${chalk.bold('reshot record "My Visual"')} to capture a flow`
625
+ );
626
+ console.log(
627
+ ` 2. Run ${chalk.bold("reshot publish")} to upload to your storage\n`
628
+ );
629
+ }
630
+ }
631
+ }
632
+
633
+ module.exports = initCommand;