@reshotdev/screenshot 0.0.1-beta.1 → 0.0.1-beta.10
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 +65 -7
- package/package.json +9 -2
- package/src/commands/auth.js +108 -26
- package/src/commands/certify.js +62 -0
- package/src/commands/ci-run.js +57 -2
- package/src/commands/ci-setup.js +5 -5
- package/src/commands/doctor-release.js +67 -0
- package/src/commands/doctor-target.js +49 -0
- package/src/commands/drifts.js +5 -70
- package/src/commands/import-tests.js +13 -13
- package/src/commands/ingest.js +10 -10
- package/src/commands/init.js +16 -277
- package/src/commands/publish.js +204 -237
- package/src/commands/pull.js +253 -23
- package/src/commands/run.js +292 -12
- package/src/commands/setup-wizard.js +277 -499
- package/src/commands/setup.js +41 -13
- package/src/commands/status.js +313 -125
- package/src/commands/sync.js +28 -236
- package/src/commands/ui.js +1 -1
- package/src/commands/verify-publish.js +46 -0
- package/src/index.js +194 -94
- package/src/lib/api-client.js +121 -35
- package/src/lib/capture-engine.js +103 -7
- package/src/lib/capture-script-runner.js +305 -58
- package/src/lib/certification.js +865 -0
- package/src/lib/config.js +181 -76
- package/src/lib/record-cdp.js +288 -16
- package/src/lib/record-config.js +1 -1
- package/src/lib/release-doctor.js +313 -0
- package/src/lib/run-manifest.js +103 -0
- package/src/lib/standalone-mode.js +1 -1
- package/src/lib/storage-providers.js +4 -4
- package/src/lib/target-contract.js +292 -0
- package/src/lib/ui-api.js +6 -7
- package/web/manager/dist/assets/{index--ZgioErz.js → index-D2qqcFNN.js} +1 -1
- package/web/manager/dist/index.html +1 -1
- package/src/commands/validate-docs.js +0 -529
package/src/commands/setup.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
const chalk = require("chalk");
|
|
3
3
|
const oraModule = require("ora");
|
|
4
4
|
const ora = oraModule.default || oraModule;
|
|
5
|
+
const { execSync } = require("child_process");
|
|
5
6
|
|
|
6
7
|
const {
|
|
7
8
|
writeSettings,
|
|
@@ -12,13 +13,13 @@ const {
|
|
|
12
13
|
} = require("../lib/config");
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
|
-
* Create a default
|
|
16
|
+
* Create a default reshot.config.json for platform mode
|
|
16
17
|
* @param {Object} settings - Project settings from auth
|
|
17
18
|
* @returns {Object} The created config
|
|
18
19
|
*/
|
|
19
20
|
function createDefaultConfig(settings) {
|
|
20
21
|
const defaultConfig = {
|
|
21
|
-
$schema: "https://reshot.dev/schemas/
|
|
22
|
+
$schema: "https://reshot.dev/schemas/reshot-config.json",
|
|
22
23
|
version: "2.0",
|
|
23
24
|
projectId: settings.projectId,
|
|
24
25
|
baseUrl: "http://localhost:3000",
|
|
@@ -94,22 +95,49 @@ async function setupCommand(options = {}) {
|
|
|
94
95
|
console.log(); // Add spacing after auth
|
|
95
96
|
}
|
|
96
97
|
|
|
97
|
-
// Step 3: Handle initialization (create
|
|
98
|
+
// Step 3: Handle initialization (create reshot.config.json if missing)
|
|
98
99
|
const hasConfig = configExists();
|
|
99
100
|
|
|
100
101
|
if (hasConfig && !force) {
|
|
101
|
-
console.log(chalk.green("✔ Configuration found:"), chalk.cyan("
|
|
102
|
+
console.log(chalk.green("✔ Configuration found:"), chalk.cyan("reshot.config.json"));
|
|
102
103
|
} else if (hasConfig && force) {
|
|
103
|
-
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
|
|
104
|
+
// Patch projectId in existing config instead of wiping scenarios
|
|
105
|
+
const fs = require("fs");
|
|
106
|
+
const path = require("path");
|
|
107
|
+
const configPath = path.join(process.cwd(), "reshot.config.json");
|
|
108
|
+
const existingConfig = JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
109
|
+
existingConfig.projectId = existingSettings.projectId;
|
|
110
|
+
fs.writeFileSync(configPath, JSON.stringify(existingConfig, null, 2) + "\n");
|
|
111
|
+
console.log(chalk.green("✔ Configuration updated (projectId synced)"));
|
|
107
112
|
} else {
|
|
108
113
|
// No config - create it
|
|
109
|
-
console.log(chalk.gray("Creating
|
|
114
|
+
console.log(chalk.gray("Creating reshot.config.json..."));
|
|
110
115
|
const newConfig = createDefaultConfig(existingSettings);
|
|
111
116
|
writeConfig(newConfig);
|
|
112
|
-
console.log(chalk.green("✔ Configuration created:"), chalk.cyan("
|
|
117
|
+
console.log(chalk.green("✔ Configuration created:"), chalk.cyan("reshot.config.json"));
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Step 3.5: Ensure @reshotdev/screenshot is in devDependencies
|
|
121
|
+
const fs = require("fs");
|
|
122
|
+
const path = require("path");
|
|
123
|
+
const pkgJsonPath = path.join(process.cwd(), "package.json");
|
|
124
|
+
if (fs.existsSync(pkgJsonPath)) {
|
|
125
|
+
const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, "utf-8"));
|
|
126
|
+
const hasDep =
|
|
127
|
+
pkgJson.devDependencies?.["@reshotdev/screenshot"] ||
|
|
128
|
+
pkgJson.dependencies?.["@reshotdev/screenshot"];
|
|
129
|
+
if (!hasDep) {
|
|
130
|
+
console.log(chalk.gray(" Adding @reshotdev/screenshot to devDependencies..."));
|
|
131
|
+
const usePnpm = fs.existsSync(path.join(process.cwd(), "pnpm-lock.yaml"));
|
|
132
|
+
const useYarn = fs.existsSync(path.join(process.cwd(), "yarn.lock"));
|
|
133
|
+
const cmd = usePnpm ? "pnpm add -D" : useYarn ? "yarn add -D" : "npm install -D";
|
|
134
|
+
try {
|
|
135
|
+
execSync(`${cmd} @reshotdev/screenshot`, { stdio: "inherit" });
|
|
136
|
+
console.log(chalk.green(" ✔ Added @reshotdev/screenshot to devDependencies"));
|
|
137
|
+
} catch {
|
|
138
|
+
console.log(chalk.yellow(" ⚠ Could not auto-install. Run manually: " + cmd + " @reshotdev/screenshot"));
|
|
139
|
+
}
|
|
140
|
+
}
|
|
113
141
|
}
|
|
114
142
|
|
|
115
143
|
// Step 4: Success summary
|
|
@@ -128,9 +156,9 @@ async function setupCommand(options = {}) {
|
|
|
128
156
|
await uiCommand({ open: true });
|
|
129
157
|
} else {
|
|
130
158
|
console.log(chalk.gray("Next steps:"));
|
|
131
|
-
console.log(chalk.gray(" • Run"), chalk.cyan("reshot
|
|
132
|
-
console.log(chalk.gray(" • Run"), chalk.cyan("reshot
|
|
133
|
-
console.log(chalk.gray(" • Run"), chalk.cyan("reshot
|
|
159
|
+
console.log(chalk.gray(" • Run"), chalk.cyan("reshot run"), chalk.gray("to generate your first local capture"));
|
|
160
|
+
console.log(chalk.gray(" • Run"), chalk.cyan("reshot publish"), chalk.gray("when you are ready for hosted assets and review workflows"));
|
|
161
|
+
console.log(chalk.gray(" • Run"), chalk.cyan("reshot studio"), chalk.gray("to inspect output locally\n"));
|
|
134
162
|
}
|
|
135
163
|
}
|
|
136
164
|
|
package/src/commands/status.js
CHANGED
|
@@ -1,20 +1,46 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* status - Check
|
|
3
|
+
* status - Check project status, sync jobs, and drift queue
|
|
4
4
|
*
|
|
5
5
|
* Displays:
|
|
6
6
|
* - Current project configuration
|
|
7
7
|
* - Recent sync jobs with status
|
|
8
8
|
* - Drift queue summary
|
|
9
|
-
* - Pending actions
|
|
10
9
|
*
|
|
11
|
-
* Usage: reshot status [--jobs] [--drifts] [--json]
|
|
10
|
+
* Usage: reshot status [--jobs] [--drifts] [--config] [--json]
|
|
12
11
|
*/
|
|
13
12
|
|
|
14
13
|
const chalk = require("chalk");
|
|
15
14
|
const config = require("../lib/config");
|
|
16
15
|
const apiClient = require("../lib/api-client");
|
|
17
16
|
|
|
17
|
+
function createIssue(scope, level, message, details = {}) {
|
|
18
|
+
return {
|
|
19
|
+
scope,
|
|
20
|
+
level,
|
|
21
|
+
message,
|
|
22
|
+
...details,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function summarizeConfig(reshotConfig) {
|
|
27
|
+
return {
|
|
28
|
+
baseUrl: reshotConfig.baseUrl || null,
|
|
29
|
+
viewport: reshotConfig.viewport || null,
|
|
30
|
+
assetDir: reshotConfig.assetDir || ".reshot/output",
|
|
31
|
+
scenarioCount: reshotConfig.scenarios?.length || 0,
|
|
32
|
+
traceDir: reshotConfig.visuals?.traceDir || null,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function readSettingsSafe() {
|
|
37
|
+
try {
|
|
38
|
+
return config.readSettings();
|
|
39
|
+
} catch (error) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
18
44
|
/**
|
|
19
45
|
* Format timestamp for display
|
|
20
46
|
*/
|
|
@@ -86,33 +112,255 @@ function formatDriftType(type) {
|
|
|
86
112
|
}
|
|
87
113
|
|
|
88
114
|
/**
|
|
89
|
-
*
|
|
115
|
+
* Display project configuration summary
|
|
90
116
|
*/
|
|
91
|
-
async function displayConfig(
|
|
117
|
+
async function displayConfig(configSummary) {
|
|
92
118
|
console.log(chalk.bold("\n📋 Project Configuration\n"));
|
|
93
119
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
120
|
+
console.log(chalk.gray(" Base URL: ") + chalk.white(configSummary.baseUrl || "(not set)"));
|
|
121
|
+
console.log(chalk.gray(" Viewport: ") + chalk.white(
|
|
122
|
+
configSummary.viewport
|
|
123
|
+
? `${configSummary.viewport.width}×${configSummary.viewport.height}`
|
|
124
|
+
: "(default)"
|
|
125
|
+
));
|
|
126
|
+
console.log(chalk.gray(" Asset Dir: ") + chalk.white(configSummary.assetDir || ".reshot/output"));
|
|
127
|
+
|
|
128
|
+
console.log(chalk.gray(" Scenarios: ") + chalk.white(`${configSummary.scenarioCount || 0} defined`));
|
|
129
|
+
|
|
130
|
+
if (configSummary.traceDir) {
|
|
131
|
+
console.log(chalk.gray(" Trace Dir: ") + chalk.white(configSummary.traceDir));
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function renderJobs(jobs) {
|
|
136
|
+
if (jobs.length === 0) {
|
|
137
|
+
console.log(chalk.gray(" No sync jobs found."));
|
|
138
|
+
console.log(chalk.gray(" Run `reshot sync` to upload traces."));
|
|
98
139
|
return;
|
|
99
140
|
}
|
|
100
141
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
142
|
+
for (const job of jobs) {
|
|
143
|
+
console.log(
|
|
144
|
+
` ${formatJobStatus(job.status)} ` +
|
|
145
|
+
chalk.gray(`${job.id.slice(0, 8)} `) +
|
|
146
|
+
chalk.white(`${job.branch || "unknown"}@${(job.commitHash || "").slice(0, 7)} `) +
|
|
147
|
+
chalk.gray(formatTime(job.createdAt)),
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
if (job.driftCount > 0) {
|
|
151
|
+
console.log(chalk.gray(` └─ ${job.driftCount} drift(s) detected`));
|
|
152
|
+
}
|
|
153
|
+
if (job.errorMessage) {
|
|
154
|
+
console.log(chalk.red(` └─ Error: ${job.errorMessage}`));
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function renderDrifts(drifts, stats, projectId, limit = 10) {
|
|
160
|
+
if (stats.total > 0) {
|
|
161
|
+
console.log(
|
|
162
|
+
chalk.gray(" Summary: ") +
|
|
163
|
+
chalk.yellow(`${stats.pending || 0} pending`) + ", " +
|
|
164
|
+
chalk.green(`${stats.approved || 0} approved`) + ", " +
|
|
165
|
+
chalk.red(`${stats.rejected || 0} rejected`) + ", " +
|
|
166
|
+
chalk.gray(`${stats.ignored || 0} ignored`),
|
|
167
|
+
);
|
|
168
|
+
console.log();
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (drifts.length === 0) {
|
|
172
|
+
console.log(chalk.green(" ✓ No pending drifts!"));
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const displayed = drifts.slice(0, limit);
|
|
177
|
+
for (const drift of displayed) {
|
|
178
|
+
console.log(
|
|
179
|
+
` ${formatDriftType(drift.driftType)} ` +
|
|
180
|
+
chalk.white(drift.journeyKey) + " " +
|
|
181
|
+
chalk.gray(`(${Math.round(drift.confidenceScore * 100)}% confidence)`),
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (drifts.length > limit) {
|
|
186
|
+
console.log(chalk.gray(`\n ... and ${drifts.length - limit} more.`));
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
console.log();
|
|
190
|
+
console.log(
|
|
191
|
+
chalk.gray(" View in dashboard: ") +
|
|
192
|
+
chalk.blue(`https://reshot.dev/app/projects/${projectId}/visuals`),
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
async function fetchJobsData(apiKey, projectId, limit = 5) {
|
|
197
|
+
try {
|
|
198
|
+
const response = await apiClient.getSyncJobs(apiKey, projectId, { limit });
|
|
199
|
+
return {
|
|
200
|
+
jobs: response.jobs || response.data?.jobs || [],
|
|
201
|
+
error: null,
|
|
202
|
+
};
|
|
203
|
+
} catch (error) {
|
|
204
|
+
return {
|
|
205
|
+
jobs: [],
|
|
206
|
+
error: {
|
|
207
|
+
message: error.message,
|
|
208
|
+
kind: error.reshot?.kind || null,
|
|
209
|
+
status: error.reshot?.status || error.response?.status || null,
|
|
210
|
+
},
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
async function fetchDriftsData(apiKey, projectId) {
|
|
216
|
+
try {
|
|
217
|
+
const response = await apiClient.getDrifts(apiKey, projectId, {
|
|
218
|
+
status: "PENDING",
|
|
219
|
+
});
|
|
220
|
+
return {
|
|
221
|
+
drifts: response.drifts || [],
|
|
222
|
+
stats: response.stats || {},
|
|
223
|
+
error: null,
|
|
224
|
+
};
|
|
225
|
+
} catch (error) {
|
|
226
|
+
return {
|
|
227
|
+
drifts: [],
|
|
228
|
+
stats: {},
|
|
229
|
+
error: {
|
|
230
|
+
message: error.message,
|
|
231
|
+
kind: error.reshot?.kind || null,
|
|
232
|
+
status: error.reshot?.status || error.response?.status || null,
|
|
233
|
+
},
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
async function buildStatusReport(options = {}) {
|
|
239
|
+
const report = {
|
|
240
|
+
generatedAt: new Date().toISOString(),
|
|
241
|
+
ok: true,
|
|
242
|
+
projectId: null,
|
|
243
|
+
mode: config.getModeInfo(),
|
|
244
|
+
config: {
|
|
245
|
+
exists: false,
|
|
246
|
+
summary: null,
|
|
247
|
+
validation: {
|
|
248
|
+
valid: false,
|
|
249
|
+
errors: [],
|
|
250
|
+
warnings: [],
|
|
251
|
+
},
|
|
252
|
+
},
|
|
253
|
+
auth: {
|
|
254
|
+
hasApiKey: false,
|
|
255
|
+
hasProjectId: false,
|
|
256
|
+
},
|
|
257
|
+
jobs: {
|
|
258
|
+
items: [],
|
|
259
|
+
error: null,
|
|
260
|
+
},
|
|
261
|
+
drifts: {
|
|
262
|
+
items: [],
|
|
263
|
+
stats: {},
|
|
264
|
+
error: null,
|
|
265
|
+
},
|
|
266
|
+
issues: [],
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
let reshotConfig = null;
|
|
270
|
+
try {
|
|
271
|
+
reshotConfig = config.readConfigLenient();
|
|
272
|
+
report.config.exists = true;
|
|
273
|
+
report.config.summary = summarizeConfig(reshotConfig);
|
|
274
|
+
} catch (error) {
|
|
275
|
+
report.issues.push(
|
|
276
|
+
createIssue(
|
|
277
|
+
"config",
|
|
278
|
+
"error",
|
|
279
|
+
"reshot.config.json not found. Run `reshot init` or `reshot setup` first.",
|
|
280
|
+
{ detail: error.message },
|
|
281
|
+
),
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
if (report.config.exists) {
|
|
286
|
+
report.config.validation = config.validateConfig();
|
|
287
|
+
for (const errorMessage of report.config.validation.errors) {
|
|
288
|
+
report.issues.push(createIssue("config", "error", errorMessage));
|
|
289
|
+
}
|
|
290
|
+
for (const warningMessage of report.config.validation.warnings) {
|
|
291
|
+
report.issues.push(createIssue("config", "warning", warningMessage));
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const settings = readSettingsSafe();
|
|
296
|
+
const apiKey = process.env.RESHOT_API_KEY || settings?.apiKey || null;
|
|
297
|
+
const projectId =
|
|
298
|
+
process.env.RESHOT_PROJECT_ID ||
|
|
299
|
+
settings?.projectId ||
|
|
300
|
+
reshotConfig?._metadata?.projectId ||
|
|
301
|
+
null;
|
|
302
|
+
|
|
303
|
+
report.projectId = projectId;
|
|
304
|
+
report.auth.hasApiKey = Boolean(apiKey);
|
|
305
|
+
report.auth.hasProjectId = Boolean(projectId);
|
|
306
|
+
|
|
307
|
+
if (!apiKey) {
|
|
308
|
+
report.issues.push(
|
|
309
|
+
createIssue(
|
|
310
|
+
"auth",
|
|
311
|
+
"error",
|
|
312
|
+
"API key not found. Set RESHOT_API_KEY or run `reshot auth`.",
|
|
313
|
+
),
|
|
314
|
+
);
|
|
107
315
|
}
|
|
108
|
-
|
|
109
|
-
|
|
316
|
+
|
|
317
|
+
if (!projectId) {
|
|
318
|
+
report.issues.push(
|
|
319
|
+
createIssue(
|
|
320
|
+
"auth",
|
|
321
|
+
"error",
|
|
322
|
+
"Project ID not found. Set RESHOT_PROJECT_ID or run `reshot setup`.",
|
|
323
|
+
),
|
|
324
|
+
);
|
|
110
325
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
326
|
+
|
|
327
|
+
if (apiKey && projectId) {
|
|
328
|
+
const [jobsResult, driftsResult] = await Promise.all([
|
|
329
|
+
fetchJobsData(apiKey, projectId, options.limit || 5),
|
|
330
|
+
fetchDriftsData(apiKey, projectId),
|
|
331
|
+
]);
|
|
332
|
+
|
|
333
|
+
report.jobs.items = jobsResult.jobs;
|
|
334
|
+
report.jobs.error = jobsResult.error;
|
|
335
|
+
report.drifts.items = driftsResult.drifts;
|
|
336
|
+
report.drifts.stats = driftsResult.stats;
|
|
337
|
+
report.drifts.error = driftsResult.error;
|
|
338
|
+
|
|
339
|
+
if (jobsResult.error) {
|
|
340
|
+
report.issues.push(
|
|
341
|
+
createIssue(
|
|
342
|
+
"jobs",
|
|
343
|
+
"warning",
|
|
344
|
+
`Could not fetch sync jobs: ${jobsResult.error.message}`,
|
|
345
|
+
jobsResult.error,
|
|
346
|
+
),
|
|
347
|
+
);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
if (driftsResult.error) {
|
|
351
|
+
report.issues.push(
|
|
352
|
+
createIssue(
|
|
353
|
+
"drifts",
|
|
354
|
+
"warning",
|
|
355
|
+
`Could not fetch drifts: ${driftsResult.error.message}`,
|
|
356
|
+
driftsResult.error,
|
|
357
|
+
),
|
|
358
|
+
);
|
|
359
|
+
}
|
|
115
360
|
}
|
|
361
|
+
|
|
362
|
+
report.ok = !report.issues.some((issue) => issue.level === "error");
|
|
363
|
+
return report;
|
|
116
364
|
}
|
|
117
365
|
|
|
118
366
|
/**
|
|
@@ -122,32 +370,9 @@ async function displayJobs(apiKey, projectId, limit = 5) {
|
|
|
122
370
|
console.log(chalk.bold("\n📦 Recent Sync Jobs\n"));
|
|
123
371
|
|
|
124
372
|
try {
|
|
125
|
-
// Fetch jobs via API
|
|
126
373
|
const response = await apiClient.getSyncJobs(apiKey, projectId, { limit });
|
|
127
|
-
|
|
128
374
|
const jobs = response.jobs || response.data?.jobs || [];
|
|
129
|
-
|
|
130
|
-
if (jobs.length === 0) {
|
|
131
|
-
console.log(chalk.gray(" No sync jobs found."));
|
|
132
|
-
console.log(chalk.gray(" Run `reshot ingest` to upload traces and docs."));
|
|
133
|
-
return;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
for (const job of jobs) {
|
|
137
|
-
console.log(
|
|
138
|
-
` ${formatJobStatus(job.status)} ` +
|
|
139
|
-
chalk.gray(`${job.id.slice(0, 8)} `) +
|
|
140
|
-
chalk.white(`${job.branch || "unknown"}@${(job.commitHash || "").slice(0, 7)} `) +
|
|
141
|
-
chalk.gray(formatTime(job.createdAt))
|
|
142
|
-
);
|
|
143
|
-
|
|
144
|
-
if (job.driftCount > 0) {
|
|
145
|
-
console.log(chalk.gray(` └─ ${job.driftCount} drift(s) detected`));
|
|
146
|
-
}
|
|
147
|
-
if (job.errorMessage) {
|
|
148
|
-
console.log(chalk.red(` └─ Error: ${job.errorMessage}`));
|
|
149
|
-
}
|
|
150
|
-
}
|
|
375
|
+
renderJobs(jobs);
|
|
151
376
|
} catch (error) {
|
|
152
377
|
console.log(chalk.yellow(" Could not fetch sync jobs: " + error.message));
|
|
153
378
|
console.log(chalk.gray(" This endpoint may not be available yet."));
|
|
@@ -164,45 +389,7 @@ async function displayDrifts(apiKey, projectId, limit = 10) {
|
|
|
164
389
|
const response = await apiClient.getDrifts(apiKey, projectId, { status: "PENDING" });
|
|
165
390
|
const drifts = response.drifts || [];
|
|
166
391
|
const stats = response.stats || {};
|
|
167
|
-
|
|
168
|
-
// Show stats summary
|
|
169
|
-
if (stats.total > 0) {
|
|
170
|
-
console.log(
|
|
171
|
-
chalk.gray(" Summary: ") +
|
|
172
|
-
chalk.yellow(`${stats.pending || 0} pending`) + ", " +
|
|
173
|
-
chalk.green(`${stats.approved || 0} approved`) + ", " +
|
|
174
|
-
chalk.red(`${stats.rejected || 0} rejected`) + ", " +
|
|
175
|
-
chalk.gray(`${stats.ignored || 0} ignored`)
|
|
176
|
-
);
|
|
177
|
-
console.log();
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
if (drifts.length === 0) {
|
|
181
|
-
console.log(chalk.green(" ✓ No pending drifts!"));
|
|
182
|
-
console.log(chalk.gray(" Your documentation is in sync with the application."));
|
|
183
|
-
return;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// Show pending drifts
|
|
187
|
-
const displayed = drifts.slice(0, limit);
|
|
188
|
-
for (const drift of displayed) {
|
|
189
|
-
console.log(
|
|
190
|
-
` ${formatDriftType(drift.driftType)} ` +
|
|
191
|
-
chalk.white(drift.journeyKey || drift.docPath) + " " +
|
|
192
|
-
chalk.gray(`(${Math.round(drift.confidenceScore * 100)}% confidence)`)
|
|
193
|
-
);
|
|
194
|
-
|
|
195
|
-
if (drift.docPath) {
|
|
196
|
-
console.log(chalk.gray(` └─ ${drift.docPath}`));
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
if (drifts.length > limit) {
|
|
201
|
-
console.log(chalk.gray(`\n ... and ${drifts.length - limit} more.`));
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
console.log();
|
|
205
|
-
console.log(chalk.gray(" View in dashboard: ") + chalk.blue(`https://reshot.dev/app/projects/${projectId}/docsync`));
|
|
392
|
+
renderDrifts(drifts, stats, projectId, limit);
|
|
206
393
|
} catch (error) {
|
|
207
394
|
console.log(chalk.yellow(" Could not fetch drifts: " + error.message));
|
|
208
395
|
}
|
|
@@ -212,64 +399,65 @@ async function displayDrifts(apiKey, projectId, limit = 10) {
|
|
|
212
399
|
* Main status command
|
|
213
400
|
*/
|
|
214
401
|
async function statusCommand(options = {}) {
|
|
215
|
-
|
|
402
|
+
const report = await buildStatusReport(options);
|
|
216
403
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
process.exit(1);
|
|
404
|
+
if (options.json) {
|
|
405
|
+
console.log(JSON.stringify(report, null, 2));
|
|
406
|
+
if (!report.ok) {
|
|
407
|
+
process.exitCode = 1;
|
|
408
|
+
}
|
|
409
|
+
return report;
|
|
224
410
|
}
|
|
225
411
|
|
|
226
|
-
|
|
227
|
-
const settings = config.readSettings();
|
|
228
|
-
const apiKey = process.env.RESHOT_API_KEY || settings?.apiKey;
|
|
229
|
-
const projectId =
|
|
230
|
-
process.env.RESHOT_PROJECT_ID ||
|
|
231
|
-
settings?.projectId ||
|
|
232
|
-
docSyncConfig._metadata?.projectId;
|
|
412
|
+
console.log(chalk.blue("\n📊 Reshot Status\n"));
|
|
233
413
|
|
|
234
|
-
if (
|
|
235
|
-
console.
|
|
236
|
-
process.exit(1);
|
|
414
|
+
if (report.projectId) {
|
|
415
|
+
console.log(chalk.gray(` Project: ${report.projectId}`));
|
|
237
416
|
}
|
|
238
417
|
|
|
239
|
-
if (!
|
|
240
|
-
|
|
241
|
-
process.exit(1);
|
|
418
|
+
if (report.config.summary && (options.config || (!options.jobs && !options.drifts))) {
|
|
419
|
+
await displayConfig(report.config.summary);
|
|
242
420
|
}
|
|
243
421
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
422
|
+
if (!options.config && (options.jobs || (!options.config && !options.drifts))) {
|
|
423
|
+
console.log(chalk.bold("\n📦 Recent Sync Jobs\n"));
|
|
424
|
+
if (report.jobs.error) {
|
|
425
|
+
console.log(chalk.yellow(" Could not fetch sync jobs: " + report.jobs.error.message));
|
|
426
|
+
console.log(chalk.gray(" This endpoint may not be available yet."));
|
|
427
|
+
} else {
|
|
428
|
+
renderJobs(report.jobs.items);
|
|
429
|
+
}
|
|
249
430
|
}
|
|
250
431
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
432
|
+
if (!options.jobs && (options.drifts || (!options.config && !options.jobs))) {
|
|
433
|
+
console.log(chalk.bold("\n🔄 Drift Queue\n"));
|
|
434
|
+
if (report.drifts.error) {
|
|
435
|
+
console.log(chalk.yellow(" Could not fetch drifts: " + report.drifts.error.message));
|
|
436
|
+
} else {
|
|
437
|
+
renderDrifts(
|
|
438
|
+
report.drifts.items,
|
|
439
|
+
report.drifts.stats,
|
|
440
|
+
report.projectId,
|
|
441
|
+
options.limit || 10,
|
|
442
|
+
);
|
|
443
|
+
}
|
|
254
444
|
}
|
|
255
445
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
446
|
+
if (report.issues.length > 0) {
|
|
447
|
+
console.log(chalk.bold("\n⚠ Issues\n"));
|
|
448
|
+
for (const issue of report.issues) {
|
|
449
|
+
const prefix = issue.level === "error" ? chalk.red(" ✖") : chalk.yellow(" ⚠");
|
|
450
|
+
console.log(`${prefix} [${issue.scope}] ${issue.message}`);
|
|
451
|
+
}
|
|
259
452
|
}
|
|
260
453
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
const response = await apiClient.getDrifts(apiKey, projectId).catch(() => ({}));
|
|
264
|
-
console.log(JSON.stringify({
|
|
265
|
-
projectId,
|
|
266
|
-
config: docSyncConfig.documentation,
|
|
267
|
-
drifts: response.drifts || [],
|
|
268
|
-
stats: response.stats || {},
|
|
269
|
-
}, null, 2));
|
|
454
|
+
if (!report.ok) {
|
|
455
|
+
process.exitCode = 1;
|
|
270
456
|
}
|
|
271
457
|
|
|
272
458
|
console.log();
|
|
459
|
+
return report;
|
|
273
460
|
}
|
|
274
461
|
|
|
275
462
|
module.exports = statusCommand;
|
|
463
|
+
module.exports.buildStatusReport = buildStatusReport;
|