@vizzly-testing/cli 0.39.0 → 0.39.2

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 (195) hide show
  1. package/README.md +12 -3
  2. package/bin/vizzly.js +1 -1
  3. package/dist/{cli.js → adapters/cli/cli.js} +50 -35
  4. package/dist/adapters/cli/setup.js +34 -0
  5. package/dist/{client → adapters/client}/index.js +6 -7
  6. package/dist/adapters/http/cloud-proxy.js +29 -0
  7. package/dist/{server → adapters/http}/http-server.js +12 -13
  8. package/dist/{server-manager/operations.js → adapters/http/server-lifecycle.js} +10 -13
  9. package/dist/{server-manager/index.js → adapters/http/server-manager.js} +23 -27
  10. package/dist/{plugin-api.js → adapters/plugins/plugin-api.js} +1 -1
  11. package/dist/{plugin-loader.js → adapters/plugins/plugin-loader.js} +1 -1
  12. package/dist/{services/index.js → adapters/plugins/services.js} +4 -9
  13. package/dist/adapters/sdk/index.d.ts +89 -0
  14. package/dist/{sdk → adapters/sdk}/index.js +22 -19
  15. package/dist/{commands/api.js → domains/api/api.commands.js} +3 -3
  16. package/dist/{auth/client.js → domains/auth/auth-client.js} +4 -3
  17. package/dist/domains/auth/auth-errors.js +50 -0
  18. package/dist/domains/auth/auth-headers.js +41 -0
  19. package/dist/domains/auth/auth.normalizers.js +50 -0
  20. package/dist/{server/routers/auth.js → domains/auth/auth.routes.js} +9 -9
  21. package/dist/domains/auth/auth.rules.js +19 -0
  22. package/dist/domains/auth/auth.serializers.js +32 -0
  23. package/dist/{auth/operations.js → domains/auth/auth.service.js} +115 -17
  24. package/dist/domains/auth/auth.validations.js +21 -0
  25. package/dist/{api/client.js → domains/auth/cloud-client.js} +11 -18
  26. package/dist/{commands/login.js → domains/auth/login.commands.js} +11 -8
  27. package/dist/{commands/logout.js → domains/auth/logout.commands.js} +6 -4
  28. package/dist/domains/auth/token-store.js +81 -0
  29. package/dist/{commands/whoami.js → domains/auth/whoami.commands.js} +6 -3
  30. package/dist/domains/baselines/baseline-download.js +192 -0
  31. package/dist/domains/baselines/baseline-write.js +22 -0
  32. package/dist/{server/routers/baseline.js → domains/baselines/baseline.routes.js} +3 -3
  33. package/dist/{tdd/services/baseline-manager.js → domains/baselines/baseline.service.js} +1 -39
  34. package/dist/{commands/baselines.js → domains/baselines/baselines.commands.js} +3 -3
  35. package/dist/domains/baselines/cloud-baselines.js +9 -0
  36. package/dist/domains/baselines/cloud-baselines.routes.js +42 -0
  37. package/dist/domains/builds/build.normalizers.js +35 -0
  38. package/dist/domains/builds/build.serializers.js +164 -0
  39. package/dist/domains/builds/build.service.js +119 -0
  40. package/dist/domains/builds/build.validations.js +39 -0
  41. package/dist/{commands/builds.js → domains/builds/builds.commands.js} +6 -74
  42. package/dist/domains/builds/cloud-builds.routes.js +31 -0
  43. package/dist/{commands/finalize.js → domains/builds/finalize.commands.js} +5 -17
  44. package/dist/{commands/status.js → domains/builds/status.commands.js} +9 -120
  45. package/dist/domains/comparisons/cloud-comparisons.js +43 -0
  46. package/dist/{tdd/services/comparison-service.js → domains/comparisons/comparison.service.js} +1 -1
  47. package/dist/{commands/comparisons.js → domains/comparisons/comparisons.commands.js} +7 -5
  48. package/dist/domains/config/config-defaults.js +27 -0
  49. package/dist/{utils → domains/config}/config-discovery.js +1 -9
  50. package/dist/{utils → domains/config}/config-loader.js +10 -9
  51. package/dist/{commands/config-cmd.js → domains/config/config.commands.js} +4 -4
  52. package/dist/{types → domains/config}/config.d.ts +3 -3
  53. package/dist/domains/config/config.normalizers.js +126 -0
  54. package/dist/{server/routers/config.js → domains/config/config.routes.js} +13 -13
  55. package/dist/domains/config/config.rules.js +47 -0
  56. package/dist/domains/config/config.serializers.js +202 -0
  57. package/dist/domains/config/config.service.js +625 -0
  58. package/dist/{utils/config-schema.js → domains/config/config.validations.js} +9 -9
  59. package/dist/{utils/environment-config.js → domains/config/environment.js} +1 -8
  60. package/dist/domains/context/compact-context.validations.js +60 -0
  61. package/dist/domains/context/context-presentation.js +368 -0
  62. package/dist/domains/context/context.commands.js +385 -0
  63. package/dist/domains/context/context.serializers.js +157 -0
  64. package/dist/domains/context/context.service.js +76 -0
  65. package/dist/domains/context/context.validations.js +100 -0
  66. package/dist/{context → domains/context}/local-workspace-provider.js +1 -1
  67. package/dist/{commands/doctor.js → domains/diagnostics/doctor.commands.js} +6 -5
  68. package/dist/{server/routers/health.js → domains/diagnostics/health.routes.js} +11 -20
  69. package/dist/{commands/preview.js → domains/previews/preview.commands.js} +9 -7
  70. package/dist/domains/previews/preview.service.js +37 -0
  71. package/dist/domains/projects/cloud-projects.routes.js +28 -0
  72. package/dist/{commands/orgs.js → domains/projects/orgs.commands.js} +6 -8
  73. package/dist/{utils → domains/projects}/project-binding.js +3 -3
  74. package/dist/domains/projects/project-errors.js +61 -0
  75. package/dist/{utils → domains/projects}/project-link-store.js +2 -2
  76. package/dist/domains/projects/project-requests.js +61 -0
  77. package/dist/{commands/project.js → domains/projects/project.commands.js} +9 -41
  78. package/dist/domains/projects/project.normalizers.js +69 -0
  79. package/dist/{project/operations.js → domains/projects/project.service.js} +89 -16
  80. package/dist/domains/projects/project.validations.js +31 -0
  81. package/dist/{commands/projects.js → domains/projects/projects.commands.js} +6 -8
  82. package/dist/{server/routers/projects.js → domains/projects/projects.routes.js} +5 -5
  83. package/dist/domains/reports/dashboard.routes.js +96 -0
  84. package/dist/{server/routers/events.js → domains/reports/events.routes.js} +14 -36
  85. package/dist/domains/reports/report-assets.routes.js +81 -0
  86. package/dist/domains/reports/report-state.js +51 -0
  87. package/dist/domains/reports/report-store.js +85 -0
  88. package/dist/domains/reports/report.serializers.js +99 -0
  89. package/dist/{services/static-report-generator.js → domains/reports/report.service.js} +11 -33
  90. package/dist/{commands/review.js → domains/review/review.commands.js} +4 -4
  91. package/dist/domains/runs/run-environment.js +69 -0
  92. package/dist/{services/test-runner.js → domains/runs/run-events.js} +11 -9
  93. package/dist/{commands/run.js → domains/runs/run.commands.js} +16 -13
  94. package/dist/domains/runs/run.normalizers.js +8 -0
  95. package/dist/domains/runs/run.rules.js +56 -0
  96. package/dist/domains/runs/run.serializers.js +101 -0
  97. package/dist/{test-runner/operations.js → domains/runs/run.service.js} +35 -7
  98. package/dist/domains/runs/run.validations.js +17 -0
  99. package/dist/{utils/session.js → domains/runs/session-store.js} +3 -2
  100. package/dist/{server/handlers/api-handler.js → domains/screenshots/cloud-handler.js} +4 -4
  101. package/dist/domains/screenshots/cloud-screenshots.js +109 -0
  102. package/dist/{utils/file-helpers.js → domains/screenshots/image-files.js} +1 -1
  103. package/dist/domains/screenshots/screenshot-hash.js +53 -0
  104. package/dist/domains/screenshots/screenshot-server-options.js +28 -0
  105. package/dist/{screenshot-server/operations.js → domains/screenshots/screenshot-server.js} +14 -1
  106. package/dist/domains/screenshots/screenshot.normalizers.js +40 -0
  107. package/dist/{server/routers/screenshot.js → domains/screenshots/screenshot.routes.js} +4 -4
  108. package/dist/domains/screenshots/screenshot.serializers.js +146 -0
  109. package/dist/{utils/security.js → domains/screenshots/screenshot.validations.js} +20 -70
  110. package/dist/{init/agents.js → domains/setup/agent-setup.js} +54 -14
  111. package/dist/{commands/init.js → domains/setup/init.commands.js} +11 -44
  112. package/dist/{server/handlers/tdd-handler.js → domains/tdd/screenshot-handler.js} +46 -281
  113. package/dist/{commands/tdd-daemon.js → domains/tdd/tdd-daemon.commands.js} +6 -6
  114. package/dist/{commands/tdd-screenshots.js → domains/tdd/tdd-screenshots.commands.js} +5 -5
  115. package/dist/{commands/tdd.js → domains/tdd/tdd.commands.js} +16 -15
  116. package/dist/{tdd/tdd-service.js → domains/tdd/tdd.service.js} +81 -405
  117. package/dist/domains/updates/cli-update.js +103 -0
  118. package/dist/domains/updates/update-check.js +2 -0
  119. package/dist/domains/updates/update-notifications.js +175 -0
  120. package/dist/domains/uploads/upload-defaults.js +4 -0
  121. package/dist/domains/uploads/upload-files.js +32 -0
  122. package/dist/domains/uploads/upload-settings.js +21 -0
  123. package/dist/domains/uploads/upload-timing.js +18 -0
  124. package/dist/{commands/upload.js → domains/uploads/upload.commands.js} +11 -9
  125. package/dist/domains/uploads/upload.normalizers.js +60 -0
  126. package/dist/domains/uploads/upload.serializers.js +179 -0
  127. package/dist/{uploader/operations.js → domains/uploads/upload.service.js} +85 -28
  128. package/dist/domains/uploads/upload.validations.js +77 -0
  129. package/dist/index.js +12 -12
  130. package/dist/reporter/reporter-bundle.iife.js +86 -86
  131. package/dist/reporter-ssr/ssr-entry.js +3 -6
  132. package/dist/shared/cloud-protocol.js +171 -0
  133. package/dist/{utils → shared}/file-changes.js +1 -1
  134. package/dist/shared/files.js +9 -0
  135. package/dist/{utils → shared}/global-config.js +7 -78
  136. package/dist/shared/log-level.js +7 -0
  137. package/dist/{utils → shared}/output.js +1 -1
  138. package/dist/{utils → shared}/package-info.js +5 -4
  139. package/dist/shared/paths.js +57 -0
  140. package/package.json +37 -36
  141. package/dist/api/core.js +0 -382
  142. package/dist/api/endpoints.js +0 -421
  143. package/dist/api/index.js +0 -19
  144. package/dist/auth/core.js +0 -237
  145. package/dist/auth/index.js +0 -31
  146. package/dist/commands/context.js +0 -1066
  147. package/dist/config/core.js +0 -439
  148. package/dist/config/operations.js +0 -327
  149. package/dist/project/core.js +0 -211
  150. package/dist/screenshot-server/core.js +0 -161
  151. package/dist/screenshot-server/index.js +0 -11
  152. package/dist/server/routers/assets.js +0 -95
  153. package/dist/server/routers/cloud-proxy.js +0 -146
  154. package/dist/server/routers/dashboard.js +0 -172
  155. package/dist/services/auth-service.js +0 -117
  156. package/dist/services/config-service.js +0 -313
  157. package/dist/services/project-service.js +0 -88
  158. package/dist/test-runner/core.js +0 -293
  159. package/dist/test-runner/index.js +0 -13
  160. package/dist/types/sdk.d.ts +0 -186
  161. package/dist/uploader/core.js +0 -432
  162. package/dist/uploader/index.js +0 -88
  163. /package/dist/{types/client.d.ts → adapters/client/index.d.ts} +0 -0
  164. /package/dist/{server → adapters/http}/middleware/cors.js +0 -0
  165. /package/dist/{server → adapters/http}/middleware/json-parser.js +0 -0
  166. /package/dist/{server → adapters/http}/middleware/response.js +0 -0
  167. /package/dist/{server-manager/core.js → adapters/http/server-options.js} +0 -0
  168. /package/dist/{utils/login-poll-wait.js → domains/auth/login-wait.js} +0 -0
  169. /package/dist/{tdd/metadata → domains/baselines}/baseline-metadata.js +0 -0
  170. /package/dist/{services/build-manager.js → domains/builds/build-info.js} +0 -0
  171. /package/dist/{tdd/services/result-service.js → domains/comparisons/comparison-results.js} +0 -0
  172. /package/dist/{init/core.js → domains/config/config-plan.js} +0 -0
  173. /package/dist/{utils/config-helpers.js → domains/config/config.js} +0 -0
  174. /package/dist/{utils/visual-context-normalizers.js → domains/context/context.normalizers.js} +0 -0
  175. /package/dist/{context → domains/context}/provider-resolver.js +0 -0
  176. /package/dist/{utils/context.js → domains/diagnostics/environment-context.js} +0 -0
  177. /package/dist/{utils/report-data.js → domains/reports/report.normalizers.js} +0 -0
  178. /package/dist/{utils/image-input-detector.js → domains/screenshots/image-input.js} +0 -0
  179. /package/dist/{tdd/core/signature.js → domains/screenshots/screenshot-identity.js} +0 -0
  180. /package/dist/{utils → domains/screenshots}/screenshot-options.js +0 -0
  181. /package/dist/{init/prompts.js → domains/setup/setup-prompts.js} +0 -0
  182. /package/dist/{tdd → domains/tdd}/runtime-context.js +0 -0
  183. /package/dist/{tdd → domains/tdd}/server-registry.js +0 -0
  184. /package/dist/{commands/tdd-daemon-process.js → domains/tdd/tdd-daemon-process.commands.js} +0 -0
  185. /package/dist/{types/index.d.ts → index.d.ts} +0 -0
  186. /package/dist/{utils → shared}/api-url.js +0 -0
  187. /package/dist/{utils → shared}/async-utils.js +0 -0
  188. /package/dist/{utils → shared}/browser.js +0 -0
  189. /package/dist/{utils → shared}/ci-env.js +0 -0
  190. /package/dist/{utils → shared}/colors.js +0 -0
  191. /package/dist/{utils → shared}/fetch-utils.js +0 -0
  192. /package/dist/{utils → shared}/git.js +0 -0
  193. /package/dist/{utils → shared}/patterns.js +0 -0
  194. /package/dist/{utils → shared}/secret-store.js +0 -0
  195. /package/dist/{errors → shared}/vizzly-error.js +0 -0
