@vizzly-testing/cli 0.20.1-beta.0 → 0.20.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 +16 -18
- package/dist/cli.js +177 -2
- package/dist/client/index.js +144 -77
- package/dist/commands/doctor.js +118 -33
- package/dist/commands/finalize.js +8 -3
- package/dist/commands/init.js +13 -18
- package/dist/commands/login.js +42 -49
- package/dist/commands/logout.js +13 -5
- package/dist/commands/project.js +95 -67
- package/dist/commands/run.js +32 -6
- package/dist/commands/status.js +81 -50
- package/dist/commands/tdd-daemon.js +61 -32
- package/dist/commands/tdd.js +14 -26
- package/dist/commands/upload.js +18 -9
- package/dist/commands/whoami.js +40 -38
- package/dist/reporter/reporter-bundle.css +1 -1
- package/dist/reporter/reporter-bundle.iife.js +204 -22
- package/dist/server/handlers/tdd-handler.js +113 -7
- package/dist/server/http-server.js +9 -3
- package/dist/server/routers/baseline.js +58 -0
- package/dist/server/routers/dashboard.js +10 -6
- package/dist/server/routers/screenshot.js +32 -0
- package/dist/server-manager/core.js +5 -2
- package/dist/server-manager/operations.js +2 -1
- package/dist/services/config-service.js +306 -0
- package/dist/tdd/tdd-service.js +190 -126
- package/dist/types/client.d.ts +25 -2
- package/dist/utils/colors.js +187 -39
- package/dist/utils/config-loader.js +3 -6
- package/dist/utils/context.js +228 -0
- package/dist/utils/output.js +449 -14
- package/docs/api-reference.md +173 -8
- package/docs/tui-elements.md +560 -0
- package/package.json +13 -7
- package/dist/report-generator/core.js +0 -315
- package/dist/report-generator/index.js +0 -8
- package/dist/report-generator/operations.js +0 -196
- package/dist/services/static-report-generator.js +0 -65
package/dist/utils/colors.js
CHANGED
|
@@ -1,66 +1,214 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Detects terminal color support and
|
|
1
|
+
// Color utility using ansis for rich terminal styling.
|
|
2
|
+
// Detects terminal color support and provides chainable color functions.
|
|
3
3
|
|
|
4
|
+
import ansis from 'ansis';
|
|
5
|
+
|
|
6
|
+
// =============================================================================
|
|
7
|
+
// Vizzly Observatory Design System Colors
|
|
8
|
+
// Aligned with @vizzly-testing/observatory color tokens
|
|
9
|
+
// =============================================================================
|
|
10
|
+
|
|
11
|
+
export let brand = {
|
|
12
|
+
// Primary brand color - Amber is Observatory's signature
|
|
13
|
+
amber: '#F59E0B',
|
|
14
|
+
// Primary brand, actions, highlights
|
|
15
|
+
amberLight: '#FBBF24',
|
|
16
|
+
// Hover states, emphasis
|
|
17
|
+
|
|
18
|
+
// Accent colors (semantic)
|
|
19
|
+
success: '#10B981',
|
|
20
|
+
// Approved, passed, active (--accent-success)
|
|
21
|
+
warning: '#F59E0B',
|
|
22
|
+
// Pending, attention (--accent-warning)
|
|
23
|
+
danger: '#EF4444',
|
|
24
|
+
// Rejected, failed, errors (--accent-danger)
|
|
25
|
+
info: '#3B82F6',
|
|
26
|
+
// Processing, informational (--accent-info)
|
|
27
|
+
|
|
28
|
+
// Surface colors (dark theme)
|
|
29
|
+
bg: '#0F172A',
|
|
30
|
+
// Page background (--vz-bg)
|
|
31
|
+
surface: '#1A2332',
|
|
32
|
+
// Cards, panels (--vz-surface)
|
|
33
|
+
elevated: '#1E293B',
|
|
34
|
+
// Dropdowns, modals (--vz-elevated)
|
|
35
|
+
border: '#374151',
|
|
36
|
+
// Primary borders (--vz-border)
|
|
37
|
+
borderSubtle: '#2D3748',
|
|
38
|
+
// Subtle dividers (--vz-border-subtle)
|
|
39
|
+
|
|
40
|
+
// Text hierarchy
|
|
41
|
+
textPrimary: '#FFFFFF',
|
|
42
|
+
// Headings, important (--text-primary)
|
|
43
|
+
textSecondary: '#9CA3AF',
|
|
44
|
+
// Body text (--text-secondary)
|
|
45
|
+
textTertiary: '#6B7280',
|
|
46
|
+
// Captions, metadata (--text-tertiary)
|
|
47
|
+
textMuted: '#4B5563',
|
|
48
|
+
// Disabled, placeholders (--text-muted)
|
|
49
|
+
|
|
50
|
+
// Legacy aliases (for backward compatibility)
|
|
51
|
+
green: '#10B981',
|
|
52
|
+
red: '#EF4444',
|
|
53
|
+
cyan: '#06B6D4',
|
|
54
|
+
// Still useful for links in terminals
|
|
55
|
+
slate: '#64748B',
|
|
56
|
+
dark: '#1E293B'
|
|
57
|
+
};
|
|
4
58
|
function supportsColorDefault() {
|
|
5
59
|
// Respect NO_COLOR: https://no-color.org/
|
|
6
60
|
if ('NO_COLOR' in process.env) return false;
|
|
7
61
|
|
|
8
62
|
// Respect FORCE_COLOR if set to a truthy value (except '0')
|
|
9
63
|
if ('FORCE_COLOR' in process.env) {
|
|
10
|
-
|
|
64
|
+
let v = process.env.FORCE_COLOR;
|
|
11
65
|
if (v && v !== '0') return true;
|
|
12
66
|
if (v === '0') return false;
|
|
13
67
|
}
|
|
14
68
|
|
|
69
|
+
// COLORTERM indicates truecolor support
|
|
70
|
+
if (process.env.COLORTERM === 'truecolor' || process.env.COLORTERM === '24bit') {
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
|
|
15
74
|
// If stdout is not a TTY, assume no color
|
|
16
75
|
if (!process.stdout || !process.stdout.isTTY) return false;
|
|
17
76
|
|
|
18
77
|
// Prefer getColorDepth when available
|
|
19
78
|
try {
|
|
20
|
-
|
|
79
|
+
let depth = typeof process.stdout.getColorDepth === 'function' ? process.stdout.getColorDepth() : 1;
|
|
21
80
|
return depth && depth > 1;
|
|
22
81
|
} catch {
|
|
23
82
|
// Fallback heuristic
|
|
24
83
|
return true;
|
|
25
84
|
}
|
|
26
85
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Create a colors API with optional color support detection
|
|
89
|
+
* @param {Object} options - Configuration options
|
|
90
|
+
* @param {boolean} [options.useColor] - Force color on/off (auto-detect if undefined)
|
|
91
|
+
* @returns {Object} Colors API with styling functions
|
|
92
|
+
*/
|
|
34
93
|
export function createColors(options = {}) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
94
|
+
let enabled = options.useColor !== undefined ? !!options.useColor : supportsColorDefault();
|
|
95
|
+
if (!enabled) {
|
|
96
|
+
// Return no-op functions when color disabled
|
|
97
|
+
let noop = (input = '') => String(input);
|
|
98
|
+
return {
|
|
99
|
+
// Modifiers
|
|
100
|
+
reset: noop,
|
|
101
|
+
bold: noop,
|
|
102
|
+
dim: noop,
|
|
103
|
+
italic: noop,
|
|
104
|
+
underline: noop,
|
|
105
|
+
strikethrough: noop,
|
|
106
|
+
// Colors
|
|
107
|
+
red: noop,
|
|
108
|
+
green: noop,
|
|
109
|
+
yellow: noop,
|
|
110
|
+
blue: noop,
|
|
111
|
+
magenta: noop,
|
|
112
|
+
cyan: noop,
|
|
113
|
+
white: noop,
|
|
114
|
+
gray: noop,
|
|
115
|
+
black: noop,
|
|
116
|
+
// Semantic aliases
|
|
117
|
+
success: noop,
|
|
118
|
+
error: noop,
|
|
119
|
+
warning: noop,
|
|
120
|
+
info: noop,
|
|
121
|
+
// Extended colors (return noop factory for chaining)
|
|
122
|
+
rgb: () => noop,
|
|
123
|
+
hex: () => noop,
|
|
124
|
+
bgRgb: () => noop,
|
|
125
|
+
bgHex: () => noop,
|
|
126
|
+
// Observatory brand colors (noop versions)
|
|
127
|
+
brand: {
|
|
128
|
+
// Primary
|
|
129
|
+
amber: noop,
|
|
130
|
+
amberLight: noop,
|
|
131
|
+
// Semantic accents
|
|
132
|
+
success: noop,
|
|
133
|
+
warning: noop,
|
|
134
|
+
danger: noop,
|
|
135
|
+
info: noop,
|
|
136
|
+
// Text hierarchy
|
|
137
|
+
textPrimary: noop,
|
|
138
|
+
textSecondary: noop,
|
|
139
|
+
textTertiary: noop,
|
|
140
|
+
textMuted: noop,
|
|
141
|
+
// Background variants
|
|
142
|
+
bgAmber: noop,
|
|
143
|
+
bgSuccess: noop,
|
|
144
|
+
bgWarning: noop,
|
|
145
|
+
bgDanger: noop,
|
|
146
|
+
bgInfo: noop,
|
|
147
|
+
// Legacy aliases
|
|
148
|
+
green: noop,
|
|
149
|
+
red: noop,
|
|
150
|
+
cyan: noop,
|
|
151
|
+
slate: noop
|
|
152
|
+
}
|
|
153
|
+
};
|
|
55
154
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
155
|
+
return {
|
|
156
|
+
// Modifiers
|
|
157
|
+
reset: ansis.reset,
|
|
158
|
+
bold: ansis.bold,
|
|
159
|
+
dim: ansis.dim,
|
|
160
|
+
italic: ansis.italic,
|
|
161
|
+
underline: ansis.underline,
|
|
162
|
+
strikethrough: ansis.strikethrough,
|
|
163
|
+
// Basic ANSI colors (fallback)
|
|
164
|
+
red: ansis.red,
|
|
165
|
+
green: ansis.green,
|
|
166
|
+
yellow: ansis.yellow,
|
|
167
|
+
blue: ansis.blue,
|
|
168
|
+
magenta: ansis.magenta,
|
|
169
|
+
cyan: ansis.cyan,
|
|
170
|
+
white: ansis.white,
|
|
171
|
+
gray: ansis.gray,
|
|
172
|
+
black: ansis.black,
|
|
173
|
+
// Semantic aliases (basic)
|
|
174
|
+
success: ansis.green,
|
|
175
|
+
error: ansis.red,
|
|
176
|
+
warning: ansis.yellow,
|
|
177
|
+
info: ansis.blue,
|
|
178
|
+
// Extended colors for rich styling
|
|
179
|
+
rgb: ansis.rgb,
|
|
180
|
+
hex: ansis.hex,
|
|
181
|
+
bgRgb: ansis.bgRgb,
|
|
182
|
+
bgHex: ansis.bgHex,
|
|
183
|
+
// Observatory brand colors (Truecolor) - aligned with design system
|
|
184
|
+
brand: {
|
|
185
|
+
// Primary brand color
|
|
186
|
+
amber: ansis.hex(brand.amber),
|
|
187
|
+
amberLight: ansis.hex(brand.amberLight),
|
|
188
|
+
// Semantic accents
|
|
189
|
+
success: ansis.hex(brand.success),
|
|
190
|
+
warning: ansis.hex(brand.warning),
|
|
191
|
+
danger: ansis.hex(brand.danger),
|
|
192
|
+
info: ansis.hex(brand.info),
|
|
193
|
+
// Text hierarchy
|
|
194
|
+
textPrimary: ansis.hex(brand.textPrimary),
|
|
195
|
+
textSecondary: ansis.hex(brand.textSecondary),
|
|
196
|
+
textTertiary: ansis.hex(brand.textTertiary),
|
|
197
|
+
textMuted: ansis.hex(brand.textMuted),
|
|
198
|
+
// Background variants
|
|
199
|
+
bgAmber: ansis.bgHex(brand.amber),
|
|
200
|
+
bgSuccess: ansis.bgHex(brand.success),
|
|
201
|
+
bgWarning: ansis.bgHex(brand.warning),
|
|
202
|
+
bgDanger: ansis.bgHex(brand.danger),
|
|
203
|
+
bgInfo: ansis.bgHex(brand.info),
|
|
204
|
+
// Legacy aliases (backward compatibility)
|
|
205
|
+
green: ansis.hex(brand.green),
|
|
206
|
+
red: ansis.hex(brand.red),
|
|
207
|
+
cyan: ansis.hex(brand.cyan),
|
|
208
|
+
slate: ansis.hex(brand.slate)
|
|
209
|
+
}
|
|
210
|
+
};
|
|
63
211
|
}
|
|
64
212
|
|
|
65
213
|
// Default export with auto-detected color support
|
|
66
|
-
export
|
|
214
|
+
export let colors = createColors();
|
|
@@ -91,10 +91,7 @@ export async function loadConfig(configPath = null, cliOverrides = {}) {
|
|
|
91
91
|
config.apiKey = token;
|
|
92
92
|
config.projectSlug = projectMapping.projectSlug;
|
|
93
93
|
config.organizationSlug = projectMapping.organizationSlug;
|
|
94
|
-
output.debug('
|
|
95
|
-
project: projectMapping.projectSlug,
|
|
96
|
-
org: projectMapping.organizationSlug
|
|
97
|
-
});
|
|
94
|
+
output.debug('config', `linked to ${projectMapping.projectSlug} (${projectMapping.organizationSlug})`);
|
|
98
95
|
}
|
|
99
96
|
}
|
|
100
97
|
|
|
@@ -104,14 +101,14 @@ export async function loadConfig(configPath = null, cliOverrides = {}) {
|
|
|
104
101
|
const envParallelId = getParallelId();
|
|
105
102
|
if (envApiKey) {
|
|
106
103
|
config.apiKey = envApiKey;
|
|
107
|
-
output.debug('
|
|
104
|
+
output.debug('config', 'using token from environment');
|
|
108
105
|
}
|
|
109
106
|
if (envApiUrl !== 'https://app.vizzly.dev') config.apiUrl = envApiUrl;
|
|
110
107
|
if (envParallelId) config.parallelId = envParallelId;
|
|
111
108
|
|
|
112
109
|
// 5. Apply CLI overrides (highest priority)
|
|
113
110
|
if (cliOverrides.token) {
|
|
114
|
-
output.debug('
|
|
111
|
+
output.debug('config', 'using token from --token flag');
|
|
115
112
|
}
|
|
116
113
|
applyCLIOverrides(config, cliOverrides);
|
|
117
114
|
return config;
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dynamic context detection for CLI commands
|
|
3
|
+
*
|
|
4
|
+
* Detects the current state of Vizzly in the working directory:
|
|
5
|
+
* - TDD server status
|
|
6
|
+
* - Project configuration
|
|
7
|
+
* - Authentication status
|
|
8
|
+
* - Baseline counts
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
12
|
+
import { homedir } from 'node:os';
|
|
13
|
+
import { dirname, join } from 'node:path';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Get dynamic context about the current Vizzly state
|
|
17
|
+
* Returns an array of context items with type, label, and value
|
|
18
|
+
*
|
|
19
|
+
* @returns {Array<{type: 'success'|'warning'|'info', label: string, value: string}>}
|
|
20
|
+
*/
|
|
21
|
+
export function getContext() {
|
|
22
|
+
let items = [];
|
|
23
|
+
try {
|
|
24
|
+
let cwd = process.cwd();
|
|
25
|
+
let globalConfigPath = join(process.env.VIZZLY_HOME || join(homedir(), '.vizzly'), 'config.json');
|
|
26
|
+
|
|
27
|
+
// Load global config once
|
|
28
|
+
let globalConfig = {};
|
|
29
|
+
try {
|
|
30
|
+
if (existsSync(globalConfigPath)) {
|
|
31
|
+
globalConfig = JSON.parse(readFileSync(globalConfigPath, 'utf8'));
|
|
32
|
+
}
|
|
33
|
+
} catch {
|
|
34
|
+
// Ignore
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Check for vizzly.config.js (project config)
|
|
38
|
+
let hasProjectConfig = existsSync(join(cwd, 'vizzly.config.js'));
|
|
39
|
+
|
|
40
|
+
// Check for .vizzly directory (TDD baselines)
|
|
41
|
+
let baselineCount = 0;
|
|
42
|
+
try {
|
|
43
|
+
let metaPath = join(cwd, '.vizzly', 'baselines', 'metadata.json');
|
|
44
|
+
if (existsSync(metaPath)) {
|
|
45
|
+
let meta = JSON.parse(readFileSync(metaPath, 'utf8'));
|
|
46
|
+
baselineCount = meta.screenshots?.length || 0;
|
|
47
|
+
}
|
|
48
|
+
} catch {
|
|
49
|
+
// Ignore
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Check for TDD server running
|
|
53
|
+
let serverRunning = false;
|
|
54
|
+
let serverPort = null;
|
|
55
|
+
try {
|
|
56
|
+
let serverFile = join(cwd, '.vizzly', 'server.json');
|
|
57
|
+
if (existsSync(serverFile)) {
|
|
58
|
+
let serverInfo = JSON.parse(readFileSync(serverFile, 'utf8'));
|
|
59
|
+
serverPort = serverInfo.port;
|
|
60
|
+
serverRunning = true;
|
|
61
|
+
}
|
|
62
|
+
} catch {
|
|
63
|
+
// Ignore
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Check for project mapping (from vizzly project:select)
|
|
67
|
+
// Traverse up to find project config, with bounds check for Windows compatibility
|
|
68
|
+
let projectMapping = null;
|
|
69
|
+
let checkPath = cwd;
|
|
70
|
+
let prevPath = null;
|
|
71
|
+
while (checkPath && checkPath !== prevPath) {
|
|
72
|
+
if (globalConfig.projects?.[checkPath]) {
|
|
73
|
+
projectMapping = globalConfig.projects[checkPath];
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
prevPath = checkPath;
|
|
77
|
+
checkPath = dirname(checkPath);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Check for OAuth login (from vizzly login)
|
|
81
|
+
let isLoggedIn = !!globalConfig.auth?.accessToken;
|
|
82
|
+
let userName = globalConfig.auth?.user?.name || globalConfig.auth?.user?.email;
|
|
83
|
+
|
|
84
|
+
// Check for env token
|
|
85
|
+
let hasEnvToken = !!process.env.VIZZLY_TOKEN;
|
|
86
|
+
|
|
87
|
+
// Build context items - prioritize most useful info
|
|
88
|
+
if (serverRunning) {
|
|
89
|
+
items.push({
|
|
90
|
+
type: 'success',
|
|
91
|
+
label: 'TDD Server',
|
|
92
|
+
value: `running on :${serverPort}`
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
if (projectMapping) {
|
|
96
|
+
items.push({
|
|
97
|
+
type: 'success',
|
|
98
|
+
label: 'Project',
|
|
99
|
+
value: `${projectMapping.projectName} (${projectMapping.organizationSlug})`
|
|
100
|
+
});
|
|
101
|
+
} else if (isLoggedIn && userName) {
|
|
102
|
+
items.push({
|
|
103
|
+
type: 'success',
|
|
104
|
+
label: 'Logged in',
|
|
105
|
+
value: userName
|
|
106
|
+
});
|
|
107
|
+
} else if (hasEnvToken) {
|
|
108
|
+
items.push({
|
|
109
|
+
type: 'success',
|
|
110
|
+
label: 'API Token',
|
|
111
|
+
value: 'via VIZZLY_TOKEN'
|
|
112
|
+
});
|
|
113
|
+
} else {
|
|
114
|
+
items.push({
|
|
115
|
+
type: 'info',
|
|
116
|
+
label: 'Not connected',
|
|
117
|
+
value: 'run vizzly login or project:select'
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
if (baselineCount > 0) {
|
|
121
|
+
items.push({
|
|
122
|
+
type: 'success',
|
|
123
|
+
label: 'Baselines',
|
|
124
|
+
value: `${baselineCount} screenshots`
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
if (!hasProjectConfig && !serverRunning && baselineCount === 0) {
|
|
128
|
+
// Only show "no config" hint if there's nothing else useful
|
|
129
|
+
items.push({
|
|
130
|
+
type: 'info',
|
|
131
|
+
label: 'Get started',
|
|
132
|
+
value: 'run vizzly init'
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
} catch {
|
|
136
|
+
// If anything fails, just return empty - context is optional
|
|
137
|
+
}
|
|
138
|
+
return items;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Get detailed context with raw values (for doctor command)
|
|
143
|
+
* Returns more detailed information suitable for diagnostics
|
|
144
|
+
*
|
|
145
|
+
* @returns {Object} Detailed context object
|
|
146
|
+
*/
|
|
147
|
+
export function getDetailedContext() {
|
|
148
|
+
let cwd = process.cwd();
|
|
149
|
+
let globalConfigPath = join(process.env.VIZZLY_HOME || join(homedir(), '.vizzly'), 'config.json');
|
|
150
|
+
let context = {
|
|
151
|
+
tddServer: {
|
|
152
|
+
running: false,
|
|
153
|
+
port: null
|
|
154
|
+
},
|
|
155
|
+
project: {
|
|
156
|
+
hasConfig: false,
|
|
157
|
+
mapping: null
|
|
158
|
+
},
|
|
159
|
+
auth: {
|
|
160
|
+
loggedIn: false,
|
|
161
|
+
userName: null,
|
|
162
|
+
hasEnvToken: false
|
|
163
|
+
},
|
|
164
|
+
baselines: {
|
|
165
|
+
count: 0,
|
|
166
|
+
path: null
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
try {
|
|
170
|
+
// Load global config
|
|
171
|
+
let globalConfig = {};
|
|
172
|
+
try {
|
|
173
|
+
if (existsSync(globalConfigPath)) {
|
|
174
|
+
globalConfig = JSON.parse(readFileSync(globalConfigPath, 'utf8'));
|
|
175
|
+
}
|
|
176
|
+
} catch {
|
|
177
|
+
// Ignore
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Check for vizzly.config.js
|
|
181
|
+
context.project.hasConfig = existsSync(join(cwd, 'vizzly.config.js'));
|
|
182
|
+
|
|
183
|
+
// Check for baselines
|
|
184
|
+
try {
|
|
185
|
+
let metaPath = join(cwd, '.vizzly', 'baselines', 'metadata.json');
|
|
186
|
+
if (existsSync(metaPath)) {
|
|
187
|
+
let meta = JSON.parse(readFileSync(metaPath, 'utf8'));
|
|
188
|
+
context.baselines.count = meta.screenshots?.length || 0;
|
|
189
|
+
context.baselines.path = join(cwd, '.vizzly', 'baselines');
|
|
190
|
+
}
|
|
191
|
+
} catch {
|
|
192
|
+
// Ignore
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Check for TDD server
|
|
196
|
+
try {
|
|
197
|
+
let serverFile = join(cwd, '.vizzly', 'server.json');
|
|
198
|
+
if (existsSync(serverFile)) {
|
|
199
|
+
let serverInfo = JSON.parse(readFileSync(serverFile, 'utf8'));
|
|
200
|
+
context.tddServer.running = true;
|
|
201
|
+
context.tddServer.port = serverInfo.port;
|
|
202
|
+
}
|
|
203
|
+
} catch {
|
|
204
|
+
// Ignore
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Check for project mapping
|
|
208
|
+
// Traverse up to find project config, with bounds check for Windows compatibility
|
|
209
|
+
let checkPath = cwd;
|
|
210
|
+
let prevPath = null;
|
|
211
|
+
while (checkPath && checkPath !== prevPath) {
|
|
212
|
+
if (globalConfig.projects?.[checkPath]) {
|
|
213
|
+
context.project.mapping = globalConfig.projects[checkPath];
|
|
214
|
+
break;
|
|
215
|
+
}
|
|
216
|
+
prevPath = checkPath;
|
|
217
|
+
checkPath = dirname(checkPath);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Check auth status
|
|
221
|
+
context.auth.loggedIn = !!globalConfig.auth?.accessToken;
|
|
222
|
+
context.auth.userName = globalConfig.auth?.user?.name || globalConfig.auth?.user?.email;
|
|
223
|
+
context.auth.hasEnvToken = !!process.env.VIZZLY_TOKEN;
|
|
224
|
+
} catch {
|
|
225
|
+
// If anything fails, return defaults
|
|
226
|
+
}
|
|
227
|
+
return context;
|
|
228
|
+
}
|