@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.
Files changed (2) hide show
  1. package/dist/index.js +92 -9
  2. 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
- 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;
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("0.1.0");
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
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.2",
4
4
  "description": "CLI for Velo CI/CD metrics",
5
5
  "type": "module",
6
6
  "bin": {