@vizzly-testing/cli 0.13.4 → 0.15.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.
- package/dist/cli.js +68 -68
- package/dist/commands/doctor.js +30 -34
- package/dist/commands/finalize.js +24 -23
- package/dist/commands/init.js +30 -28
- package/dist/commands/login.js +49 -55
- package/dist/commands/logout.js +14 -19
- package/dist/commands/project.js +83 -103
- package/dist/commands/run.js +77 -89
- package/dist/commands/status.js +48 -49
- package/dist/commands/tdd-daemon.js +90 -86
- package/dist/commands/tdd.js +59 -88
- package/dist/commands/upload.js +57 -57
- package/dist/commands/whoami.js +40 -45
- package/dist/index.js +2 -5
- package/dist/plugin-loader.js +15 -17
- package/dist/reporter/reporter-bundle.css +1 -1
- package/dist/reporter/reporter-bundle.iife.js +78 -32
- package/dist/sdk/index.js +36 -45
- package/dist/server/handlers/api-handler.js +14 -15
- package/dist/server/handlers/tdd-handler.js +34 -37
- package/dist/server/http-server.js +75 -869
- package/dist/server/middleware/cors.js +22 -0
- package/dist/server/middleware/json-parser.js +35 -0
- package/dist/server/middleware/response.js +79 -0
- package/dist/server/routers/assets.js +91 -0
- package/dist/server/routers/auth.js +144 -0
- package/dist/server/routers/baseline.js +163 -0
- package/dist/server/routers/cloud-proxy.js +146 -0
- package/dist/server/routers/config.js +126 -0
- package/dist/server/routers/dashboard.js +130 -0
- package/dist/server/routers/health.js +61 -0
- package/dist/server/routers/projects.js +168 -0
- package/dist/server/routers/screenshot.js +86 -0
- package/dist/services/auth-service.js +1 -1
- package/dist/services/build-manager.js +13 -40
- package/dist/services/config-service.js +2 -4
- package/dist/services/html-report-generator.js +6 -5
- package/dist/services/index.js +64 -0
- package/dist/services/project-service.js +121 -40
- package/dist/services/screenshot-server.js +9 -9
- package/dist/services/server-manager.js +11 -18
- package/dist/services/static-report-generator.js +3 -4
- package/dist/services/tdd-service.js +246 -103
- package/dist/services/test-runner.js +24 -25
- package/dist/services/uploader.js +5 -4
- package/dist/types/commands/init.d.ts +1 -2
- package/dist/types/index.d.ts +2 -3
- package/dist/types/plugin-loader.d.ts +1 -2
- package/dist/types/reporter/src/api/client.d.ts +178 -0
- package/dist/types/reporter/src/components/app-router.d.ts +1 -3
- package/dist/types/reporter/src/components/code-block.d.ts +4 -0
- package/dist/types/reporter/src/components/comparison/comparison-modes/onion-skin-mode.d.ts +10 -0
- package/dist/types/reporter/src/components/comparison/comparison-modes/overlay-mode.d.ts +11 -0
- package/dist/types/reporter/src/components/comparison/comparison-modes/shared/base-comparison-mode.d.ts +14 -0
- package/dist/types/reporter/src/components/comparison/comparison-modes/shared/image-renderer.d.ts +30 -0
- package/dist/types/reporter/src/components/comparison/comparison-modes/toggle-view.d.ts +8 -0
- package/dist/types/reporter/src/components/comparison/comparison-viewer.d.ts +4 -0
- package/dist/types/reporter/src/components/comparison/fullscreen-viewer.d.ts +13 -0
- package/dist/types/reporter/src/components/comparison/screenshot-display.d.ts +16 -0
- package/dist/types/reporter/src/components/comparison/screenshot-list.d.ts +9 -0
- package/dist/types/reporter/src/components/comparison/variant-selector.d.ts +1 -1
- package/dist/types/reporter/src/components/design-system/alert.d.ts +9 -0
- package/dist/types/reporter/src/components/design-system/badge.d.ts +17 -0
- package/dist/types/reporter/src/components/design-system/button.d.ts +19 -0
- package/dist/types/reporter/src/components/design-system/card.d.ts +31 -0
- package/dist/types/reporter/src/components/design-system/empty-state.d.ts +13 -0
- package/dist/types/reporter/src/components/design-system/form-controls.d.ts +44 -0
- package/dist/types/reporter/src/components/design-system/health-ring.d.ts +7 -0
- package/dist/types/reporter/src/components/design-system/index.d.ts +11 -0
- package/dist/types/reporter/src/components/design-system/modal.d.ts +10 -0
- package/dist/types/reporter/src/components/design-system/skeleton.d.ts +19 -0
- package/dist/types/reporter/src/components/design-system/spinner.d.ts +10 -0
- package/dist/types/reporter/src/components/design-system/tabs.d.ts +13 -0
- package/dist/types/reporter/src/components/layout/header.d.ts +5 -0
- package/dist/types/reporter/src/components/layout/index.d.ts +2 -0
- package/dist/types/reporter/src/components/layout/layout.d.ts +6 -0
- package/dist/types/reporter/src/components/views/builds-view.d.ts +1 -0
- package/dist/types/reporter/src/components/views/comparison-detail-view.d.ts +5 -0
- package/dist/types/reporter/src/components/views/comparisons-view.d.ts +5 -6
- package/dist/types/reporter/src/components/views/stats-view.d.ts +1 -6
- package/dist/types/reporter/src/components/waiting-for-screenshots.d.ts +1 -0
- package/dist/types/reporter/src/hooks/queries/use-auth-queries.d.ts +15 -0
- package/dist/types/reporter/src/hooks/queries/use-cloud-queries.d.ts +6 -0
- package/dist/types/reporter/src/hooks/queries/use-config-queries.d.ts +6 -0
- package/dist/types/reporter/src/hooks/queries/use-tdd-queries.d.ts +9 -0
- package/dist/types/reporter/src/lib/query-client.d.ts +2 -0
- package/dist/types/reporter/src/lib/query-keys.d.ts +13 -0
- package/dist/types/sdk/index.d.ts +2 -4
- package/dist/types/server/handlers/tdd-handler.d.ts +2 -0
- package/dist/types/server/http-server.d.ts +1 -1
- package/dist/types/server/middleware/cors.d.ts +11 -0
- package/dist/types/server/middleware/json-parser.d.ts +10 -0
- package/dist/types/server/middleware/response.d.ts +50 -0
- package/dist/types/server/routers/assets.d.ts +6 -0
- package/dist/types/server/routers/auth.d.ts +9 -0
- package/dist/types/server/routers/baseline.d.ts +13 -0
- package/dist/types/server/routers/cloud-proxy.d.ts +11 -0
- package/dist/types/server/routers/config.d.ts +9 -0
- package/dist/types/server/routers/dashboard.d.ts +6 -0
- package/dist/types/server/routers/health.d.ts +11 -0
- package/dist/types/server/routers/projects.d.ts +9 -0
- package/dist/types/server/routers/screenshot.d.ts +11 -0
- package/dist/types/services/build-manager.d.ts +4 -3
- package/dist/types/services/config-service.d.ts +2 -3
- package/dist/types/services/index.d.ts +7 -0
- package/dist/types/services/project-service.d.ts +6 -4
- package/dist/types/services/screenshot-server.d.ts +5 -5
- package/dist/types/services/server-manager.d.ts +5 -3
- package/dist/types/services/tdd-service.d.ts +12 -1
- package/dist/types/services/test-runner.d.ts +3 -3
- package/dist/types/utils/output.d.ts +84 -0
- package/dist/utils/config-loader.js +24 -48
- package/dist/utils/global-config.js +2 -17
- package/dist/utils/output.js +445 -0
- package/dist/utils/security.js +3 -4
- package/docs/api-reference.md +0 -1
- package/docs/plugins.md +22 -22
- package/package.json +3 -2
- package/dist/container/index.js +0 -215
- package/dist/services/base-service.js +0 -154
- package/dist/types/container/index.d.ts +0 -59
- package/dist/types/reporter/src/components/comparison/viewer-modes/onion-viewer.d.ts +0 -3
- package/dist/types/reporter/src/components/comparison/viewer-modes/overlay-viewer.d.ts +0 -3
- package/dist/types/reporter/src/components/comparison/viewer-modes/side-by-side-viewer.d.ts +0 -3
- package/dist/types/reporter/src/components/comparison/viewer-modes/toggle-viewer.d.ts +0 -3
- package/dist/types/reporter/src/components/dashboard/dashboard-header.d.ts +0 -5
- package/dist/types/reporter/src/components/dashboard/dashboard-stats.d.ts +0 -4
- package/dist/types/reporter/src/components/dashboard/empty-state.d.ts +0 -8
- package/dist/types/reporter/src/components/ui/form-field.d.ts +0 -16
- package/dist/types/reporter/src/components/ui/status-badge.d.ts +0 -5
- package/dist/types/reporter/src/hooks/use-auth.d.ts +0 -10
- package/dist/types/reporter/src/hooks/use-baseline-actions.d.ts +0 -5
- package/dist/types/reporter/src/hooks/use-config.d.ts +0 -9
- package/dist/types/reporter/src/hooks/use-projects.d.ts +0 -10
- package/dist/types/reporter/src/hooks/use-report-data.d.ts +0 -7
- package/dist/types/reporter/src/hooks/use-vizzly-api.d.ts +0 -9
- package/dist/types/services/base-service.d.ts +0 -71
- package/dist/types/utils/console-ui.d.ts +0 -61
- package/dist/types/utils/logger-factory.d.ts +0 -26
- package/dist/types/utils/logger.d.ts +0 -79
- package/dist/utils/console-ui.js +0 -241
- package/dist/utils/logger-factory.js +0 -76
- package/dist/utils/logger.js +0 -231
package/dist/cli.js
CHANGED
|
@@ -16,8 +16,8 @@ import { projectSelectCommand, projectListCommand, projectTokenCommand, projectR
|
|
|
16
16
|
import { getPackageVersion } from './utils/package-info.js';
|
|
17
17
|
import { loadPlugins } from './plugin-loader.js';
|
|
18
18
|
import { loadConfig } from './utils/config-loader.js';
|
|
19
|
-
import {
|
|
20
|
-
import
|
|
19
|
+
import { createServices } from './services/index.js';
|
|
20
|
+
import * as output from './utils/output.js';
|
|
21
21
|
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').option('--json', 'Machine-readable output').option('--no-color', 'Disable colored output');
|
|
22
22
|
|
|
23
23
|
// Load plugins before defining commands
|
|
@@ -32,35 +32,38 @@ for (let i = 0; i < process.argv.length; i++) {
|
|
|
32
32
|
verboseMode = true;
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
verbose: verboseMode
|
|
35
|
+
|
|
36
|
+
// Configure output early
|
|
37
|
+
output.configure({
|
|
38
|
+
verbose: verboseMode,
|
|
39
|
+
color: !process.argv.includes('--no-color'),
|
|
40
|
+
json: process.argv.includes('--json')
|
|
39
41
|
});
|
|
40
|
-
let
|
|
42
|
+
let config = await loadConfig(configPath, {});
|
|
43
|
+
let services = createServices(config);
|
|
41
44
|
let plugins = [];
|
|
42
45
|
try {
|
|
43
|
-
plugins = await loadPlugins(configPath, config
|
|
46
|
+
plugins = await loadPlugins(configPath, config);
|
|
44
47
|
for (let plugin of plugins) {
|
|
45
48
|
try {
|
|
46
49
|
// Add timeout protection for plugin registration (5 seconds)
|
|
47
50
|
let registerPromise = plugin.register(program, {
|
|
48
51
|
config,
|
|
49
|
-
|
|
50
|
-
|
|
52
|
+
services,
|
|
53
|
+
output
|
|
51
54
|
});
|
|
52
55
|
let timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error('Plugin registration timeout (5s)')), 5000));
|
|
53
56
|
await Promise.race([registerPromise, timeoutPromise]);
|
|
54
|
-
|
|
57
|
+
output.debug(`Registered plugin: ${plugin.name}`);
|
|
55
58
|
} catch (error) {
|
|
56
|
-
|
|
59
|
+
output.warn(`Failed to register plugin ${plugin.name}: ${error.message}`);
|
|
57
60
|
}
|
|
58
61
|
}
|
|
59
62
|
} catch (error) {
|
|
60
|
-
|
|
63
|
+
output.debug(`Plugin loading failed: ${error.message}`);
|
|
61
64
|
}
|
|
62
65
|
program.command('init').description('Initialize Vizzly in your project').option('--force', 'Overwrite existing configuration').action(async options => {
|
|
63
|
-
|
|
66
|
+
let globalOptions = program.opts();
|
|
64
67
|
await init({
|
|
65
68
|
...globalOptions,
|
|
66
69
|
...options,
|
|
@@ -68,24 +71,24 @@ program.command('init').description('Initialize Vizzly in your project').option(
|
|
|
68
71
|
});
|
|
69
72
|
});
|
|
70
73
|
program.command('upload').description('Upload screenshots to Vizzly').argument('<path>', 'Path to screenshots directory or file').option('-b, --build-name <name>', 'Build name for grouping').option('-m, --metadata <json>', 'Additional metadata as JSON').option('--batch-size <n>', 'Upload batch size', v => parseInt(v, 10)).option('--upload-timeout <ms>', 'Upload timeout in milliseconds', v => parseInt(v, 10)).option('--branch <branch>', 'Git branch').option('--commit <sha>', 'Git commit SHA').option('--message <msg>', 'Commit message').option('--environment <env>', 'Environment name', 'test').option('--threshold <number>', 'Comparison threshold', parseFloat).option('--token <token>', 'API token override').option('--wait', 'Wait for build completion').option('--upload-all', 'Upload all screenshots without SHA deduplication').option('--parallel-id <id>', 'Unique identifier for parallel test execution').action(async (path, options) => {
|
|
71
|
-
|
|
74
|
+
let globalOptions = program.opts();
|
|
72
75
|
|
|
73
76
|
// Validate options
|
|
74
|
-
|
|
77
|
+
let validationErrors = validateUploadOptions(path, options);
|
|
75
78
|
if (validationErrors.length > 0) {
|
|
76
|
-
|
|
77
|
-
validationErrors.forEach(error =>
|
|
79
|
+
output.error('Validation errors:');
|
|
80
|
+
validationErrors.forEach(error => output.printErr(` - ${error}`));
|
|
78
81
|
process.exit(1);
|
|
79
82
|
}
|
|
80
83
|
await uploadCommand(path, options, globalOptions);
|
|
81
84
|
});
|
|
82
85
|
|
|
83
86
|
// TDD command with subcommands - Local visual testing with interactive dashboard
|
|
84
|
-
|
|
87
|
+
let tddCmd = program.command('tdd').description('Run tests in TDD mode with local visual comparisons');
|
|
85
88
|
|
|
86
89
|
// TDD Start - Background server
|
|
87
90
|
tddCmd.command('start').description('Start background TDD server with dashboard').option('--port <port>', 'Port for TDD server', '47392').option('--open', 'Open dashboard in browser').option('--baseline-build <id>', 'Use specific build as baseline').option('--baseline-comparison <id>', 'Use specific comparison as baseline').option('--environment <env>', 'Environment name', 'test').option('--threshold <number>', 'Comparison threshold', parseFloat).option('--timeout <ms>', 'Server timeout in milliseconds', '30000').option('--token <token>', 'API token override').option('--daemon-child', 'Internal: run as daemon child process').action(async options => {
|
|
88
|
-
|
|
91
|
+
let globalOptions = program.opts();
|
|
89
92
|
|
|
90
93
|
// If this is a daemon child process, run the server directly
|
|
91
94
|
if (options.daemonChild) {
|
|
@@ -97,34 +100,34 @@ tddCmd.command('start').description('Start background TDD server with dashboard'
|
|
|
97
100
|
|
|
98
101
|
// TDD Stop - Kill background server
|
|
99
102
|
tddCmd.command('stop').description('Stop background TDD server').action(async options => {
|
|
100
|
-
|
|
103
|
+
let globalOptions = program.opts();
|
|
101
104
|
await tddStopCommand(options, globalOptions);
|
|
102
105
|
});
|
|
103
106
|
|
|
104
107
|
// TDD Status - Check server status
|
|
105
108
|
tddCmd.command('status').description('Check TDD server status').action(async options => {
|
|
106
|
-
|
|
109
|
+
let globalOptions = program.opts();
|
|
107
110
|
await tddStatusCommand(options, globalOptions);
|
|
108
111
|
});
|
|
109
112
|
|
|
110
113
|
// TDD Run - One-off test run with ephemeral server (generates static report)
|
|
111
114
|
tddCmd.command('run <command>').description('Run tests once in TDD mode with local visual comparisons').option('--port <port>', 'Port for TDD server', '47392').option('--branch <branch>', 'Git branch override').option('--environment <env>', 'Environment name', 'test').option('--threshold <number>', 'Comparison threshold', parseFloat).option('--token <token>', 'API token override').option('--timeout <ms>', 'Server timeout in milliseconds', '30000').option('--baseline-build <id>', 'Use specific build as baseline').option('--baseline-comparison <id>', 'Use specific comparison as baseline').option('--set-baseline', 'Accept current screenshots as new baseline (overwrites existing)').action(async (command, options) => {
|
|
112
|
-
|
|
115
|
+
let globalOptions = program.opts();
|
|
113
116
|
|
|
114
117
|
// Validate options
|
|
115
|
-
|
|
118
|
+
let validationErrors = validateTddOptions(command, options);
|
|
116
119
|
if (validationErrors.length > 0) {
|
|
117
|
-
|
|
118
|
-
validationErrors.forEach(error =>
|
|
120
|
+
output.error('Validation errors:');
|
|
121
|
+
validationErrors.forEach(error => output.printErr(` - ${error}`));
|
|
119
122
|
process.exit(1);
|
|
120
123
|
}
|
|
121
|
-
|
|
124
|
+
let {
|
|
122
125
|
result,
|
|
123
126
|
cleanup
|
|
124
127
|
} = await tddCommand(command, options, globalOptions);
|
|
125
128
|
|
|
126
129
|
// Set up cleanup on process signals
|
|
127
|
-
|
|
130
|
+
let handleCleanup = async () => {
|
|
128
131
|
await cleanup();
|
|
129
132
|
};
|
|
130
133
|
process.once('SIGINT', () => {
|
|
@@ -140,122 +143,119 @@ tddCmd.command('run <command>').description('Run tests once in TDD mode with loc
|
|
|
140
143
|
await cleanup();
|
|
141
144
|
});
|
|
142
145
|
program.command('run').description('Run tests with Vizzly integration').argument('<command>', 'Test command to run').option('--port <port>', 'Port for screenshot server', '47392').option('-b, --build-name <name>', 'Custom build name').option('--branch <branch>', 'Git branch override').option('--commit <sha>', 'Git commit SHA').option('--message <msg>', 'Commit message').option('--environment <env>', 'Environment name', 'test').option('--token <token>', 'API token override').option('--wait', 'Wait for build completion').option('--timeout <ms>', 'Server timeout in milliseconds', '30000').option('--allow-no-token', 'Allow running without API token').option('--upload-all', 'Upload all screenshots without SHA deduplication').option('--parallel-id <id>', 'Unique identifier for parallel test execution').action(async (command, options) => {
|
|
143
|
-
|
|
146
|
+
let globalOptions = program.opts();
|
|
144
147
|
|
|
145
148
|
// Validate options
|
|
146
|
-
|
|
149
|
+
let validationErrors = validateRunOptions(command, options);
|
|
147
150
|
if (validationErrors.length > 0) {
|
|
148
|
-
|
|
149
|
-
validationErrors.forEach(error =>
|
|
151
|
+
output.error('Validation errors:');
|
|
152
|
+
validationErrors.forEach(error => output.printErr(` - ${error}`));
|
|
150
153
|
process.exit(1);
|
|
151
154
|
}
|
|
152
155
|
try {
|
|
153
|
-
|
|
156
|
+
let result = await runCommand(command, options, globalOptions);
|
|
154
157
|
if (result && !result.success && result.exitCode > 0) {
|
|
155
158
|
process.exit(result.exitCode);
|
|
156
159
|
}
|
|
157
160
|
} catch (error) {
|
|
158
|
-
|
|
159
|
-
if (globalOptions.verbose) {
|
|
160
|
-
console.error('Stack trace:', error.stack);
|
|
161
|
-
}
|
|
161
|
+
output.error('Command failed', error);
|
|
162
162
|
process.exit(1);
|
|
163
163
|
}
|
|
164
164
|
});
|
|
165
165
|
program.command('status').description('Check the status of a build').argument('<build-id>', 'Build ID to check status for').action(async (buildId, options) => {
|
|
166
|
-
|
|
166
|
+
let globalOptions = program.opts();
|
|
167
167
|
|
|
168
168
|
// Validate options
|
|
169
|
-
|
|
169
|
+
let validationErrors = validateStatusOptions(buildId, options);
|
|
170
170
|
if (validationErrors.length > 0) {
|
|
171
|
-
|
|
172
|
-
validationErrors.forEach(error =>
|
|
171
|
+
output.error('Validation errors:');
|
|
172
|
+
validationErrors.forEach(error => output.printErr(` - ${error}`));
|
|
173
173
|
process.exit(1);
|
|
174
174
|
}
|
|
175
175
|
await statusCommand(buildId, options, globalOptions);
|
|
176
176
|
});
|
|
177
177
|
program.command('finalize').description('Finalize a parallel build after all shards complete').argument('<parallel-id>', 'Parallel ID to finalize').action(async (parallelId, options) => {
|
|
178
|
-
|
|
178
|
+
let globalOptions = program.opts();
|
|
179
179
|
|
|
180
180
|
// Validate options
|
|
181
|
-
|
|
181
|
+
let validationErrors = validateFinalizeOptions(parallelId, options);
|
|
182
182
|
if (validationErrors.length > 0) {
|
|
183
|
-
|
|
184
|
-
validationErrors.forEach(error =>
|
|
183
|
+
output.error('Validation errors:');
|
|
184
|
+
validationErrors.forEach(error => output.printErr(` - ${error}`));
|
|
185
185
|
process.exit(1);
|
|
186
186
|
}
|
|
187
187
|
await finalizeCommand(parallelId, options, globalOptions);
|
|
188
188
|
});
|
|
189
189
|
program.command('doctor').description('Run diagnostics to check your environment and configuration').option('--api', 'Include API connectivity checks').action(async options => {
|
|
190
|
-
|
|
190
|
+
let globalOptions = program.opts();
|
|
191
191
|
|
|
192
192
|
// Validate options
|
|
193
|
-
|
|
193
|
+
let validationErrors = validateDoctorOptions(options);
|
|
194
194
|
if (validationErrors.length > 0) {
|
|
195
|
-
|
|
196
|
-
validationErrors.forEach(error =>
|
|
195
|
+
output.error('Validation errors:');
|
|
196
|
+
validationErrors.forEach(error => output.printErr(` - ${error}`));
|
|
197
197
|
process.exit(1);
|
|
198
198
|
}
|
|
199
199
|
await doctorCommand(options, globalOptions);
|
|
200
200
|
});
|
|
201
201
|
program.command('login').description('Authenticate with your Vizzly account').option('--api-url <url>', 'API URL override').action(async options => {
|
|
202
|
-
|
|
202
|
+
let globalOptions = program.opts();
|
|
203
203
|
|
|
204
204
|
// Validate options
|
|
205
|
-
|
|
205
|
+
let validationErrors = validateLoginOptions(options);
|
|
206
206
|
if (validationErrors.length > 0) {
|
|
207
|
-
|
|
208
|
-
validationErrors.forEach(error =>
|
|
207
|
+
output.error('Validation errors:');
|
|
208
|
+
validationErrors.forEach(error => output.printErr(` - ${error}`));
|
|
209
209
|
process.exit(1);
|
|
210
210
|
}
|
|
211
211
|
await loginCommand(options, globalOptions);
|
|
212
212
|
});
|
|
213
213
|
program.command('logout').description('Clear stored authentication tokens').option('--api-url <url>', 'API URL override').action(async options => {
|
|
214
|
-
|
|
214
|
+
let globalOptions = program.opts();
|
|
215
215
|
|
|
216
216
|
// Validate options
|
|
217
|
-
|
|
217
|
+
let validationErrors = validateLogoutOptions(options);
|
|
218
218
|
if (validationErrors.length > 0) {
|
|
219
|
-
|
|
220
|
-
validationErrors.forEach(error =>
|
|
219
|
+
output.error('Validation errors:');
|
|
220
|
+
validationErrors.forEach(error => output.printErr(` - ${error}`));
|
|
221
221
|
process.exit(1);
|
|
222
222
|
}
|
|
223
223
|
await logoutCommand(options, globalOptions);
|
|
224
224
|
});
|
|
225
225
|
program.command('whoami').description('Show current authentication status and user information').option('--api-url <url>', 'API URL override').action(async options => {
|
|
226
|
-
|
|
226
|
+
let globalOptions = program.opts();
|
|
227
227
|
|
|
228
228
|
// Validate options
|
|
229
|
-
|
|
229
|
+
let validationErrors = validateWhoamiOptions(options);
|
|
230
230
|
if (validationErrors.length > 0) {
|
|
231
|
-
|
|
232
|
-
validationErrors.forEach(error =>
|
|
231
|
+
output.error('Validation errors:');
|
|
232
|
+
validationErrors.forEach(error => output.printErr(` - ${error}`));
|
|
233
233
|
process.exit(1);
|
|
234
234
|
}
|
|
235
235
|
await whoamiCommand(options, globalOptions);
|
|
236
236
|
});
|
|
237
237
|
program.command('project:select').description('Configure project for current directory').option('--api-url <url>', 'API URL override').action(async options => {
|
|
238
|
-
|
|
238
|
+
let globalOptions = program.opts();
|
|
239
239
|
|
|
240
240
|
// Validate options
|
|
241
|
-
|
|
241
|
+
let validationErrors = validateProjectOptions(options);
|
|
242
242
|
if (validationErrors.length > 0) {
|
|
243
|
-
|
|
244
|
-
validationErrors.forEach(error =>
|
|
243
|
+
output.error('Validation errors:');
|
|
244
|
+
validationErrors.forEach(error => output.printErr(` - ${error}`));
|
|
245
245
|
process.exit(1);
|
|
246
246
|
}
|
|
247
247
|
await projectSelectCommand(options, globalOptions);
|
|
248
248
|
});
|
|
249
249
|
program.command('project:list').description('Show all configured projects').action(async options => {
|
|
250
|
-
|
|
250
|
+
let globalOptions = program.opts();
|
|
251
251
|
await projectListCommand(options, globalOptions);
|
|
252
252
|
});
|
|
253
253
|
program.command('project:token').description('Show project token for current directory').action(async options => {
|
|
254
|
-
|
|
254
|
+
let globalOptions = program.opts();
|
|
255
255
|
await projectTokenCommand(options, globalOptions);
|
|
256
256
|
});
|
|
257
257
|
program.command('project:remove').description('Remove project configuration for current directory').action(async options => {
|
|
258
|
-
|
|
258
|
+
let globalOptions = program.opts();
|
|
259
259
|
await projectRemoveCommand(options, globalOptions);
|
|
260
260
|
});
|
|
261
261
|
program.parse();
|
package/dist/commands/doctor.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { URL } from 'url';
|
|
2
2
|
import { loadConfig } from '../utils/config-loader.js';
|
|
3
|
-
import
|
|
3
|
+
import * as output from '../utils/output.js';
|
|
4
4
|
import { ApiService } from '../services/api-service.js';
|
|
5
5
|
import { ConfigError } from '../errors/vizzly-error.js';
|
|
6
6
|
import { getApiToken } from '../utils/environment-config.js';
|
|
@@ -11,16 +11,12 @@ import { getApiToken } from '../utils/environment-config.js';
|
|
|
11
11
|
* @param {Object} globalOptions - Global CLI options
|
|
12
12
|
*/
|
|
13
13
|
export async function doctorCommand(options = {}, globalOptions = {}) {
|
|
14
|
-
|
|
15
|
-
const ui = new ConsoleUI({
|
|
14
|
+
output.configure({
|
|
16
15
|
json: globalOptions.json,
|
|
17
16
|
verbose: globalOptions.verbose,
|
|
18
17
|
color: !globalOptions.noColor
|
|
19
18
|
});
|
|
20
|
-
|
|
21
|
-
// Note: ConsoleUI handles cleanup via global process listeners
|
|
22
|
-
|
|
23
|
-
const diagnostics = {
|
|
19
|
+
let diagnostics = {
|
|
24
20
|
environment: {
|
|
25
21
|
nodeVersion: null,
|
|
26
22
|
nodeVersionValid: null
|
|
@@ -41,71 +37,71 @@ export async function doctorCommand(options = {}, globalOptions = {}) {
|
|
|
41
37
|
let hasErrors = false;
|
|
42
38
|
try {
|
|
43
39
|
// Determine if we'll attempt remote checks (API connectivity)
|
|
44
|
-
|
|
40
|
+
let willCheckConnectivity = Boolean(options.api || getApiToken());
|
|
45
41
|
|
|
46
42
|
// Announce preflight, indicating local-only when no token/connectivity is planned
|
|
47
|
-
|
|
43
|
+
output.info(`Running Vizzly preflight${willCheckConnectivity ? '' : ' (local checks only)'}...`);
|
|
48
44
|
|
|
49
45
|
// Node.js version check (require >= 20)
|
|
50
|
-
|
|
51
|
-
|
|
46
|
+
let nodeVersion = process.version;
|
|
47
|
+
let nodeMajor = parseInt(nodeVersion.slice(1).split('.')[0], 10);
|
|
52
48
|
diagnostics.environment.nodeVersion = nodeVersion;
|
|
53
49
|
diagnostics.environment.nodeVersionValid = nodeMajor >= 20;
|
|
54
50
|
if (nodeMajor >= 20) {
|
|
55
|
-
|
|
51
|
+
output.success(`Node.js version: ${nodeVersion} (supported)`);
|
|
56
52
|
} else {
|
|
57
53
|
hasErrors = true;
|
|
58
|
-
|
|
54
|
+
output.error('Node.js version must be >= 20');
|
|
59
55
|
}
|
|
60
56
|
|
|
61
57
|
// Load configuration (apply global CLI overrides like --config only)
|
|
62
|
-
|
|
58
|
+
let config = await loadConfig(globalOptions.config);
|
|
63
59
|
|
|
64
60
|
// Validate apiUrl
|
|
65
61
|
diagnostics.configuration.apiUrl = config.apiUrl;
|
|
66
62
|
try {
|
|
67
|
-
|
|
63
|
+
let url = new URL(config.apiUrl);
|
|
68
64
|
if (!['http:', 'https:'].includes(url.protocol)) {
|
|
69
65
|
throw new ConfigError('URL must use http or https');
|
|
70
66
|
}
|
|
71
67
|
diagnostics.configuration.apiUrlValid = true;
|
|
72
|
-
|
|
68
|
+
output.success(`API URL: ${config.apiUrl}`);
|
|
73
69
|
} catch (e) {
|
|
74
70
|
diagnostics.configuration.apiUrlValid = false;
|
|
75
71
|
hasErrors = true;
|
|
76
|
-
|
|
72
|
+
output.error('Invalid apiUrl in configuration (set VIZZLY_API_URL or config file)', e);
|
|
77
73
|
}
|
|
78
74
|
|
|
79
75
|
// Validate threshold (0..1 inclusive)
|
|
80
|
-
|
|
76
|
+
let threshold = Number(config?.comparison?.threshold);
|
|
81
77
|
diagnostics.configuration.threshold = threshold;
|
|
82
|
-
|
|
78
|
+
let thresholdValid = Number.isFinite(threshold) && threshold >= 0 && threshold <= 1;
|
|
83
79
|
diagnostics.configuration.thresholdValid = thresholdValid;
|
|
84
80
|
if (thresholdValid) {
|
|
85
|
-
|
|
81
|
+
output.success(`Threshold: ${threshold}`);
|
|
86
82
|
} else {
|
|
87
83
|
hasErrors = true;
|
|
88
|
-
|
|
84
|
+
output.error('Invalid threshold (expected number between 0 and 1)');
|
|
89
85
|
}
|
|
90
86
|
|
|
91
87
|
// Report effective port without binding
|
|
92
|
-
|
|
88
|
+
let port = config?.server?.port ?? 47392;
|
|
93
89
|
diagnostics.configuration.port = port;
|
|
94
|
-
|
|
90
|
+
output.info(`Effective port: ${port}`);
|
|
95
91
|
|
|
96
92
|
// Optional: API connectivity check when --api is provided or VIZZLY_TOKEN is present
|
|
97
|
-
|
|
93
|
+
let autoApi = Boolean(getApiToken());
|
|
98
94
|
if (options.api || autoApi) {
|
|
99
95
|
diagnostics.connectivity.checked = true;
|
|
100
96
|
if (!config.apiKey) {
|
|
101
97
|
diagnostics.connectivity.ok = false;
|
|
102
98
|
diagnostics.connectivity.error = 'Missing API token (VIZZLY_TOKEN)';
|
|
103
99
|
hasErrors = true;
|
|
104
|
-
|
|
100
|
+
output.error('Missing API token for connectivity check');
|
|
105
101
|
} else {
|
|
106
|
-
|
|
102
|
+
output.progress('Checking API connectivity...');
|
|
107
103
|
try {
|
|
108
|
-
|
|
104
|
+
let api = new ApiService({
|
|
109
105
|
baseUrl: config.apiUrl,
|
|
110
106
|
token: config.apiKey,
|
|
111
107
|
command: 'doctor'
|
|
@@ -115,26 +111,26 @@ export async function doctorCommand(options = {}, globalOptions = {}) {
|
|
|
115
111
|
limit: 1
|
|
116
112
|
});
|
|
117
113
|
diagnostics.connectivity.ok = true;
|
|
118
|
-
|
|
114
|
+
output.success('API connectivity OK');
|
|
119
115
|
} catch (err) {
|
|
120
116
|
diagnostics.connectivity.ok = false;
|
|
121
117
|
diagnostics.connectivity.error = err?.message || String(err);
|
|
122
118
|
hasErrors = true;
|
|
123
|
-
|
|
119
|
+
output.error('API connectivity failed', err);
|
|
124
120
|
}
|
|
125
121
|
}
|
|
126
122
|
}
|
|
127
123
|
|
|
128
124
|
// Summary
|
|
129
125
|
if (hasErrors) {
|
|
130
|
-
|
|
126
|
+
output.warn('Preflight completed with issues.');
|
|
131
127
|
} else {
|
|
132
|
-
|
|
128
|
+
output.success('Preflight passed.');
|
|
133
129
|
}
|
|
134
130
|
|
|
135
131
|
// Emit structured data in json/verbose modes
|
|
136
132
|
if (globalOptions.json || globalOptions.verbose) {
|
|
137
|
-
|
|
133
|
+
output.data({
|
|
138
134
|
passed: !hasErrors,
|
|
139
135
|
diagnostics,
|
|
140
136
|
timestamp: new Date().toISOString()
|
|
@@ -142,9 +138,9 @@ export async function doctorCommand(options = {}, globalOptions = {}) {
|
|
|
142
138
|
}
|
|
143
139
|
} catch (error) {
|
|
144
140
|
hasErrors = true;
|
|
145
|
-
|
|
141
|
+
output.error('Failed to run preflight', error);
|
|
146
142
|
} finally {
|
|
147
|
-
|
|
143
|
+
output.cleanup();
|
|
148
144
|
if (hasErrors) process.exit(1);
|
|
149
145
|
}
|
|
150
146
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { loadConfig } from '../utils/config-loader.js';
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
2
|
+
import * as output from '../utils/output.js';
|
|
3
|
+
import { createServices } from '../services/index.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Finalize command implementation
|
|
@@ -9,52 +9,53 @@ import { createServiceContainer } from '../container/index.js';
|
|
|
9
9
|
* @param {Object} globalOptions - Global CLI options
|
|
10
10
|
*/
|
|
11
11
|
export async function finalizeCommand(parallelId, options = {}, globalOptions = {}) {
|
|
12
|
-
|
|
13
|
-
const ui = new ConsoleUI({
|
|
12
|
+
output.configure({
|
|
14
13
|
json: globalOptions.json,
|
|
15
14
|
verbose: globalOptions.verbose,
|
|
16
15
|
color: !globalOptions.noColor
|
|
17
16
|
});
|
|
18
17
|
try {
|
|
19
18
|
// Load configuration with CLI overrides
|
|
20
|
-
|
|
19
|
+
let allOptions = {
|
|
21
20
|
...globalOptions,
|
|
22
21
|
...options
|
|
23
22
|
};
|
|
24
|
-
|
|
23
|
+
let config = await loadConfig(globalOptions.config, allOptions);
|
|
25
24
|
|
|
26
25
|
// Validate API token
|
|
27
26
|
if (!config.apiKey) {
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
output.error('API token required. Use --token or set VIZZLY_TOKEN environment variable');
|
|
28
|
+
process.exit(1);
|
|
30
29
|
}
|
|
31
30
|
if (globalOptions.verbose) {
|
|
32
|
-
|
|
31
|
+
output.info('Configuration loaded');
|
|
32
|
+
output.debug('Config details', {
|
|
33
33
|
parallelId,
|
|
34
34
|
apiUrl: config.apiUrl
|
|
35
35
|
});
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
// Create
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
38
|
+
// Create services and get API service
|
|
39
|
+
output.startSpinner('Finalizing parallel build...');
|
|
40
|
+
let services = createServices(config, 'finalize');
|
|
41
|
+
let apiService = services.apiService;
|
|
42
|
+
output.stopSpinner();
|
|
43
43
|
|
|
44
44
|
// Call finalize endpoint
|
|
45
|
-
|
|
45
|
+
let result = await apiService.finalizeParallelBuild(parallelId);
|
|
46
46
|
if (globalOptions.json) {
|
|
47
|
-
|
|
47
|
+
output.data(result);
|
|
48
48
|
} else {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
output.success(`Parallel build ${result.build.id} finalized successfully`);
|
|
50
|
+
output.info(`Status: ${result.build.status}`);
|
|
51
|
+
output.info(`Parallel ID: ${result.build.parallel_id}`);
|
|
52
52
|
}
|
|
53
53
|
} catch (error) {
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
output.stopSpinner();
|
|
55
|
+
output.error('Failed to finalize parallel build', error);
|
|
56
|
+
process.exit(1);
|
|
56
57
|
} finally {
|
|
57
|
-
|
|
58
|
+
output.cleanup();
|
|
58
59
|
}
|
|
59
60
|
}
|
|
60
61
|
|
|
@@ -64,7 +65,7 @@ export async function finalizeCommand(parallelId, options = {}, globalOptions =
|
|
|
64
65
|
* @param {Object} options - Command options
|
|
65
66
|
*/
|
|
66
67
|
export function validateFinalizeOptions(parallelId, _options) {
|
|
67
|
-
|
|
68
|
+
let errors = [];
|
|
68
69
|
if (!parallelId || parallelId.trim() === '') {
|
|
69
70
|
errors.push('Parallel ID is required');
|
|
70
71
|
}
|