package/README.md CHANGED
@@ -168,9 +168,9 @@ Install the Vizzly skill and a short `AGENTS.md` note:
168
168
  pnpm exec vizzly init --agent-guidance
169
169
  ```
170
170
 
171
- This adds `.agents/skills/vizzly` and a note in `AGENTS.md`, keeping other
172
- guidance. Use `--agent-skill` to install only the skill during setup.
173
- These flags don't open the wizard or update an existing Vizzly setup.
171
+ This adds `.agents/skills/vizzly` and a note in `AGENTS.md`, keeping your
172
+ other instructions. Use `--agent-skill` for just the skill. Run either command
173
+ again to refresh it without changing your config.
174
174
  `--skip-agent-skill` skips agent setup unless you also request `--agent-guidance`.
175
175
 
176
176
  Use `context` to read local results. For cloud results, check the API schema
@@ -298,9 +298,18 @@ with `pnpm exec` when Vizzly is installed locally.
298
298
  Installed plugins add commands such as `storybook`, `static-site`, and `previews`.
299
299
  See their setup guides above for details.
300
300
 
301
+ ## Contributing
302
+
303
+ Start in `src/domains/<domain>/` for CLI behavior or
304
+ `src/reporter/src/domains/<domain>/` for reporter UI. The
305
+ [architecture guide](docs/architecture.md) explains ownership, file roles, and
306
+ the shared runtime boundaries. Run `pnpm run check:architecture` when changing
307
+ source organization or imports.
308
+
301
309
  ## More
302
310
 
303
311
  [Full docs](https://docs.vizzly.dev) ·
312
+ [Updating Vizzly](docs/updates.md) ·
304
313
  [Report a bug or suggest a change](https://github.com/vizzly-testing/cli/issues)
305
314
 
306
315
  PRs welcome. MIT licensed.
package/bin/vizzly.js CHANGED
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import '../dist/cli.js';
3
+ import '../dist/adapters/cli/cli.js';
@@ -2,39 +2,45 @@
2
2
  import 'dotenv/config';
3
3
  import { existsSync, statSync } from 'node:fs';
4
4
  import { Option, program } from 'commander';
5
- import { apiCommand, apiSchemaCommand, validateApiOptions } from './commands/api.js';
6
- import { baselinesCommand } from './commands/baselines.js';
7
- import { buildsCommand, validateBuildsOptions } from './commands/builds.js';
8
- import { comparisonsCommand, validateComparisonsOptions } from './commands/comparisons.js';
9
- import { configCommand } from './commands/config-cmd.js';
10
- import { contextBuildCommand, contextComparisonCommand, contextReviewQueueCommand, contextScreenshotCommand, contextSimilarCommand, validateContextBuildOptions, validateContextComparisonOptions, validateContextReviewQueueOptions, validateContextScreenshotOptions, validateContextSimilarOptions } from './commands/context.js';
11
- import { doctorCommand } from './commands/doctor.js';
12
- import { finalizeCommand, validateFinalizeOptions } from './commands/finalize.js';
13
- import { init } from './commands/init.js';
14
- import { loginCommand } from './commands/login.js';
15
- import { logoutCommand } from './commands/logout.js';
16
- import { orgsCommand } from './commands/orgs.js';
17
- import { previewCommand, validatePreviewOptions } from './commands/preview.js';
18
- import { projectLinkCommand, validateProjectLinkOptions } from './commands/project.js';
19
- import { projectsCommand, validateProjectsOptions } from './commands/projects.js';
20
- import { approveCommand, commentCommand, rejectCommand, validateApproveOptions, validateCommentOptions, validateRejectOptions } from './commands/review.js';
21
- import { runCommand, validateRunOptions } from './commands/run.js';
22
- import { statusCommand, validateStatusOptions } from './commands/status.js';
23
- import { tddCommand, validateTddOptions } from './commands/tdd.js';
24
- import { runDaemonChild, tddListCommand, tddStartCommand, tddStatusCommand, tddStopCommand, validateTddStartOptions } from './commands/tdd-daemon.js';
25
- import { acceptTddScreenshot, latestTddScreenshot, listTddScreenshots, showTddScreenshot } from './commands/tdd-screenshots.js';
26
- import { uploadCommand, validateUploadOptions } from './commands/upload.js';
27
- import { whoamiCommand } from './commands/whoami.js';
28
- import { createPluginServices } from './plugin-api.js';
29
- import { loadPlugins } from './plugin-loader.js';
30
- import { createServices } from './services/index.js';
31
- import { withTimeout } from './utils/async-utils.js';
32
- import { colors } from './utils/colors.js';
33
- import { loadConfig } from './utils/config-loader.js';
34
- import { getContext } from './utils/context.js';
35
- import { saveUserPath } from './utils/global-config.js';
36
- import * as output from './utils/output.js';
37
- import { getPackageVersion } from './utils/package-info.js';
5
+ import { apiCommand, apiSchemaCommand, validateApiOptions } from '../../domains/api/api.commands.js';
6
+ import { loginCommand } from '../../domains/auth/login.commands.js';
7
+ import { logoutCommand } from '../../domains/auth/logout.commands.js';
8
+ import { whoamiCommand } from '../../domains/auth/whoami.commands.js';
9
+ import { baselinesCommand } from '../../domains/baselines/baselines.commands.js';
10
+ import { validateBuildsOptions, validateFinalizeOptions, validateStatusOptions } from '../../domains/builds/build.validations.js';
11
+ import { buildsCommand } from '../../domains/builds/builds.commands.js';
12
+ import { finalizeCommand } from '../../domains/builds/finalize.commands.js';
13
+ import { statusCommand } from '../../domains/builds/status.commands.js';
14
+ import { comparisonsCommand, validateComparisonsOptions } from '../../domains/comparisons/comparisons.commands.js';
15
+ import { configCommand } from '../../domains/config/config.commands.js';
16
+ import { loadConfig } from '../../domains/config/config-loader.js';
17
+ import { contextBuildCommand, contextComparisonCommand, contextReviewQueueCommand, contextScreenshotCommand, contextSimilarCommand } from '../../domains/context/context.commands.js';
18
+ import { validateContextBuildOptions, validateContextComparisonOptions, validateContextReviewQueueOptions, validateContextScreenshotOptions, validateContextSimilarOptions } from '../../domains/context/context.validations.js';
19
+ import { doctorCommand } from '../../domains/diagnostics/doctor.commands.js';
20
+ import { getContext } from '../../domains/diagnostics/environment-context.js';
21
+ import { previewCommand, validatePreviewOptions } from '../../domains/previews/preview.commands.js';
22
+ import { orgsCommand } from '../../domains/projects/orgs.commands.js';
23
+ import { projectLinkCommand } from '../../domains/projects/project.commands.js';
24
+ import { validateProjectLinkOptions } from '../../domains/projects/project.validations.js';
25
+ import { projectsCommand, validateProjectsOptions } from '../../domains/projects/projects.commands.js';
26
+ import { approveCommand, commentCommand, rejectCommand, validateApproveOptions, validateCommentOptions, validateRejectOptions } from '../../domains/review/review.commands.js';
27
+ import { runCommand, validateRunOptions } from '../../domains/runs/run.commands.js';
28
+ import { init } from '../../domains/setup/init.commands.js';
29
+ import { tddCommand, validateTddOptions } from '../../domains/tdd/tdd.commands.js';
30
+ import { runDaemonChild, tddListCommand, tddStartCommand, tddStatusCommand, tddStopCommand, validateTddStartOptions } from '../../domains/tdd/tdd-daemon.commands.js';
31
+ import { acceptTddScreenshot, latestTddScreenshot, listTddScreenshots, showTddScreenshot } from '../../domains/tdd/tdd-screenshots.commands.js';
32
+ import { runUpdatedCli } from '../../domains/updates/cli-update.js';
33
+ import { notifyUpdates } from '../../domains/updates/update-notifications.js';
34
+ import { uploadCommand, validateUploadOptions } from '../../domains/uploads/upload.commands.js';
35
+ import { withTimeout } from '../../shared/async-utils.js';
36
+ import { colors } from '../../shared/colors.js';
37
+ import { saveUserPath } from '../../shared/global-config.js';
38
+ import * as output from '../../shared/output.js';
39
+ import { getPackageVersion } from '../../shared/package-info.js';
40
+ import { createPluginServices } from '../plugins/plugin-api.js';
41
+ import { loadPlugins } from '../plugins/plugin-loader.js';
42
+ import { createServices } from '../plugins/services.js';
43
+ import { connectProject } from './setup.js';
38
44
 
39
45
  // Custom help formatting with BearDen design system
40
46
  const formatHelp = (cmd, helper) => {
@@ -393,7 +399,7 @@ function writeCommanderError(text) {
393
399
  }
394
400
  }
395
401
  }
396
- program.name('vizzly').description('Vizzly CLI for visual regression testing').version(getPackageVersion()).option('-c, --config <path>', 'Config file path').option('--token <token>', 'Vizzly API token').option('-v, --verbose', 'Verbose output (shorthand for --log-level debug)').option('--log-level <level>', 'Log level: debug, info, warn, error (default: info, or VIZZLY_LOG_LEVEL env var)').option('--json [fields]', 'JSON output, optionally specify fields (e.g., --json id,status,branch)').option('--color', 'Force colored output (even in non-TTY)').option('--no-color', 'Disable colored output').option('--strict', 'Fail on any error (default: be resilient, warn on non-critical issues)').configureHelp({
402
+ program.name('vizzly').description('Vizzly CLI for visual regression testing').version(getPackageVersion()).option('-c, --config <path>', 'Config file path').option('--token <token>', 'Vizzly API token').option('-v, --verbose', 'Verbose output (shorthand for --log-level debug)').option('--log-level <level>', 'Log level: debug, info, warn, error (default: info, or VIZZLY_LOG_LEVEL env var)').option('--json [fields]', 'JSON output, optionally specify fields (e.g., --json id,status,branch)').option('--color', 'Force colored output (even in non-TTY)').option('--no-color', 'Disable colored output').option('--no-update-notifier', 'Skip update checks and notices').option('--strict', 'Fail on any error (default: be resilient, warn on non-critical issues)').configureHelp({
397
403
  formatHelp
398
404
  });
399
405
  program.configureOutput({
@@ -402,6 +408,13 @@ program.configureOutput({
402
408
  program.showHelpAfterError('(Run vizzly --help for available commands)');
403
409
  program.showSuggestionAfterError();
404
410
  program.exitOverride();
411
+ program.hook('preAction', async (_command, actionCommand) => {
412
+ if (actionCommand.name() === 'init') return;
413
+ let updatedCli = await notifyUpdates(actionCommand.optsWithGlobals());
414
+ if (!updatedCli) return;
415
+ let code = await runUpdatedCli(updatedCli, process.argv.slice(2), process.cwd());
416
+ process.exit(code);
417
+ });
405
418
 
406
419
  // Load plugins before defining commands
407
420
  // We need to manually parse to get the config option early
@@ -456,12 +469,14 @@ if (needsRuntimeBootstrap) {
456
469
  output.debug(`Plugin loading failed: ${error.message}`);
457
470
  }
458
471
  }
459
- program.command('init').description('Initialize Vizzly in your project').option('--quick', 'Create an editable config without the setup wizard').option('--force', 'Overwrite existing configuration').option('--agent-skill', 'Install the Vizzly agent skill during setup').option('--agent-guidance', 'Install the Vizzly skill and AGENTS.md guidance during setup').option('--skip-agent-skill', 'Skip the Vizzly agent skill prompt').action(async options => {
472
+ program.command('init').description('Initialize Vizzly in your project').option('--quick', 'Create an editable config without the setup wizard').option('--force', 'Overwrite existing configuration').option('--agent-skill', 'Install or refresh the Vizzly agent skill').option('--agent-guidance', 'Install or refresh the Vizzly skill and AGENTS.md guidance').option('--skip-agent-skill', 'Skip the Vizzly agent skill prompt').action(async options => {
460
473
  let globalOptions = getGlobalOptions();
461
474
  try {
462
475
  let result = await init({
463
476
  ...globalOptions,
464
477
  ...options
478
+ }, {
479
+ connectProject
465
480
  });
466
481
  process.exitCode = result.exitCode || 0;
467
482
  } catch (error) {
@@ -0,0 +1,34 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { fileURLToPath } from 'node:url';
3
+ import { projectLinkCommand } from '../../domains/projects/project.commands.js';
4
+ function runProcess(command, args) {
5
+ return new Promise((resolve, reject) => {
6
+ let child = spawn(command, args, {
7
+ stdio: 'inherit'
8
+ });
9
+ child.once('error', reject);
10
+ child.once('close', (code, signal) => {
11
+ if (code === 0) resolve();else reject(new Error(`${command} did not finish successfully (${signal || code}).`));
12
+ });
13
+ });
14
+ }
15
+ export async function connectProject(project, options) {
16
+ let exitCode = 0;
17
+ let link = () => projectLinkCommand(project, {}, options, {
18
+ exit: code => {
19
+ exitCode = code;
20
+ }
21
+ });
22
+ let result = await link();
23
+ if (result?.reason === 'auth_required') {
24
+ let cli = fileURLToPath(new URL('./cli.js', import.meta.url));
25
+ await runProcess(process.execPath, [cli, 'login']);
26
+ result = await link();
27
+ }
28
+ let status = result?.linked ? 'linked' : 'failed';
29
+ if (!result?.linked && exitCode === 1) status = 'auth_required';
30
+ return {
31
+ status,
32
+ project
33
+ };
34
+ }
@@ -7,8 +7,9 @@ import { existsSync, readFileSync } from 'node:fs';
7
7
  import { request as httpRequest } from 'node:http';
8
8
  import { request as httpsRequest } from 'node:https';
9
9
  import { dirname, join, parse } from 'node:path';
10
- import { getBuildId, getServerUrl, isTddMode, setVizzlyEnabled } from '../utils/environment-config.js';
11
- import { getScreenshotOptionsPayload, normalizeScreenshotOptions } from '../utils/screenshot-options.js';
10
+ import { getBuildId, getServerUrl, isTddMode, setVizzlyEnabled } from '../../domains/config/environment.js';
11
+ import { serializeScreenshotRequest } from '../../domains/screenshots/screenshot.serializers.js';
12
+ import { normalizeScreenshotOptions } from '../../domains/screenshots/screenshot-options.js';
12
13
 
13
14
  // Internal client state
14
15
  let currentClient = null;
@@ -209,14 +210,12 @@ function createSimpleClient(serverUrl, clientOptions = {}) {
209
210
  let isFilePath = typeof imageBuffer === 'string';
210
211
  let image = isFilePath ? imageBuffer : imageBuffer.toString('base64');
211
212
  let type = isFilePath ? 'file-path' : 'base64';
212
- let screenshotData = {
213
+ let screenshotData = serializeScreenshotRequest({
213
214
  buildId: normalizedOptions.buildId ?? getBuildId(),
214
215
  name,
215
216
  image,
216
- type,
217
- properties: normalizedOptions.properties
218
- };
219
- Object.assign(screenshotData, getScreenshotOptionsPayload(normalizedOptions));
217
+ type
218
+ }, normalizedOptions);
220
219
  let httpStart = Date.now();
221
220
  let {
222
221
  status,
@@ -0,0 +1,29 @@
1
+ import { createCloudBaselinesRouter } from '../../domains/baselines/cloud-baselines.routes.js';
2
+ import { createCloudBuildsRouter } from '../../domains/builds/cloud-builds.routes.js';
3
+ import { createCloudProjectsRouter } from '../../domains/projects/cloud-projects.routes.js';
4
+ import { sendError, sendServiceUnavailable } from './middleware/response.js';
5
+ export function createCloudProxyRouter({
6
+ authService
7
+ }) {
8
+ let projects = createCloudProjectsRouter({
9
+ authService
10
+ });
11
+ let builds = createCloudBuildsRouter({
12
+ authService
13
+ });
14
+ let baselines = createCloudBaselinesRouter({
15
+ authService
16
+ });
17
+ return async function handleCloudProxyRoute(req, res, pathname, parsedUrl) {
18
+ if (!pathname.startsWith('/api/cloud')) return false;
19
+ if (!authService) {
20
+ sendServiceUnavailable(res, 'Auth service');
21
+ return true;
22
+ }
23
+ if (await projects(req, res, pathname)) return true;
24
+ if (await builds(req, res, pathname, parsedUrl)) return true;
25
+ if (await baselines(req, res, pathname)) return true;
26
+ sendError(res, 404, 'Cloud API endpoint not found');
27
+ return true;
28
+ };
29
+ }
@@ -4,22 +4,21 @@
4
4
  */
5
5
 
6
6
  import { createServer } from 'node:http';
7
- import * as output from '../utils/output.js';
8
-
7
+ import { createAuthRouter } from '../../domains/auth/auth.routes.js';
8
+ import { createBaselineRouter } from '../../domains/baselines/baseline.routes.js';
9
+ import { createConfigRouter } from '../../domains/config/config.routes.js';
10
+ // Routers
11
+ import { createHealthRouter } from '../../domains/diagnostics/health.routes.js';
12
+ import { createProjectsRouter } from '../../domains/projects/projects.routes.js';
13
+ import { createDashboardRouter } from '../../domains/reports/dashboard.routes.js';
14
+ import { createEventsRouter } from '../../domains/reports/events.routes.js';
15
+ import { createAssetsRouter } from '../../domains/reports/report-assets.routes.js';
16
+ import { createScreenshotRouter } from '../../domains/screenshots/screenshot.routes.js';
17
+ import * as output from '../../shared/output.js';
18
+ import { createCloudProxyRouter } from './cloud-proxy.js';
9
19
  // Middleware
10
20
  import { corsMiddleware } from './middleware/cors.js';
11
21
  import { sendError } from './middleware/response.js';
12
- import { createAssetsRouter } from './routers/assets.js';
13
- import { createAuthRouter } from './routers/auth.js';
14
- import { createBaselineRouter } from './routers/baseline.js';
15
- import { createCloudProxyRouter } from './routers/cloud-proxy.js';
16
- import { createConfigRouter } from './routers/config.js';
17
- import { createDashboardRouter } from './routers/dashboard.js';
18
- import { createEventsRouter } from './routers/events.js';
19
- // Routers
20
- import { createHealthRouter } from './routers/health.js';
21
- import { createProjectsRouter } from './routers/projects.js';
22
- import { createScreenshotRouter } from './routers/screenshot.js';
23
22
  export const createHttpServer = (port, screenshotHandler, services = {}) => {
24
23
  let server = null;
25
24
  const defaultBuildId = services.buildId || null;
@@ -11,7 +11,7 @@
11
11
  * This makes them trivially testable without mocking modules.
12
12
  */
13
13
 
14
- import { buildServerInfo, buildServerJsonPaths, buildServicesWithExtras, determineHandlerMode, getPort } from './core.js';
14
+ import { buildServerInfo, buildServerJsonPaths, buildServicesWithExtras, determineHandlerMode, getPort } from './server-options.js';
15
15
 
16
16
  // ============================================================================
17
17
  // Server Lifecycle Operations
@@ -114,22 +114,19 @@ export async function stopServer({
114
114
  let {
115
115
  fs
116
116
  } = deps;
117
- if (httpServer) {
118
- await httpServer.stop();
119
- }
120
- if (handler?.cleanup) {
117
+ try {
118
+ await httpServer?.stop();
119
+ } finally {
121
120
  try {
122
- handler.cleanup();
121
+ await handler?.cleanup?.();
123
122
  } catch {
124
- // Don't throw - cleanup errors shouldn't fail the stop process
123
+ // Preserve the HTTP shutdown error when handler cleanup also fails.
125
124
  }
125
+ removeServerJson({
126
+ projectRoot,
127
+ fs
128
+ });
126
129
  }
127
-
128
- // Clean up server.json
129
- removeServerJson({
130
- projectRoot,
131
- fs
132
- });
133
130
  }
134
131
 
135
132
  // ============================================================================
@@ -1,24 +1,12 @@
1
- /**
2
- * Server Manager Module - Public exports
3
- *
4
- * Provides functional server management primitives:
5
- * - core.js: Pure functions for building server info, configs, interfaces
6
- * - operations.js: Server operations with dependency injection
7
- */
1
+ /** Screenshot server composition and lifecycle interface. */
8
2
 
9
3
  import { existsSync, mkdirSync, unlinkSync, writeFileSync } from 'node:fs';
10
- import { createApiClient } from '../api/index.js';
11
- import { createApiHandler } from '../server/handlers/api-handler.js';
12
- import { createTddHandler } from '../server/handlers/tdd-handler.js';
13
- import { createHttpServer } from '../server/http-server.js';
14
-
15
- // Core pure functions
16
- import { buildServerInterface } from './core.js';
17
- export { buildClientOptions, buildServerInfo, buildServerInterface, buildServerJsonPaths, buildServicesWithExtras, DEFAULT_PORT, determineHandlerMode, getPort, hasApiKey } from './core.js';
18
-
19
- // Server operations (take dependencies as parameters)
20
- import { getTddResults, startServer, stopServer } from './operations.js';
21
- export { getTddResults, removeServerJson, startServer, stopServer, writeServerJson } from './operations.js';
4
+ import { createApiClient } from '../../domains/auth/cloud-client.js';
5
+ import { createApiHandler } from '../../domains/screenshots/cloud-handler.js';
6
+ import { createTddHandler } from '../../domains/tdd/screenshot-handler.js';
7
+ import { createHttpServer } from './http-server.js';
8
+ import { getTddResults, startServer, stopServer } from './server-lifecycle.js';
9
+ import { buildServerInterface } from './server-options.js';
22
10
 
23
11
  /**
24
12
  * Create a server manager object that provides the interface commands expect.
@@ -26,9 +14,12 @@ export { getTddResults, removeServerJson, startServer, stopServer, writeServerJs
26
14
  *
27
15
  * @param {Object} config - Configuration object
28
16
  * @param {Object} [services={}] - Optional services object
17
+ * @param {Object} [options={}] - Runtime paths
18
+ * @param {string} [options.projectRoot] - Workspace for capture and discovery files
29
19
  * @returns {Object} Server manager with start/stop/getTddResults methods
30
20
  */
31
- export function createServerManager(config, services = {}) {
21
+ export function createServerManager(config, services = {}, options = {}) {
22
+ let projectRoot = options.projectRoot || process.cwd();
32
23
  let httpServer = null;
33
24
  let handler = null;
34
25
  let deps = {
@@ -50,7 +41,7 @@ export function createServerManager(config, services = {}) {
50
41
  buildId,
51
42
  tddMode,
52
43
  setBaseline,
53
- projectRoot: process.cwd(),
44
+ projectRoot,
54
45
  services,
55
46
  deps
56
47
  });
@@ -58,12 +49,17 @@ export function createServerManager(config, services = {}) {
58
49
  handler = result.handler;
59
50
  },
60
51
  async stop() {
61
- await stopServer({
62
- httpServer,
63
- handler,
64
- projectRoot: process.cwd(),
65
- deps
66
- });
52
+ try {
53
+ await stopServer({
54
+ httpServer,
55
+ handler,
56
+ projectRoot,
57
+ deps
58
+ });
59
+ } finally {
60
+ httpServer = null;
61
+ handler = null;
62
+ }
67
63
  },
68
64
  async getTddResults() {
69
65
  return getTddResults({
@@ -9,7 +9,7 @@
9
9
  * exposed to plugins to prevent coupling to implementation details.
10
10
  */
11
11
 
12
- import { detectBranch, detectCommit, detectCommitMessage, detectPullRequestNumber, generateBuildNameWithGit } from './utils/git.js';
12
+ import { detectBranch, detectCommit, detectCommitMessage, detectPullRequestNumber, generateBuildNameWithGit } from '../../shared/git.js';
13
13
 
14
14
  /**
15
15
  * Creates a stable plugin services object from the internal services
@@ -3,7 +3,7 @@ import { dirname, isAbsolute, relative, resolve, sep } from 'node:path';
3
3
  import { pathToFileURL } from 'node:url';
4
4
  import { glob } from 'glob';
5
5
  import { z } from 'zod';
6
- import * as output from './utils/output.js';
6
+ import * as output from '../../shared/output.js';
7
7
 
8
8
  /**
9
9
  * Load and register plugins from node_modules and config
@@ -1,16 +1,11 @@
1
1
  /**
2
2
  * Service Factory
3
- * Creates services for plugin API compatibility.
4
- *
5
- * Internal commands now use functional modules directly:
6
- * - API: import { createApiClient, getBuild } from '../api/index.js'
7
- * - Auth: import { createAuthClient, whoami } from '../auth/index.js'
8
- *
9
- * This factory is only used by cli.js to provide services to plugins.
3
+ * Assembles the execution and screenshot-server interfaces consumed by plugins.
4
+ * Commands import their domain operations directly.
10
5
  */
11
6
 
12
- import { createServerManager } from '../server-manager/index.js';
13
- import { TestRunner } from './test-runner.js';
7
+ import { TestRunner } from '../../domains/runs/run-events.js';
8
+ import { createServerManager } from '../http/server-manager.js';
14
9
 
15
10
  /**
16
11
  * Create services for plugin API compatibility.
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Vizzly SDK Type Definitions
3
+ * Full SDK for custom integrations
4
+ * @module @vizzly-testing/cli/sdk
5
+ */
6
+
7
+ // Re-export common types
8
+ /**
9
+ * VizzlySDK class - Full SDK for custom integrations
10
+ *
11
+ * @example
12
+ * import { createVizzly } from '@vizzly-testing/cli/sdk';
13
+ *
14
+ * const vizzly = await createVizzly({
15
+ * apiKey: process.env.VIZZLY_TOKEN,
16
+ * server: { port: 3003 }
17
+ * });
18
+ *
19
+ * await vizzly.start();
20
+ * await vizzly.screenshot('test', buffer);
21
+ * await vizzly.upload();
22
+ * await vizzly.stop();
23
+ */
24
+ export {
25
+ BaselineData,
26
+ ComparisonResult,
27
+ OutputUtils,
28
+ ScreenshotOptions,
29
+ TddResults,
30
+ TddService,
31
+ Uploader,
32
+ UploadOptions,
33
+ UploadResult,
34
+ VizzlyConfig,
35
+ VizzlySDK,
36
+ } from '../../index';
37
+
38
+ /**
39
+ * Create a new Vizzly SDK instance
40
+ *
41
+ * @param config - Configuration options
42
+ * @param options - Additional options
43
+ * @returns Configured Vizzly SDK instance
44
+ *
45
+ * @example
46
+ * const vizzly = await createVizzly({
47
+ * apiKey: process.env.VIZZLY_TOKEN,
48
+ * apiUrl: 'https://app.vizzly.dev',
49
+ * server: { port: 3003 }
50
+ * });
51
+ */
52
+ export function createVizzly(
53
+ config?: import('../../index').VizzlyConfig,
54
+ options?: { verbose?: boolean }
55
+ ): Promise<import('../../index').VizzlySDK>;
56
+
57
+ /** Load configuration from file and environment */
58
+ export function loadConfig(options?: {
59
+ cwd?: string;
60
+ }): Promise<import('../../index').VizzlyConfig>;
61
+
62
+ /** Output utilities */
63
+ export const output: import('../../index').OutputUtils;
64
+
65
+ /** Create an uploader instance */
66
+ export function createUploader(
67
+ config?: {
68
+ apiKey?: string;
69
+ apiUrl?: string;
70
+ userAgent?: string;
71
+ command?: string;
72
+ upload?: import('../../index').UploadConfig;
73
+ },
74
+ options?: {
75
+ signal?: AbortSignal;
76
+ batchSize?: number;
77
+ timeout?: number;
78
+ }
79
+ ): import('../../index').Uploader;
80
+
81
+ /** Create a TDD service instance */
82
+ export function createTDDService(
83
+ config: import('../../index').VizzlyConfig,
84
+ options?: {
85
+ workingDir?: string;
86
+ setBaseline?: boolean;
87
+ authService?: unknown;
88
+ }
89
+ ): import('../../index').TddService;
@@ -12,14 +12,15 @@
12
12
 
13
13
  import { EventEmitter } from 'node:events';
14
14
  import { createServer } from 'node:http';
15
- import { VizzlyError } from '../errors/vizzly-error.js';
16
- import { handleRequest as handleScreenshotRequest, startServer as startScreenshotServer, stopServer as stopScreenshotServer } from '../screenshot-server/index.js';
17
- import { createTDDService } from '../tdd/tdd-service.js';
18
- import { createUploader } from '../uploader/index.js';
19
- import { loadConfig } from '../utils/config-loader.js';
20
- import { resolveImageBuffer } from '../utils/file-helpers.js';
21
- import * as output from '../utils/output.js';
22
- import { getScreenshotOptionsPayload, normalizeScreenshotOptions } from '../utils/screenshot-options.js';
15
+ import { loadConfig } from '../../domains/config/config-loader.js';
16
+ import { resolveImageBuffer } from '../../domains/screenshots/image-files.js';
17
+ import { serializeScreenshotRequest } from '../../domains/screenshots/screenshot.serializers.js';
18
+ import { normalizeScreenshotOptions } from '../../domains/screenshots/screenshot-options.js';
19
+ import { handleRequest as handleScreenshotRequest, startServer as startScreenshotServer, stopServer as stopScreenshotServer } from '../../domains/screenshots/screenshot-server.js';
20
+ import { createTDDService } from '../../domains/tdd/tdd.service.js';
21
+ import { createUploader } from '../../domains/uploads/upload.service.js';
22
+ import * as output from '../../shared/output.js';
23
+ import { VizzlyError } from '../../shared/vizzly-error.js';
23
24
 
24
25
  /**
25
26
  * Create a new Vizzly instance with custom configuration
@@ -248,7 +249,10 @@ export class VizzlySDK extends EventEmitter {
248
249
  }
249
250
  async startTDD(options = {}) {
250
251
  let tddService = this.createTDDService(options);
251
- return tddService.start(options);
252
+ tddService.clearRunData();
253
+ await tddService.loadBaseline();
254
+ this.services.tddService = tddService;
255
+ return tddService;
252
256
  }
253
257
 
254
258
  /**
@@ -301,14 +305,12 @@ export class VizzlySDK extends EventEmitter {
301
305
 
302
306
  // Convert Buffer to base64 for JSON transport
303
307
  let imageBase64 = buffer.toString('base64');
304
- let screenshotData = {
308
+ let screenshotData = serializeScreenshotRequest({
305
309
  buildId,
306
310
  name,
307
311
  image: imageBase64,
308
- type: 'base64',
309
- properties: normalizedOptions.properties
310
- };
311
- Object.assign(screenshotData, getScreenshotOptionsPayload(normalizedOptions));
312
+ type: 'base64'
313
+ }, normalizedOptions);
312
314
  // POST to the local screenshot server
313
315
  let serverUrl = `http://localhost:${this.config.server?.port || 3000}`;
