@tryghost/velo-cli 0.1.0 → 0.1.2
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 +92 -9
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -8,10 +8,14 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
8
8
|
|
|
9
9
|
// src/index.ts
|
|
10
10
|
import { Command } from "commander";
|
|
11
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
12
|
+
import { fileURLToPath } from "url";
|
|
13
|
+
import { dirname, join as join2 } from "path";
|
|
11
14
|
|
|
12
15
|
// src/commands/login.ts
|
|
13
16
|
import { createServer } from "http";
|
|
14
17
|
import { URL } from "url";
|
|
18
|
+
import { createInterface } from "readline";
|
|
15
19
|
import chalk from "chalk";
|
|
16
20
|
import open from "open";
|
|
17
21
|
import ora from "ora";
|
|
@@ -109,14 +113,76 @@ async function verifyToken(token) {
|
|
|
109
113
|
}
|
|
110
114
|
|
|
111
115
|
// src/commands/login.ts
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
116
|
+
function isHeadless() {
|
|
117
|
+
if (process.env.SSH_CLIENT || process.env.SSH_TTY) {
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
if (process.platform === "linux" && !process.env.DISPLAY && !process.env.WAYLAND_DISPLAY) {
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
if (process.env.container || process.env.DOCKER_CONTAINER) {
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
if (process.env.CI) {
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
function prompt(question) {
|
|
132
|
+
const rl = createInterface({
|
|
133
|
+
input: process.stdin,
|
|
134
|
+
output: process.stdout
|
|
135
|
+
});
|
|
136
|
+
return new Promise((resolve) => {
|
|
137
|
+
rl.question(question, (answer) => {
|
|
138
|
+
rl.close();
|
|
139
|
+
resolve(answer.trim());
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
async function manualLogin() {
|
|
144
|
+
const loginUrl = `${API_BASE_URL}/cli/login`;
|
|
145
|
+
console.log(chalk.blue("Velo CLI Login (Manual Mode)"));
|
|
146
|
+
console.log(chalk.gray("No browser detected. Please authenticate manually.\n"));
|
|
147
|
+
console.log(chalk.white("1. Open this URL in a browser on any device:"));
|
|
148
|
+
console.log(chalk.cyan(` ${loginUrl}
|
|
149
|
+
`));
|
|
150
|
+
console.log(chalk.white("2. Sign in with your Ghost Google account"));
|
|
151
|
+
console.log(chalk.white("3. Copy the token shown on the success page"));
|
|
152
|
+
console.log(chalk.white("4. Paste it below:\n"));
|
|
153
|
+
const token = await prompt(chalk.yellow("Token: "));
|
|
154
|
+
if (!token) {
|
|
155
|
+
console.log(chalk.red("\nNo token provided. Login cancelled."));
|
|
156
|
+
process.exit(1);
|
|
157
|
+
}
|
|
158
|
+
const spinner = ora("Verifying token...").start();
|
|
159
|
+
try {
|
|
160
|
+
const verification = await verifyToken(token);
|
|
161
|
+
spinner.stop();
|
|
162
|
+
if (!verification.valid) {
|
|
163
|
+
console.log(chalk.red(`
|
|
164
|
+
Invalid token: ${verification.error || "Unknown error"}`));
|
|
165
|
+
process.exit(1);
|
|
166
|
+
}
|
|
167
|
+
const credentials = {
|
|
168
|
+
token,
|
|
169
|
+
email: verification.email,
|
|
170
|
+
expires_at: new Date(Date.now() + 30 * 24 * 60 * 60 * 1e3).toISOString()
|
|
171
|
+
};
|
|
172
|
+
saveCredentials(credentials);
|
|
173
|
+
console.log(chalk.green(`
|
|
174
|
+
\u2713 Logged in as ${chalk.bold(credentials.email)}`));
|
|
175
|
+
console.log(chalk.gray(` Credentials saved to ~/.config/velo/credentials.json`));
|
|
176
|
+
console.log(chalk.gray(`
|
|
177
|
+
Run ${chalk.cyan("velo status")} to see CI metrics.`));
|
|
178
|
+
} catch (error) {
|
|
179
|
+
spinner.stop();
|
|
180
|
+
console.log(chalk.red(`
|
|
181
|
+
Failed to verify token: ${error.message}`));
|
|
182
|
+
process.exit(1);
|
|
119
183
|
}
|
|
184
|
+
}
|
|
185
|
+
async function browserLogin() {
|
|
120
186
|
const port = DEFAULT_CALLBACK_PORT;
|
|
121
187
|
const loginUrl = `${API_BASE_URL}/cli/login?port=${port}`;
|
|
122
188
|
console.log(chalk.blue("Velo CLI Login"));
|
|
@@ -231,6 +297,20 @@ ${error.message}`));
|
|
|
231
297
|
process.exit(1);
|
|
232
298
|
}
|
|
233
299
|
}
|
|
300
|
+
async function login(options = {}) {
|
|
301
|
+
const existing = loadCredentials();
|
|
302
|
+
if (existing) {
|
|
303
|
+
console.log(chalk.yellow(`Already logged in as ${existing.email}`));
|
|
304
|
+
console.log(chalk.gray(`Token expires: ${new Date(existing.expires_at).toLocaleDateString()}`));
|
|
305
|
+
console.log(chalk.gray(`Run ${chalk.cyan("velo logout")} to log out first.`));
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
if (options.manual || isHeadless()) {
|
|
309
|
+
await manualLogin();
|
|
310
|
+
} else {
|
|
311
|
+
await browserLogin();
|
|
312
|
+
}
|
|
313
|
+
}
|
|
234
314
|
|
|
235
315
|
// src/commands/logout.ts
|
|
236
316
|
import chalk2 from "chalk";
|
|
@@ -369,9 +449,11 @@ async function api(endpoint, options) {
|
|
|
369
449
|
}
|
|
370
450
|
|
|
371
451
|
// src/index.ts
|
|
452
|
+
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
453
|
+
var pkg = JSON.parse(readFileSync2(join2(__dirname, "..", "package.json"), "utf-8"));
|
|
372
454
|
var program = new Command();
|
|
373
|
-
program.name("velo").description("CLI for Velo CI/CD metrics").version(
|
|
374
|
-
program.command("login").description("Authenticate via Ghost SSO").action(login);
|
|
455
|
+
program.name("velo").description("CLI for Velo CI/CD metrics").version(pkg.version);
|
|
456
|
+
program.command("login").description("Authenticate via Ghost SSO").option("--manual", "Use manual token entry (for headless servers)").action((options) => login({ manual: options.manual }));
|
|
375
457
|
program.command("logout").description("Clear saved credentials").action(logout);
|
|
376
458
|
program.command("status").description("Show CI health overview").option("-r, --repo <repo>", "Filter by repository (e.g., TryGhost/Ghost)").option("-d, --days <days>", "Number of days to analyze (default: 7)", "7").action((options) => {
|
|
377
459
|
status({
|
|
@@ -389,6 +471,7 @@ program.command("api <endpoint>").description("Make raw API calls").option("-m,
|
|
|
389
471
|
program.addHelpText("after", `
|
|
390
472
|
Examples:
|
|
391
473
|
$ velo login # Authenticate via browser SSO
|
|
474
|
+
$ velo login --manual # Manual token entry (headless servers)
|
|
392
475
|
$ velo status # Quick CI health check
|
|
393
476
|
$ velo status --repo TryGhost/Ghost # Status for specific repo
|
|
394
477
|
$ velo api /api/health # Check API health
|