@vizzly-testing/cli 0.35.2 → 0.35.3-beta.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/README.md +8 -5
- package/dist/api/client.js +20 -5
- package/dist/api/core.js +17 -3
- package/dist/api/endpoints.js +24 -3
- package/dist/api/index.js +1 -1
- package/dist/cli.js +25 -65
- package/dist/commands/baselines.js +0 -7
- package/dist/commands/comparisons.js +52 -45
- package/dist/commands/config-cmd.js +0 -7
- package/dist/commands/context.js +444 -238
- package/dist/commands/doctor.js +0 -8
- package/dist/commands/init.js +7 -12
- package/dist/commands/login.js +0 -13
- package/dist/commands/logout.js +0 -12
- package/dist/commands/orgs.js +0 -9
- package/dist/commands/run.js +84 -29
- package/dist/commands/status.js +276 -59
- package/dist/commands/tdd.js +19 -3
- package/dist/commands/whoami.js +0 -12
- package/dist/context/local-workspace-provider.js +293 -37
- package/dist/plugin-loader.js +1 -1
- package/dist/reporter/reporter-bundle.css +1 -1
- package/dist/reporter/reporter-bundle.iife.js +17 -17
- package/dist/server/routers/events.js +4 -4
- package/dist/test-runner/operations.js +70 -28
- package/dist/types/index.d.ts +4 -2
- package/dist/uploader/core.js +6 -6
- package/dist/uploader/operations.js +4 -1
- package/dist/utils/visual-context-normalizers.js +455 -0
- package/package.json +13 -13
- package/skills/vizzly/SKILL.md +68 -101
- package/skills/vizzly/agents/openai.yaml +2 -2
- package/skills/vizzly/references/cli-context.md +79 -27
- package/skills/vizzly/references/dynamic-content.md +28 -33
- package/skills/vizzly/references/sdks.md +26 -39
- package/skills/vizzly/references/setup-ci.md +44 -20
- package/dist/config/index.js +0 -13
- package/dist/project/index.js +0 -13
- package/dist/tdd/index.js +0 -19
- package/dist/tdd/services/baseline-downloader.js +0 -151
- package/dist/tdd/services/hotspot-service.js +0 -58
- package/dist/utils/environment.js +0 -109
- package/skills/vizzly/references/public-screenshots.md +0 -44
package/dist/commands/status.js
CHANGED
|
@@ -3,18 +3,17 @@
|
|
|
3
3
|
* Uses functional API operations directly
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { createApiClient as defaultCreateApiClient,
|
|
6
|
+
import { createApiClient as defaultCreateApiClient, getBuildStatus as defaultGetBuildStatus, getPreviewInfo as defaultGetPreviewInfo } from '../api/index.js';
|
|
7
7
|
import { getAppBaseUrl } from '../utils/api-url.js';
|
|
8
8
|
import { loadConfig as defaultLoadConfig } from '../utils/config-loader.js';
|
|
9
|
-
import { getApiUrl as defaultGetApiUrl } from '../utils/environment-config.js';
|
|
10
9
|
import * as defaultOutput from '../utils/output.js';
|
|
10
|
+
import { getVisualReviewState } from '../utils/visual-context-normalizers.js';
|
|
11
11
|
function createStatusDeps(deps = {}) {
|
|
12
12
|
return {
|
|
13
13
|
loadConfig: deps.loadConfig || defaultLoadConfig,
|
|
14
14
|
createApiClient: deps.createApiClient || defaultCreateApiClient,
|
|
15
|
-
|
|
15
|
+
getBuildStatus: deps.getBuildStatus || defaultGetBuildStatus,
|
|
16
16
|
getPreviewInfo: deps.getPreviewInfo || defaultGetPreviewInfo,
|
|
17
|
-
getApiUrl: deps.getApiUrl || defaultGetApiUrl,
|
|
18
17
|
output: deps.output || defaultOutput,
|
|
19
18
|
exit: deps.exit || (code => process.exit(code))
|
|
20
19
|
};
|
|
@@ -32,7 +31,7 @@ function createStatusClient({
|
|
|
32
31
|
}) {
|
|
33
32
|
return createApiClient({
|
|
34
33
|
baseUrl: config.apiUrl,
|
|
35
|
-
token: config.apiKey,
|
|
34
|
+
token: config.apiKey || config.userToken,
|
|
36
35
|
command: 'status'
|
|
37
36
|
});
|
|
38
37
|
}
|
|
@@ -52,10 +51,108 @@ async function fetchOptionalPreviewInfo(getPreviewInfo, client, buildId) {
|
|
|
52
51
|
export function normalizeBuildStatus(buildStatus) {
|
|
53
52
|
return buildStatus.build || buildStatus;
|
|
54
53
|
}
|
|
55
|
-
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Read exact processing counts from canonical or legacy status responses.
|
|
57
|
+
*
|
|
58
|
+
* Legacy `pending_screenshots` is review state, not queue state, so it is
|
|
59
|
+
* deliberately excluded. Missing processing fields remain missing instead of
|
|
60
|
+
* becoming client-authored zeroes.
|
|
61
|
+
*
|
|
62
|
+
* @param {Object} status - Canonical status bundle or legacy build record.
|
|
63
|
+
* @returns {Object|undefined} API-provided processing facts when available.
|
|
64
|
+
*/
|
|
65
|
+
export function getProcessingStatus(status = {}) {
|
|
66
|
+
if (status.processing) {
|
|
67
|
+
return status.processing;
|
|
68
|
+
}
|
|
69
|
+
let build = normalizeBuildStatus(status);
|
|
70
|
+
let processing = {};
|
|
71
|
+
let fields = [['total', build.screenshot_count], ['completed', build.completed_jobs], ['failed', build.failed_jobs], ['active', build.processing_screenshots]];
|
|
72
|
+
for (let [name, value] of fields) {
|
|
73
|
+
if (value != null) {
|
|
74
|
+
processing[name] = value;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return Object.keys(processing).length > 0 ? processing : undefined;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Read comparison totals without deriving them from screenshot counts.
|
|
82
|
+
*
|
|
83
|
+
* @param {Object} status - Canonical status bundle or legacy build record.
|
|
84
|
+
* @returns {Object|undefined} API-provided comparison facts when available.
|
|
85
|
+
*/
|
|
86
|
+
export function getComparisonStatus(status = {}) {
|
|
87
|
+
if (status.comparisons) {
|
|
88
|
+
return status.comparisons;
|
|
89
|
+
}
|
|
90
|
+
let build = normalizeBuildStatus(status);
|
|
91
|
+
let comparisons = {};
|
|
92
|
+
let fields = [['total', build.total_comparisons], ['new', build.new_comparisons], ['changed', build.changed_comparisons], ['identical', build.identical_comparisons]];
|
|
93
|
+
for (let [name, value] of fields) {
|
|
94
|
+
if (value != null) {
|
|
95
|
+
comparisons[name] = value;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return Object.keys(comparisons).length > 0 ? comparisons : undefined;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Prefer canonical Cricket review state while retaining the legacy fallback.
|
|
103
|
+
*
|
|
104
|
+
* Processing status stays separate: a pending review never means a screenshot
|
|
105
|
+
* is still being processed.
|
|
106
|
+
*
|
|
107
|
+
* @param {Object} status - Canonical status bundle or legacy build record.
|
|
108
|
+
* @returns {string|null} API-provided review state when available.
|
|
109
|
+
*/
|
|
110
|
+
export function getBuildReviewState(status = {}) {
|
|
111
|
+
let build = normalizeBuildStatus(status);
|
|
112
|
+
return getVisualReviewState(build) || getVisualReviewState(status.visualReview?.build || status.visual_review) || null;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Give agents concrete paths from lifecycle status into visual evidence.
|
|
117
|
+
*
|
|
118
|
+
* @param {Object} build - Build record from the status response.
|
|
119
|
+
* @returns {{label: string, command: string}[]} Executable CLI commands.
|
|
120
|
+
*/
|
|
121
|
+
export function createStatusSuggestedCommands(build = {}) {
|
|
122
|
+
if (!build.id) {
|
|
123
|
+
return [];
|
|
124
|
+
}
|
|
125
|
+
return [{
|
|
126
|
+
label: 'Inspect build context',
|
|
127
|
+
command: `vizzly --json context build ${build.id} --agent --source cloud`
|
|
128
|
+
}, {
|
|
129
|
+
label: 'List comparisons',
|
|
130
|
+
command: `vizzly --json comparisons --build ${build.id}`
|
|
131
|
+
}];
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Preserve the established JSON fields while exposing canonical status facts.
|
|
136
|
+
*
|
|
137
|
+
* Every lifecycle, processing, comparison, and review value comes directly
|
|
138
|
+
* from the API. Undefined values intentionally disappear during JSON encoding
|
|
139
|
+
* rather than being presented as false certainty.
|
|
140
|
+
*
|
|
141
|
+
* @param {Object} status - Canonical status bundle or legacy build record.
|
|
142
|
+
* @param {Object|null} previewInfo - Optional preview response.
|
|
143
|
+
* @returns {Object} Machine-readable status payload.
|
|
144
|
+
*/
|
|
145
|
+
export function createStatusData(status, previewInfo = null) {
|
|
146
|
+
let build = normalizeBuildStatus(status);
|
|
147
|
+
let processing = getProcessingStatus(status);
|
|
148
|
+
let comparisons = getComparisonStatus(status);
|
|
149
|
+
let visualReview = build.visual_review || status.visualReview?.build || status.visual_review;
|
|
56
150
|
return {
|
|
151
|
+
resource: status.resource,
|
|
152
|
+
schemaVersion: status.schema_version,
|
|
57
153
|
buildId: build.id,
|
|
58
154
|
status: build.status,
|
|
155
|
+
conclusion: status.conclusion,
|
|
59
156
|
name: build.name,
|
|
60
157
|
createdAt: build.created_at,
|
|
61
158
|
updatedAt: build.updated_at,
|
|
@@ -64,15 +161,24 @@ export function createStatusData(build, previewInfo = null) {
|
|
|
64
161
|
branch: build.branch,
|
|
65
162
|
commit: build.commit_sha,
|
|
66
163
|
commitMessage: build.commit_message,
|
|
67
|
-
screenshotsTotal: build.screenshot_count
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
164
|
+
screenshotsTotal: processing?.total ?? build.screenshot_count,
|
|
165
|
+
processing,
|
|
166
|
+
comparisonsTotal: comparisons?.total ?? build.total_comparisons,
|
|
167
|
+
comparisons,
|
|
168
|
+
newComparisons: comparisons?.new ?? build.new_comparisons,
|
|
169
|
+
changedComparisons: comparisons?.changed ?? build.changed_comparisons,
|
|
170
|
+
identicalComparisons: comparisons?.identical ?? build.identical_comparisons,
|
|
171
|
+
reviewState: getBuildReviewState(status),
|
|
172
|
+
review: status.review,
|
|
173
|
+
reviewFlow: status.reviewFlow,
|
|
174
|
+
visualReview,
|
|
72
175
|
approvalStatus: build.approval_status,
|
|
73
176
|
executionTime: build.execution_time_ms,
|
|
74
177
|
isBaseline: build.is_baseline,
|
|
75
178
|
userAgent: build.user_agent,
|
|
179
|
+
scope: status.scope,
|
|
180
|
+
links: status.links,
|
|
181
|
+
suggestedCommands: createStatusSuggestedCommands(build),
|
|
76
182
|
preview: previewInfo ? {
|
|
77
183
|
url: previewInfo.preview_url,
|
|
78
184
|
status: previewInfo.status,
|
|
@@ -95,11 +201,21 @@ export function createBuildInfo(build) {
|
|
|
95
201
|
}
|
|
96
202
|
return buildInfo;
|
|
97
203
|
}
|
|
98
|
-
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Format API comparison outcomes without filling missing buckets with zeroes.
|
|
207
|
+
*
|
|
208
|
+
* @param {Object} status - Canonical status bundle or legacy build record.
|
|
209
|
+
* @param {Object} colors - Output color helpers.
|
|
210
|
+
* @returns {string} Human-readable comparison summary.
|
|
211
|
+
*/
|
|
212
|
+
export function createComparisonStats(status, colors) {
|
|
213
|
+
let build = normalizeBuildStatus(status);
|
|
214
|
+
let comparisons = getComparisonStatus(status) || {};
|
|
99
215
|
let stats = [];
|
|
100
|
-
let newCount = build.new_comparisons
|
|
101
|
-
let changedCount = build.changed_comparisons
|
|
102
|
-
let identicalCount = build.identical_comparisons
|
|
216
|
+
let newCount = comparisons.new ?? build.new_comparisons;
|
|
217
|
+
let changedCount = comparisons.changed ?? build.changed_comparisons;
|
|
218
|
+
let identicalCount = comparisons.identical ?? build.identical_comparisons;
|
|
103
219
|
if (newCount > 0) {
|
|
104
220
|
stats.push(`${colors.brand.info(newCount)} new`);
|
|
105
221
|
}
|
|
@@ -111,27 +227,47 @@ export function createComparisonStats(build, colors) {
|
|
|
111
227
|
}
|
|
112
228
|
return stats.join(colors.brand.textMuted(' · '));
|
|
113
229
|
}
|
|
114
|
-
|
|
115
|
-
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Build the best available legacy link when the API did not return one.
|
|
233
|
+
*
|
|
234
|
+
* Slug routes match the current app, while the project-ID route remains as a
|
|
235
|
+
* compatibility fallback for older build responses.
|
|
236
|
+
*
|
|
237
|
+
* @param {string} baseUrl - API or app base URL.
|
|
238
|
+
* @param {Object} build - Build record.
|
|
239
|
+
* @param {Object|null} scope - Canonical organization and project scope.
|
|
240
|
+
* @returns {string|null} Build URL when the response has enough identity.
|
|
241
|
+
*/
|
|
242
|
+
export function createBuildUrl(baseUrl, build, scope = null) {
|
|
243
|
+
if (!baseUrl) {
|
|
116
244
|
return null;
|
|
117
245
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
}
|
|
123
|
-
export function getProcessingProgress(build) {
|
|
124
|
-
if (build.status !== 'processing' && build.status !== 'pending') {
|
|
125
|
-
return null;
|
|
246
|
+
let organizationSlug = scope?.organization?.slug || build.organization_slug || build.org_slug;
|
|
247
|
+
let projectSlug = scope?.project?.slug || build.project_slug;
|
|
248
|
+
let appBaseUrl = getAppBaseUrl(baseUrl);
|
|
249
|
+
if (organizationSlug && projectSlug) {
|
|
250
|
+
return `${appBaseUrl}/${organizationSlug}/${projectSlug}/builds/${build.id}`;
|
|
126
251
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
let processingScreenshots = build.processing_screenshots || 0;
|
|
130
|
-
let totalJobs = completedJobs + failedJobs + processingScreenshots;
|
|
131
|
-
if (totalJobs <= 0) {
|
|
132
|
-
return null;
|
|
252
|
+
if (build.project_id) {
|
|
253
|
+
return `${appBaseUrl}/projects/${build.project_id}/builds/${build.id}`;
|
|
133
254
|
}
|
|
134
|
-
return
|
|
255
|
+
return null;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Preserve status failure behavior while accepting the canonical conclusion.
|
|
260
|
+
*
|
|
261
|
+
* Review-required and rejected builds keep their existing successful command
|
|
262
|
+
* exit behavior. Only build or processing failures produce a failing status.
|
|
263
|
+
*
|
|
264
|
+
* @param {Object} status - Canonical status bundle or legacy build record.
|
|
265
|
+
* @returns {boolean} Whether human status should exit non-zero.
|
|
266
|
+
*/
|
|
267
|
+
export function shouldFailStatus(status) {
|
|
268
|
+
let build = normalizeBuildStatus(status);
|
|
269
|
+
let processing = getProcessingStatus(status);
|
|
270
|
+
return build.status === 'failed' || ['build_failed', 'processing_failed'].includes(status.conclusion) || (processing?.failed ?? 0) > 0;
|
|
135
271
|
}
|
|
136
272
|
function writeStatusTiming({
|
|
137
273
|
build,
|
|
@@ -149,10 +285,46 @@ function writeStatusTiming({
|
|
|
149
285
|
output.hint(`Took ${Math.round(build.execution_time_ms / 1000)}s`);
|
|
150
286
|
}
|
|
151
287
|
}
|
|
152
|
-
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Render follow-up commands after the factual status summary.
|
|
291
|
+
*
|
|
292
|
+
* Status answers lifecycle questions; these commands are the explicit bridge
|
|
293
|
+
* to screenshot evidence without bloating the status response itself.
|
|
294
|
+
*
|
|
295
|
+
* @param {Object} build - Build record from the status response.
|
|
296
|
+
* @param {Object} output - Configured output boundary.
|
|
297
|
+
*/
|
|
298
|
+
function writeSuggestedCommands({
|
|
299
|
+
build,
|
|
300
|
+
output
|
|
301
|
+
}) {
|
|
302
|
+
let commands = createStatusSuggestedCommands(build);
|
|
303
|
+
if (commands.length === 0) {
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
let colors = output.getColors();
|
|
307
|
+
output.blank();
|
|
308
|
+
output.print(' Suggested commands');
|
|
309
|
+
for (let item of commands) {
|
|
310
|
+
output.print(` ${colors.brand.textMuted(item.command)}`);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Add optional API facts without inventing placeholders for missing metadata.
|
|
316
|
+
*
|
|
317
|
+
* @param {Object} status - Canonical status bundle or legacy build record.
|
|
318
|
+
* @returns {Object} Available verbose fields.
|
|
319
|
+
*/
|
|
320
|
+
function createVerboseInfo(status) {
|
|
321
|
+
let build = normalizeBuildStatus(status);
|
|
153
322
|
let verboseInfo = {};
|
|
154
|
-
if (
|
|
155
|
-
|
|
323
|
+
if (status.review) {
|
|
324
|
+
let reviewParts = [['approved', status.review.approved], ['rejected', status.review.rejected], ['pending', status.review.pending]].filter(([, value]) => value != null).map(([label, value]) => `${value} ${label}`);
|
|
325
|
+
if (reviewParts.length > 0) {
|
|
326
|
+
verboseInfo.Review = reviewParts.join(', ');
|
|
327
|
+
}
|
|
156
328
|
}
|
|
157
329
|
if (build.avg_diff_percentage != null) {
|
|
158
330
|
verboseInfo['Avg Diff'] = `${(build.avg_diff_percentage * 100).toFixed(2)}%`;
|
|
@@ -163,29 +335,72 @@ function createVerboseInfo(build) {
|
|
|
163
335
|
if (build.is_baseline) {
|
|
164
336
|
verboseInfo.Baseline = 'Yes';
|
|
165
337
|
}
|
|
166
|
-
|
|
338
|
+
if (build.user_agent) {
|
|
339
|
+
verboseInfo['User Agent'] = build.user_agent;
|
|
340
|
+
}
|
|
167
341
|
verboseInfo['Build ID'] = build.id;
|
|
168
|
-
|
|
342
|
+
if (status.scope?.project?.id || build.project_id) {
|
|
343
|
+
verboseInfo['Project ID'] = status.scope?.project?.id || build.project_id;
|
|
344
|
+
}
|
|
169
345
|
return verboseInfo;
|
|
170
346
|
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Format only the processing facts supplied by the API.
|
|
350
|
+
*
|
|
351
|
+
* @param {Object|undefined} processing - Canonical processing counts.
|
|
352
|
+
* @returns {string} Human-readable processing summary.
|
|
353
|
+
*/
|
|
354
|
+
function formatProcessingStatus(processing) {
|
|
355
|
+
if (!processing) {
|
|
356
|
+
return '';
|
|
357
|
+
}
|
|
358
|
+
let labels = {
|
|
359
|
+
total: 'total',
|
|
360
|
+
completed: 'completed',
|
|
361
|
+
failed: 'failed',
|
|
362
|
+
active: 'active',
|
|
363
|
+
pending: 'pending'
|
|
364
|
+
};
|
|
365
|
+
return Object.entries(labels).filter(([field]) => processing[field] != null).map(([field, label]) => `${processing[field]} ${label}`).join(' · ');
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Present lifecycle, processing, comparisons, and review as separate facts.
|
|
370
|
+
*
|
|
371
|
+
* Keeping these lanes separate prevents review-pending counts from looking
|
|
372
|
+
* like unfinished processing and keeps the human view aligned with JSON.
|
|
373
|
+
*
|
|
374
|
+
* @param {Object} options - Human status presentation inputs.
|
|
375
|
+
*/
|
|
171
376
|
function writeHumanStatus({
|
|
172
377
|
build,
|
|
173
378
|
buildUrl,
|
|
174
379
|
globalOptions,
|
|
175
380
|
output,
|
|
176
|
-
previewInfo
|
|
381
|
+
previewInfo,
|
|
382
|
+
status
|
|
177
383
|
}) {
|
|
178
384
|
output.header('status', build.status);
|
|
179
385
|
output.keyValue(createBuildInfo(build));
|
|
180
386
|
output.blank();
|
|
181
387
|
let colors = output.getColors();
|
|
182
|
-
let comparisonStats = createComparisonStats(
|
|
183
|
-
|
|
388
|
+
let comparisonStats = createComparisonStats(status, colors);
|
|
389
|
+
let processing = getProcessingStatus(status);
|
|
390
|
+
let screenshotsTotal = processing?.total ?? build.screenshot_count;
|
|
391
|
+
let processingSummary = formatProcessingStatus(processing);
|
|
392
|
+
if (screenshotsTotal != null) {
|
|
393
|
+
output.labelValue('Screenshots', String(screenshotsTotal));
|
|
394
|
+
}
|
|
395
|
+
if (processingSummary) {
|
|
396
|
+
output.labelValue('Processing', processingSummary);
|
|
397
|
+
}
|
|
184
398
|
if (comparisonStats) {
|
|
185
399
|
output.labelValue('Comparisons', comparisonStats);
|
|
186
400
|
}
|
|
187
|
-
|
|
188
|
-
|
|
401
|
+
let reviewState = getBuildReviewState(status);
|
|
402
|
+
if (reviewState) {
|
|
403
|
+
output.labelValue('Review', reviewState);
|
|
189
404
|
}
|
|
190
405
|
output.blank();
|
|
191
406
|
writeStatusTiming({
|
|
@@ -207,17 +422,20 @@ function writeHumanStatus({
|
|
|
207
422
|
output.blank();
|
|
208
423
|
output.divider();
|
|
209
424
|
output.blank();
|
|
210
|
-
output.keyValue(createVerboseInfo(
|
|
211
|
-
}
|
|
212
|
-
let progress = getProcessingProgress(build);
|
|
213
|
-
if (progress !== null) {
|
|
214
|
-
output.blank();
|
|
215
|
-
output.print(` ${output.progressBar(progress, 100)} ${Math.round(progress)}%`);
|
|
425
|
+
output.keyValue(createVerboseInfo(status));
|
|
216
426
|
}
|
|
427
|
+
writeSuggestedCommands({
|
|
428
|
+
build,
|
|
429
|
+
output
|
|
430
|
+
});
|
|
217
431
|
}
|
|
218
432
|
|
|
219
433
|
/**
|
|
220
|
-
*
|
|
434
|
+
* Fetch and present the server-owned build status contract.
|
|
435
|
+
*
|
|
436
|
+
* Preview remains an optional adjacent resource, and established 5xx and
|
|
437
|
+
* build-failure exit semantics remain unchanged.
|
|
438
|
+
*
|
|
221
439
|
* @param {string} buildId - Build ID to check status for
|
|
222
440
|
* @param {Object} options - Command options
|
|
223
441
|
* @param {Object} globalOptions - Global CLI options
|
|
@@ -227,9 +445,8 @@ export async function statusCommand(buildId, options = {}, globalOptions = {}, d
|
|
|
227
445
|
let {
|
|
228
446
|
loadConfig,
|
|
229
447
|
createApiClient,
|
|
230
|
-
|
|
448
|
+
getBuildStatus,
|
|
231
449
|
getPreviewInfo,
|
|
232
|
-
getApiUrl,
|
|
233
450
|
output,
|
|
234
451
|
exit
|
|
235
452
|
} = createStatusDeps(deps);
|
|
@@ -243,8 +460,8 @@ export async function statusCommand(buildId, options = {}, globalOptions = {}, d
|
|
|
243
460
|
let config = await loadConfig(globalOptions.config, allOptions);
|
|
244
461
|
|
|
245
462
|
// Validate API token
|
|
246
|
-
if (!config.apiKey) {
|
|
247
|
-
output.error('
|
|
463
|
+
if (!config.apiKey && !config.userToken) {
|
|
464
|
+
output.error('Authentication required. Use --token, set VIZZLY_TOKEN, or run "vizzly login"');
|
|
248
465
|
output.cleanup();
|
|
249
466
|
exit(1);
|
|
250
467
|
return {
|
|
@@ -261,7 +478,7 @@ export async function statusCommand(buildId, options = {}, globalOptions = {}, d
|
|
|
261
478
|
createApiClient,
|
|
262
479
|
config
|
|
263
480
|
});
|
|
264
|
-
let buildStatus = await
|
|
481
|
+
let buildStatus = await getBuildStatus(client, buildId);
|
|
265
482
|
|
|
266
483
|
// Also fetch preview info (if exists)
|
|
267
484
|
let previewInfo = await fetchOptionalPreviewInfo(getPreviewInfo, client, buildId);
|
|
@@ -272,26 +489,26 @@ export async function statusCommand(buildId, options = {}, globalOptions = {}, d
|
|
|
272
489
|
|
|
273
490
|
// Output in JSON mode
|
|
274
491
|
if (globalOptions.json) {
|
|
275
|
-
output.data(createStatusData(
|
|
492
|
+
output.data(createStatusData(buildStatus, previewInfo));
|
|
276
493
|
output.cleanup();
|
|
277
494
|
return;
|
|
278
495
|
}
|
|
279
496
|
|
|
280
497
|
// Human-readable output
|
|
281
498
|
// Show build URL if we can construct it
|
|
282
|
-
let
|
|
283
|
-
let buildUrl = createBuildUrl(baseUrl, build);
|
|
499
|
+
let buildUrl = buildStatus.links?.web || createBuildUrl(config.apiUrl, build, buildStatus.scope);
|
|
284
500
|
writeHumanStatus({
|
|
285
501
|
build,
|
|
286
502
|
buildUrl,
|
|
287
503
|
globalOptions,
|
|
288
504
|
output,
|
|
289
|
-
previewInfo
|
|
505
|
+
previewInfo,
|
|
506
|
+
status: buildStatus
|
|
290
507
|
});
|
|
291
508
|
output.cleanup();
|
|
292
509
|
|
|
293
510
|
// Exit with appropriate code based on build status
|
|
294
|
-
if (shouldFailStatus(
|
|
511
|
+
if (shouldFailStatus(buildStatus)) {
|
|
295
512
|
exit(1);
|
|
296
513
|
}
|
|
297
514
|
} catch (error) {
|
package/dist/commands/tdd.js
CHANGED
|
@@ -16,8 +16,22 @@ import { initializeDaemon as defaultInitializeDaemon, runTests as defaultRunTest
|
|
|
16
16
|
import { loadConfig as defaultLoadConfig } from '../utils/config-loader.js';
|
|
17
17
|
import { detectBranch as defaultDetectBranch, detectCommit as defaultDetectCommit, detectCommitMessage as defaultDetectCommitMessage, detectPullRequestNumber as defaultDetectPullRequestNumber, generateBuildNameWithGit as defaultGenerateBuildNameWithGit } from '../utils/git.js';
|
|
18
18
|
import * as defaultOutput from '../utils/output.js';
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Build the follow-up command for local TDD evidence.
|
|
22
|
+
*
|
|
23
|
+
* JSON consumers need a self-contained command that returns structured
|
|
24
|
+
* evidence when executed. Human output keeps the existing readable summary.
|
|
25
|
+
*
|
|
26
|
+
* @param {Object} options - Command output options.
|
|
27
|
+
* @param {boolean} [options.structured=false] - Include machine-readable JSON.
|
|
28
|
+
* @returns {string} Executable local context command.
|
|
29
|
+
*/
|
|
30
|
+
function buildLocalContextCommand({
|
|
31
|
+
structured = false
|
|
32
|
+
} = {}) {
|
|
33
|
+
let jsonFlag = structured ? ' --json' : '';
|
|
34
|
+
return `vizzly context build current --source local --agent${jsonFlag}`;
|
|
21
35
|
}
|
|
22
36
|
|
|
23
37
|
/**
|
|
@@ -257,7 +271,9 @@ export async function tddCommand(testCommand, options = {}, globalOptions = {},
|
|
|
257
271
|
comparisons,
|
|
258
272
|
summary,
|
|
259
273
|
reportPath: runResult.reportPath || '.vizzly/report/index.html',
|
|
260
|
-
contextCommand: buildLocalContextCommand(
|
|
274
|
+
contextCommand: buildLocalContextCommand({
|
|
275
|
+
structured: true
|
|
276
|
+
})
|
|
261
277
|
});
|
|
262
278
|
output.cleanup();
|
|
263
279
|
}
|
package/dist/commands/whoami.js
CHANGED
|
@@ -182,16 +182,4 @@ export async function whoamiCommand(options = {}, globalOptions = {}, deps = {})
|
|
|
182
182
|
exit(1);
|
|
183
183
|
}
|
|
184
184
|
}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
/**
|
|
188
|
-
* Validate whoami options
|
|
189
|
-
* @param {Object} options - Command options
|
|
190
|
-
*/
|
|
191
|
-
export function validateWhoamiOptions() {
|
|
192
|
-
let errors = [];
|
|
193
|
-
|
|
194
|
-
// No specific validation needed for whoami command
|
|
195
|
-
|
|
196
|
-
return errors;
|
|
197
185
|
}
|