@tryghost/velo-cli 0.1.2 → 0.1.4
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/index.js +55 -17
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -104,12 +104,26 @@ async function apiRequest(endpoint, options = {}) {
|
|
|
104
104
|
}
|
|
105
105
|
async function verifyToken(token) {
|
|
106
106
|
const url = `${API_BASE_URL}/cli/verify`;
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
107
|
+
try {
|
|
108
|
+
const response = await fetch(url, {
|
|
109
|
+
headers: {
|
|
110
|
+
"Authorization": `Bearer ${token}`
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
const contentType = response.headers.get("content-type") || "";
|
|
114
|
+
if (contentType.includes("text/html")) {
|
|
115
|
+
return {
|
|
116
|
+
valid: false,
|
|
117
|
+
error: "Authentication required (got HTML instead of JSON - endpoint may be behind Cloudflare Access)"
|
|
118
|
+
};
|
|
110
119
|
}
|
|
111
|
-
|
|
112
|
-
|
|
120
|
+
return await response.json();
|
|
121
|
+
} catch (error) {
|
|
122
|
+
return {
|
|
123
|
+
valid: false,
|
|
124
|
+
error: error.message
|
|
125
|
+
};
|
|
126
|
+
}
|
|
113
127
|
}
|
|
114
128
|
|
|
115
129
|
// src/commands/login.ts
|
|
@@ -278,11 +292,6 @@ async function browserLogin() {
|
|
|
278
292
|
try {
|
|
279
293
|
const credentials = await credentialsPromise;
|
|
280
294
|
spinner.stop();
|
|
281
|
-
const verification = await verifyToken(credentials.token);
|
|
282
|
-
if (!verification.valid) {
|
|
283
|
-
console.log(chalk.red("Token verification failed. Please try again."));
|
|
284
|
-
return;
|
|
285
|
-
}
|
|
286
295
|
saveCredentials(credentials);
|
|
287
296
|
console.log(chalk.green(`
|
|
288
297
|
\u2713 Logged in as ${chalk.bold(credentials.email)}`));
|
|
@@ -343,17 +352,46 @@ async function status(options) {
|
|
|
343
352
|
endpoint += `&repo=${encodeURIComponent(options.repo)}`;
|
|
344
353
|
}
|
|
345
354
|
const metrics = await apiRequest(endpoint);
|
|
355
|
+
const repoStats = {};
|
|
356
|
+
let totalRuns = 0;
|
|
357
|
+
let totalSuccesses = 0;
|
|
358
|
+
let totalDurationMs = 0;
|
|
359
|
+
let durationCount = 0;
|
|
360
|
+
for (const item of metrics.summary) {
|
|
361
|
+
const count = parseInt(item.count, 10);
|
|
362
|
+
totalRuns += count;
|
|
363
|
+
if (!repoStats[item.repo]) {
|
|
364
|
+
repoStats[item.repo] = { total: 0, successes: 0, failures: 0, totalDurationMs: 0, successCount: 0 };
|
|
365
|
+
}
|
|
366
|
+
repoStats[item.repo].total += count;
|
|
367
|
+
if (item.status === "success") {
|
|
368
|
+
totalSuccesses += count;
|
|
369
|
+
repoStats[item.repo].successes += count;
|
|
370
|
+
repoStats[item.repo].totalDurationMs += item.avg_duration_ms * count;
|
|
371
|
+
repoStats[item.repo].successCount += count;
|
|
372
|
+
totalDurationMs += item.avg_duration_ms * count;
|
|
373
|
+
durationCount += count;
|
|
374
|
+
} else if (item.status === "failure") {
|
|
375
|
+
repoStats[item.repo].failures += count;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
const successRate = totalRuns > 0 ? totalSuccesses / totalRuns * 100 : 0;
|
|
379
|
+
const avgDurationMin = durationCount > 0 ? totalDurationMs / durationCount / 6e4 : 0;
|
|
346
380
|
console.log(chalk3.white.bold("Last " + days + " days:"));
|
|
347
|
-
console.log(` Total runs: ${chalk3.cyan(
|
|
348
|
-
console.log(` Success rate: ${formatRate(
|
|
349
|
-
console.log(` Avg duration: ${chalk3.gray(
|
|
381
|
+
console.log(` Total runs: ${chalk3.cyan(totalRuns.toLocaleString())}`);
|
|
382
|
+
console.log(` Success rate: ${formatRate(successRate)}`);
|
|
383
|
+
console.log(` Avg duration: ${chalk3.gray(avgDurationMin.toFixed(1) + " min")}`);
|
|
350
384
|
console.log();
|
|
351
|
-
const repos = Object.entries(
|
|
385
|
+
const repos = Object.entries(repoStats).map(([repo, data]) => ({
|
|
386
|
+
repo,
|
|
387
|
+
...data,
|
|
388
|
+
successRate: data.total > 0 ? data.successes / data.total * 100 : 0
|
|
389
|
+
})).sort((a, b) => b.total - a.total).slice(0, 5);
|
|
352
390
|
if (repos.length > 0) {
|
|
353
391
|
console.log(chalk3.white.bold("Top repos:"));
|
|
354
|
-
for (const
|
|
355
|
-
const shortName = repo.split("/")[1] || repo;
|
|
356
|
-
const rateStr = formatRate(data.
|
|
392
|
+
for (const data of repos) {
|
|
393
|
+
const shortName = data.repo.split("/")[1] || data.repo;
|
|
394
|
+
const rateStr = formatRate(data.successRate);
|
|
357
395
|
console.log(` ${chalk3.cyan(shortName.padEnd(20))} ${data.total.toString().padStart(5)} runs ${rateStr}`);
|
|
358
396
|
}
|
|
359
397
|
console.log();
|