@riventa/cli 1.0.0 → 1.1.0

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 +90 -39
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -91,7 +91,7 @@ async function loginCommand() {
91
91
  }
92
92
  console.log();
93
93
  console.log(chalk2.bold(" Welcome to Riventa.Dev CLI"));
94
- console.log(chalk2.gray(" To use the CLI, you need an API key.\n"));
94
+ console.log(chalk2.gray(" Connect your terminal to your Riventa account.\n"));
95
95
  const { method } = await inquirer.prompt([
96
96
  {
97
97
  type: "list",
@@ -99,60 +99,108 @@ async function loginCommand() {
99
99
  message: "How would you like to authenticate?",
100
100
  choices: [
101
101
  {
102
- name: `${chalk2.cyan("Browser")} \u2014 open dashboard to create/copy your API key`,
102
+ name: `${chalk2.cyan("Browser")} \u2014 sign in via browser (recommended)`,
103
103
  value: "browser"
104
104
  },
105
105
  {
106
- name: `${chalk2.cyan("Paste")} \u2014 enter an API key you already have`,
106
+ name: `${chalk2.cyan("API Key")} \u2014 paste an existing API key`,
107
107
  value: "manual"
108
108
  }
109
109
  ]
110
110
  }
111
111
  ]);
112
- let apiKey;
113
112
  if (method === "browser") {
114
- const dashboardUrl = "https://riventa.dev/settings?tab=api-keys";
115
- console.log(chalk2.gray(`
116
- Opening ${dashboardUrl}
117
- `));
113
+ await deviceFlowLogin();
114
+ } else {
115
+ await manualLogin();
116
+ }
117
+ }
118
+ async function deviceFlowLogin() {
119
+ const baseUrl = getBaseUrl();
120
+ const spinner = ora("Requesting authorization...").start();
121
+ try {
122
+ const response = await fetch(`${baseUrl}/v1/auth/device`, {
123
+ method: "POST",
124
+ headers: { "Content-Type": "application/json" }
125
+ });
126
+ if (!response.ok) {
127
+ throw new Error("Failed to create device code");
128
+ }
129
+ const result = await response.json();
130
+ const { deviceCode, userCode, verificationUrl } = result.data;
131
+ spinner.stop();
132
+ console.log();
133
+ console.log(chalk2.bold(" Your device code:"));
134
+ console.log();
135
+ console.log(chalk2.cyan(` ${userCode.slice(0, 4)} - ${userCode.slice(4)}`));
136
+ console.log();
137
+ console.log(chalk2.gray(` Opening ${verificationUrl}`));
138
+ console.log(chalk2.gray(" Enter the code above to authorize this device.\n"));
118
139
  try {
119
- await open(dashboardUrl);
140
+ await open(verificationUrl);
120
141
  } catch {
121
- console.log(chalk2.yellow(` Could not open browser. Please visit:
122
- ${dashboardUrl}
142
+ console.log(chalk2.yellow(` Could not open browser automatically.`));
143
+ console.log(chalk2.yellow(` Please visit: ${chalk2.white(verificationUrl)}
123
144
  `));
124
145
  }
125
- const answer = await inquirer.prompt([
126
- {
127
- type: "password",
128
- name: "apiKey",
129
- message: "Paste your API key (starts with riv_):",
130
- mask: "*",
131
- validate: (input) => {
132
- if (!input.startsWith("riv_")) return 'API key must start with "riv_"';
133
- if (input.length < 20) return "API key is too short";
134
- return true;
146
+ const pollSpinner = ora("Waiting for authorization...").start();
147
+ const maxAttempts = 60;
148
+ let attempts = 0;
149
+ while (attempts < maxAttempts) {
150
+ await sleep(3e3);
151
+ attempts++;
152
+ try {
153
+ const pollResponse = await fetch(`${baseUrl}/v1/auth/device?code=${deviceCode}`);
154
+ const pollResult = await pollResponse.json();
155
+ if (pollResult.data?.status === "authorized") {
156
+ pollSpinner.succeed(chalk2.green("Authorized!"));
157
+ const { apiKey, user } = pollResult.data;
158
+ setConfig("apiKey", apiKey);
159
+ setConfig("user", user);
160
+ console.log();
161
+ console.log(chalk2.gray(" Logged in as: ") + chalk2.white(user.email));
162
+ if (user.name) console.log(chalk2.gray(" Name: ") + chalk2.white(user.name));
163
+ console.log();
164
+ console.log(chalk2.gray(" Get started:"));
165
+ console.log(chalk2.gray(" riventa init \u2014 initialize a project"));
166
+ console.log(chalk2.gray(" riventa projects \u2014 list your projects"));
167
+ console.log(chalk2.gray(" riventa review \u2014 run AI code review"));
168
+ console.log(chalk2.gray(" riventa --help \u2014 see all commands"));
169
+ console.log();
170
+ return;
135
171
  }
136
- }
137
- ]);
138
- apiKey = answer.apiKey;
139
- } else {
140
- const answer = await inquirer.prompt([
141
- {
142
- type: "password",
143
- name: "apiKey",
144
- message: "Enter your API key:",
145
- mask: "*",
146
- validate: (input) => {
147
- if (!input.startsWith("riv_")) return 'API key must start with "riv_"';
148
- if (input.length < 20) return "API key is too short";
149
- return true;
172
+ if (pollResult.data?.status !== "pending") {
173
+ pollSpinner.fail(chalk2.red("Authorization failed"));
174
+ console.log(chalk2.red("\n The code may have expired. Run `riventa login` to try again.\n"));
175
+ process.exit(1);
150
176
  }
177
+ } catch {
151
178
  }
152
- ]);
153
- apiKey = answer.apiKey;
179
+ }
180
+ pollSpinner.fail(chalk2.red("Authorization timed out"));
181
+ console.log(chalk2.red("\n The code expired. Run `riventa login` to try again.\n"));
182
+ process.exit(1);
183
+ } catch {
184
+ spinner.fail(chalk2.red("Failed to start authorization"));
185
+ console.log(chalk2.red("\n Could not reach Riventa servers. Check your connection.\n"));
186
+ process.exit(1);
154
187
  }
155
- await verifyAndSaveKey(apiKey);
188
+ }
189
+ async function manualLogin() {
190
+ const answer = await inquirer.prompt([
191
+ {
192
+ type: "password",
193
+ name: "apiKey",
194
+ message: "Enter your API key (starts with riv_):",
195
+ mask: "*",
196
+ validate: (input) => {
197
+ if (!input.startsWith("riv_")) return 'API key must start with "riv_"';
198
+ if (input.length < 20) return "API key is too short";
199
+ return true;
200
+ }
201
+ }
202
+ ]);
203
+ await verifyAndSaveKey(answer.apiKey);
156
204
  }
157
205
  async function verifyAndSaveKey(apiKey) {
158
206
  const spinner = ora("Verifying API key...").start();
@@ -190,6 +238,9 @@ async function verifyAndSaveKey(apiKey) {
190
238
  process.exit(1);
191
239
  }
192
240
  }
241
+ function sleep(ms) {
242
+ return new Promise((resolve) => setTimeout(resolve, ms));
243
+ }
193
244
 
194
245
  // src/commands/init.ts
195
246
  import chalk3 from "chalk";
@@ -1184,7 +1235,7 @@ async function healthCommand() {
1184
1235
 
1185
1236
  // src/index.ts
1186
1237
  var program = new Command();
1187
- program.name("riventa").description(chalk14.cyan("Riventa.Dev CLI") + chalk14.gray(" \u2014 AI-powered DevOps automation")).version("1.0.0").addHelpText("after", `
1238
+ program.name("riventa").description(chalk14.cyan("Riventa.Dev CLI") + chalk14.gray(" \u2014 AI-powered DevOps automation")).version("1.1.0").addHelpText("after", `
1188
1239
  ${chalk14.bold("Quick Start:")}
1189
1240
  ${chalk14.gray("$")} riventa login Authenticate with your API key
1190
1241
  ${chalk14.gray("$")} riventa init Initialize a project
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@riventa/cli",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "Riventa.Dev CLI - Command line tools for DevOps automation",
5
5
  "type": "module",
6
6
  "bin": {