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