@vizzly-testing/cli 0.14.0 → 0.15.1
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 +70 -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 +74 -41
- 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/screenshot-display.d.ts +16 -0
- 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 +1 -4
- package/dist/types/reporter/src/components/views/comparisons-view.d.ts +1 -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 +33 -34
- 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,40 @@ 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,
|
|
54
|
+
// Backwards compatibility alias for plugins using old API
|
|
55
|
+
logger: output
|
|
51
56
|
});
|
|
52
57
|
let timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error('Plugin registration timeout (5s)')), 5000));
|
|
53
58
|
await Promise.race([registerPromise, timeoutPromise]);
|
|
54
|
-
|
|
59
|
+
output.debug(`Registered plugin: ${plugin.name}`);
|
|
55
60
|
} catch (error) {
|
|
56
|
-
|
|
61
|
+
output.warn(`Failed to register plugin ${plugin.name}: ${error.message}`);
|
|
57
62
|
}
|
|
58
63
|
}
|
|
59
64
|
} catch (error) {
|
|
60
|
-
|
|
65
|
+
output.debug(`Plugin loading failed: ${error.message}`);
|
|
61
66
|
}
|
|
62
67
|
program.command('init').description('Initialize Vizzly in your project').option('--force', 'Overwrite existing configuration').action(async options => {
|
|
63
|
-
|
|
68
|
+
let globalOptions = program.opts();
|
|
64
69
|
await init({
|
|
65
70
|
...globalOptions,
|
|
66
71
|
...options,
|
|
@@ -68,24 +73,24 @@ program.command('init').description('Initialize Vizzly in your project').option(
|
|
|
68
73
|
});
|
|
69
74
|
});
|
|
70
75
|
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
|
-
|
|
76
|
+
let globalOptions = program.opts();
|
|
72
77
|
|
|
73
78
|
// Validate options
|
|
74
|
-
|
|
79
|
+
let validationErrors = validateUploadOptions(path, options);
|
|
75
80
|
if (validationErrors.length > 0) {
|
|
76
|
-
|
|
77
|
-
validationErrors.forEach(error =>
|
|
81
|
+
output.error('Validation errors:');
|
|
82
|
+
validationErrors.forEach(error => output.printErr(` - ${error}`));
|
|
78
83
|
process.exit(1);
|
|
79
84
|
}
|
|
80
85
|
await uploadCommand(path, options, globalOptions);
|
|
81
86
|
});
|
|
82
87
|
|
|
83
88
|
// TDD command with subcommands - Local visual testing with interactive dashboard
|
|
84
|
-
|
|
89
|
+
let tddCmd = program.command('tdd').description('Run tests in TDD mode with local visual comparisons');
|
|
85
90
|
|
|
86
91
|
// TDD Start - Background server
|
|
87
92
|
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
|
-
|
|
93
|
+
let globalOptions = program.opts();
|
|
89
94
|
|
|
90
95
|
// If this is a daemon child process, run the server directly
|
|
91
96
|
if (options.daemonChild) {
|
|
@@ -97,34 +102,34 @@ tddCmd.command('start').description('Start background TDD server with dashboard'
|
|
|
97
102
|
|
|
98
103
|
// TDD Stop - Kill background server
|
|
99
104
|
tddCmd.command('stop').description('Stop background TDD server').action(async options => {
|
|
100
|
-
|
|
105
|
+
let globalOptions = program.opts();
|
|
101
106
|
await tddStopCommand(options, globalOptions);
|
|
102
107
|
});
|
|
103
108
|
|
|
104
109
|
// TDD Status - Check server status
|
|
105
110
|
tddCmd.command('status').description('Check TDD server status').action(async options => {
|
|
106
|
-
|
|
111
|
+
let globalOptions = program.opts();
|
|
107
112
|
await tddStatusCommand(options, globalOptions);
|
|
108
113
|
});
|
|
109
114
|
|
|
110
115
|
// TDD Run - One-off test run with ephemeral server (generates static report)
|
|
111
116
|
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
|
-
|
|
117
|
+
let globalOptions = program.opts();
|
|
113
118
|
|
|
114
119
|
// Validate options
|
|
115
|
-
|
|
120
|
+
let validationErrors = validateTddOptions(command, options);
|
|
116
121
|
if (validationErrors.length > 0) {
|
|
117
|
-
|
|
118
|
-
validationErrors.forEach(error =>
|
|
122
|
+
output.error('Validation errors:');
|
|
123
|
+
validationErrors.forEach(error => output.printErr(` - ${error}`));
|
|
119
124
|
process.exit(1);
|
|
120
125
|
}
|
|
121
|
-
|
|
126
|
+
let {
|
|
122
127
|
result,
|
|
123
128
|
cleanup
|
|
124
129
|
} = await tddCommand(command, options, globalOptions);
|
|
125
130
|
|
|
126
131
|
// Set up cleanup on process signals
|
|
127
|
-
|
|
132
|
+
let handleCleanup = async () => {
|
|
128
133
|
await cleanup();
|
|
129
134
|
};
|
|
130
135
|
process.once('SIGINT', () => {
|
|
@@ -140,122 +145,119 @@ tddCmd.command('run <command>').description('Run tests once in TDD mode with loc
|
|
|
140
145
|
await cleanup();
|
|
141
146
|
});
|
|
142
147
|
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
|
-
|
|
148
|
+
let globalOptions = program.opts();
|
|
144
149
|
|
|
145
150
|
// Validate options
|
|
146
|
-
|
|
151
|
+
let validationErrors = validateRunOptions(command, options);
|
|
147
152
|
if (validationErrors.length > 0) {
|
|
148
|
-
|
|
149
|
-
validationErrors.forEach(error =>
|
|
153
|
+
output.error('Validation errors:');
|
|
154
|
+
validationErrors.forEach(error => output.printErr(` - ${error}`));
|
|
150
155
|
process.exit(1);
|
|
151
156
|
}
|
|
152
157
|
try {
|
|
153
|
-
|
|
158
|
+
let result = await runCommand(command, options, globalOptions);
|
|
154
159
|
if (result && !result.success && result.exitCode > 0) {
|
|
155
160
|
process.exit(result.exitCode);
|
|
156
161
|
}
|
|
157
162
|
} catch (error) {
|
|
158
|
-
|
|
159
|
-
if (globalOptions.verbose) {
|
|
160
|
-
console.error('Stack trace:', error.stack);
|
|
161
|
-
}
|
|
163
|
+
output.error('Command failed', error);
|
|
162
164
|
process.exit(1);
|
|
163
165
|
}
|
|
164
166
|
});
|
|
165
167
|
program.command('status').description('Check the status of a build').argument('<build-id>', 'Build ID to check status for').action(async (buildId, options) => {
|
|
166
|
-
|
|
168
|
+
let globalOptions = program.opts();
|
|
167
169
|
|
|
168
170
|
// Validate options
|
|
169
|
-
|
|
171
|
+
let validationErrors = validateStatusOptions(buildId, options);
|
|
170
172
|
if (validationErrors.length > 0) {
|
|
171
|
-
|
|
172
|
-
validationErrors.forEach(error =>
|
|
173
|
+
output.error('Validation errors:');
|
|
174
|
+
validationErrors.forEach(error => output.printErr(` - ${error}`));
|
|
173
175
|
process.exit(1);
|
|
174
176
|
}
|
|
175
177
|
await statusCommand(buildId, options, globalOptions);
|
|
176
178
|
});
|
|
177
179
|
program.command('finalize').description('Finalize a parallel build after all shards complete').argument('<parallel-id>', 'Parallel ID to finalize').action(async (parallelId, options) => {
|
|
178
|
-
|
|
180
|
+
let globalOptions = program.opts();
|
|
179
181
|
|
|
180
182
|
// Validate options
|
|
181
|
-
|
|
183
|
+
let validationErrors = validateFinalizeOptions(parallelId, options);
|
|
182
184
|
if (validationErrors.length > 0) {
|
|
183
|
-
|
|
184
|
-
validationErrors.forEach(error =>
|
|
185
|
+
output.error('Validation errors:');
|
|
186
|
+
validationErrors.forEach(error => output.printErr(` - ${error}`));
|
|
185
187
|
process.exit(1);
|
|
186
188
|
}
|
|
187
189
|
await finalizeCommand(parallelId, options, globalOptions);
|
|
188
190
|
});
|
|
189
191
|
program.command('doctor').description('Run diagnostics to check your environment and configuration').option('--api', 'Include API connectivity checks').action(async options => {
|
|
190
|
-
|
|
192
|
+
let globalOptions = program.opts();
|
|
191
193
|
|
|
192
194
|
// Validate options
|
|
193
|
-
|
|
195
|
+
let validationErrors = validateDoctorOptions(options);
|
|
194
196
|
if (validationErrors.length > 0) {
|
|
195
|
-
|
|
196
|
-
validationErrors.forEach(error =>
|
|
197
|
+
output.error('Validation errors:');
|
|
198
|
+
validationErrors.forEach(error => output.printErr(` - ${error}`));
|
|
197
199
|
process.exit(1);
|
|
198
200
|
}
|
|
199
201
|
await doctorCommand(options, globalOptions);
|
|
200
202
|
});
|
|
201
203
|
program.command('login').description('Authenticate with your Vizzly account').option('--api-url <url>', 'API URL override').action(async options => {
|
|
202
|
-
|
|
204
|
+
let globalOptions = program.opts();
|
|
203
205
|
|
|
204
206
|
// Validate options
|
|
205
|
-
|
|
207
|
+
let validationErrors = validateLoginOptions(options);
|
|
206
208
|
if (validationErrors.length > 0) {
|
|
207
|
-
|
|
208
|
-
validationErrors.forEach(error =>
|
|
209
|
+
output.error('Validation errors:');
|
|
210
|
+
validationErrors.forEach(error => output.printErr(` - ${error}`));
|
|
209
211
|
process.exit(1);
|
|
210
212
|
}
|
|
211
213
|
await loginCommand(options, globalOptions);
|
|
212
214
|
});
|
|
213
215
|
program.command('logout').description('Clear stored authentication tokens').option('--api-url <url>', 'API URL override').action(async options => {
|
|
214
|
-
|
|
216
|
+
let globalOptions = program.opts();
|
|
215
217
|
|
|
216
218
|
// Validate options
|
|
217
|
-
|
|
219
|
+
let validationErrors = validateLogoutOptions(options);
|
|
218
220
|
if (validationErrors.length > 0) {
|
|
219
|
-
|
|
220
|
-
validationErrors.forEach(error =>
|
|
221
|
+
output.error('Validation errors:');
|
|
222
|
+
validationErrors.forEach(error => output.printErr(` - ${error}`));
|
|
221
223
|
process.exit(1);
|
|
222
224
|
}
|
|
223
225
|
await logoutCommand(options, globalOptions);
|
|
224
226
|
});
|
|
225
227
|
program.command('whoami').description('Show current authentication status and user information').option('--api-url <url>', 'API URL override').action(async options => {
|
|
226
|
-
|
|
228
|
+
let globalOptions = program.opts();
|
|
227
229
|
|
|
228
230
|
// Validate options
|
|
229
|
-
|
|
231
|
+
let validationErrors = validateWhoamiOptions(options);
|
|
230
232
|
if (validationErrors.length > 0) {
|
|
231
|
-
|
|
232
|
-
validationErrors.forEach(error =>
|
|
233
|
+
output.error('Validation errors:');
|
|
234
|
+
validationErrors.forEach(error => output.printErr(` - ${error}`));
|
|
233
235
|
process.exit(1);
|
|
234
236
|
}
|
|
235
237
|
await whoamiCommand(options, globalOptions);
|
|
236
238
|
});
|
|
237
239
|
program.command('project:select').description('Configure project for current directory').option('--api-url <url>', 'API URL override').action(async options => {
|
|
238
|
-
|
|
240
|
+
let globalOptions = program.opts();
|
|
239
241
|
|
|
240
242
|
// Validate options
|
|
241
|
-
|
|
243
|
+
let validationErrors = validateProjectOptions(options);
|
|
242
244
|
if (validationErrors.length > 0) {
|
|
243
|
-
|
|
244
|
-
validationErrors.forEach(error =>
|
|
245
|
+
output.error('Validation errors:');
|
|
246
|
+
validationErrors.forEach(error => output.printErr(` - ${error}`));
|
|
245
247
|
process.exit(1);
|
|
246
248
|
}
|
|
247
249
|
await projectSelectCommand(options, globalOptions);
|
|
248
250
|
});
|
|
249
251
|
program.command('project:list').description('Show all configured projects').action(async options => {
|
|
250
|
-
|
|
252
|
+
let globalOptions = program.opts();
|
|
251
253
|
await projectListCommand(options, globalOptions);
|
|
252
254
|
});
|
|
253
255
|
program.command('project:token').description('Show project token for current directory').action(async options => {
|
|
254
|
-
|
|
256
|
+
let globalOptions = program.opts();
|
|
255
257
|
await projectTokenCommand(options, globalOptions);
|
|
256
258
|
});
|
|
257
259
|
program.command('project:remove').description('Remove project configuration for current directory').action(async options => {
|
|
258
|
-
|
|
260
|
+
let globalOptions = program.opts();
|
|
259
261
|
await projectRemoveCommand(options, globalOptions);
|
|
260
262
|
});
|
|
261
263
|
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
|
}
|