314
316
  let fetchFn = this.services.fetch || fetch;
@@ -419,9 +421,10 @@ export class VizzlySDK extends EventEmitter {
419
421
  }
420
422
  }
421
423
  }
422
- export { createTDDService } from '../tdd/tdd-service.js';
423
- // Export service creators for advanced usage
424
- export { createUploader } from '../uploader/index.js';
424
+
425
425
  // Re-export key utilities and errors
426
- export { loadConfig } from '../utils/config-loader.js';
427
- export * as output from '../utils/output.js';
426
+ export { loadConfig } from '../../domains/config/config-loader.js';
427
+ export { createTDDService } from '../../domains/tdd/tdd.service.js';
428
+ // Export service creators for advanced usage
429
+ export { createUploader } from '../../domains/uploads/upload.service.js';
430
+ export * as output from '../../shared/output.js';
@@ -6,9 +6,9 @@ import { open, readFile, unlink } from 'node:fs/promises';
6
6
  import { resolve } from 'node:path';
7
7
  import { Readable } from 'node:stream';
8
8
  import { pipeline } from 'node:stream/promises';
9
- import { createApiClient as defaultCreateApiClient } from '../api/index.js';
10
- import { loadConfig as defaultLoadConfig } from '../utils/config-loader.js';
11
- import * as defaultOutput from '../utils/output.js';
9
+ import * as defaultOutput from '../../shared/output.js';
10
+ import { createApiClient as defaultCreateApiClient } from '../auth/cloud-client.js';
11
+ import { loadConfig as defaultLoadConfig } from '../config/config-loader.js';
12
12
  let API_METHODS = ['GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE'];
13
13
  function createApiCommandDeps(deps = {}) {
14
14
  return {