@l4yercak3/cli 1.0.4 → 1.0.6
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/bin/cli.js +12 -0
- package/package.json +1 -1
- package/src/api/backend-client.js +17 -3
- package/src/commands/api-keys.js +119 -0
- package/src/commands/login.js +11 -1
- package/src/commands/spread.js +183 -18
- package/src/commands/upgrade.js +48 -0
- package/tests/commands/login.test.js +3 -0
package/bin/cli.js
CHANGED
|
@@ -14,6 +14,8 @@ const loginCommand = require('../src/commands/login');
|
|
|
14
14
|
const logoutCommand = require('../src/commands/logout');
|
|
15
15
|
const statusCommand = require('../src/commands/status');
|
|
16
16
|
const spreadCommand = require('../src/commands/spread');
|
|
17
|
+
const apiKeysCommand = require('../src/commands/api-keys');
|
|
18
|
+
const upgradeCommand = require('../src/commands/upgrade');
|
|
17
19
|
|
|
18
20
|
// Create CLI program
|
|
19
21
|
const program = new Command();
|
|
@@ -48,6 +50,16 @@ program
|
|
|
48
50
|
.description(spreadCommand.description)
|
|
49
51
|
.action(spreadCommand.handler);
|
|
50
52
|
|
|
53
|
+
program
|
|
54
|
+
.command(apiKeysCommand.command)
|
|
55
|
+
.description(apiKeysCommand.description)
|
|
56
|
+
.action(apiKeysCommand.handler);
|
|
57
|
+
|
|
58
|
+
program
|
|
59
|
+
.command(upgradeCommand.command)
|
|
60
|
+
.description(upgradeCommand.description)
|
|
61
|
+
.action(upgradeCommand.handler);
|
|
62
|
+
|
|
51
63
|
// Show logo and welcome message if no command provided
|
|
52
64
|
if (process.argv.length === 2) {
|
|
53
65
|
console.log(''); // initial spacing
|
package/package.json
CHANGED
|
@@ -37,6 +37,7 @@ class BackendClient {
|
|
|
37
37
|
|
|
38
38
|
/**
|
|
39
39
|
* Make API request
|
|
40
|
+
* Returns response data with error details preserved for specific handling
|
|
40
41
|
*/
|
|
41
42
|
async request(method, endpoint, data = null) {
|
|
42
43
|
const url = `${this.baseUrl}${endpoint}`;
|
|
@@ -54,7 +55,14 @@ class BackendClient {
|
|
|
54
55
|
const responseData = await response.json();
|
|
55
56
|
|
|
56
57
|
if (!response.ok) {
|
|
57
|
-
|
|
58
|
+
// Create error with additional details from backend
|
|
59
|
+
const error = new Error(responseData.message || responseData.error || `API request failed: ${response.status}`);
|
|
60
|
+
error.code = responseData.code || 'UNKNOWN_ERROR';
|
|
61
|
+
error.suggestion = responseData.suggestion || null;
|
|
62
|
+
error.upgradeUrl = responseData.upgradeUrl || null;
|
|
63
|
+
error.upgradeCommand = responseData.upgradeCommand || null;
|
|
64
|
+
error.status = response.status;
|
|
65
|
+
throw error;
|
|
58
66
|
}
|
|
59
67
|
|
|
60
68
|
return responseData;
|
|
@@ -124,13 +132,19 @@ class BackendClient {
|
|
|
124
132
|
}
|
|
125
133
|
}
|
|
126
134
|
|
|
135
|
+
/**
|
|
136
|
+
* List API keys for an organization
|
|
137
|
+
* Returns: { keys, limit, currentCount, canCreateMore, limitDescription }
|
|
138
|
+
*/
|
|
139
|
+
async listApiKeys(organizationId) {
|
|
140
|
+
return await this.request('GET', `/api/v1/api-keys/list?organizationId=${organizationId}`);
|
|
141
|
+
}
|
|
142
|
+
|
|
127
143
|
/**
|
|
128
144
|
* Generate API key for organization
|
|
129
145
|
* Note: This calls Convex action directly, requires session
|
|
130
146
|
*/
|
|
131
147
|
async generateApiKey(organizationId, name, scopes = ['*']) {
|
|
132
|
-
// This will need to call Convex action via backend API wrapper
|
|
133
|
-
// For now, placeholder
|
|
134
148
|
return await this.request('POST', `/api/v1/api-keys/generate`, {
|
|
135
149
|
organizationId,
|
|
136
150
|
name,
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API Keys Command
|
|
3
|
+
* List and manage API keys for organizations
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const configManager = require('../config/config-manager');
|
|
7
|
+
const backendClient = require('../api/backend-client');
|
|
8
|
+
const inquirer = require('inquirer');
|
|
9
|
+
const chalk = require('chalk');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* API Keys list command handler
|
|
13
|
+
*/
|
|
14
|
+
async function handleApiKeysList() {
|
|
15
|
+
// Check if logged in
|
|
16
|
+
if (!configManager.isLoggedIn()) {
|
|
17
|
+
console.log(chalk.yellow(' ⚠️ You must be logged in first'));
|
|
18
|
+
console.log(chalk.gray('\n Run "l4yercak3 login" to authenticate\n'));
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
console.log(chalk.cyan(' 🔑 API Keys\n'));
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
// Get organizations
|
|
26
|
+
const orgsResponse = await backendClient.getOrganizations();
|
|
27
|
+
const organizations = Array.isArray(orgsResponse)
|
|
28
|
+
? orgsResponse
|
|
29
|
+
: orgsResponse.organizations || orgsResponse.data || [];
|
|
30
|
+
|
|
31
|
+
if (organizations.length === 0) {
|
|
32
|
+
console.log(chalk.yellow(' ⚠️ No organizations found'));
|
|
33
|
+
console.log(chalk.gray('\n Run "l4yercak3 spread" to create an organization\n'));
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Select organization if multiple
|
|
38
|
+
let organizationId;
|
|
39
|
+
let organizationName;
|
|
40
|
+
|
|
41
|
+
if (organizations.length === 1) {
|
|
42
|
+
organizationId = organizations[0].id;
|
|
43
|
+
organizationName = organizations[0].name;
|
|
44
|
+
} else {
|
|
45
|
+
const { orgChoice } = await inquirer.prompt([
|
|
46
|
+
{
|
|
47
|
+
type: 'list',
|
|
48
|
+
name: 'orgChoice',
|
|
49
|
+
message: 'Select organization:',
|
|
50
|
+
choices: organizations.map(org => ({
|
|
51
|
+
name: `${org.name} (${org.id})`,
|
|
52
|
+
value: org.id,
|
|
53
|
+
})),
|
|
54
|
+
},
|
|
55
|
+
]);
|
|
56
|
+
organizationId = orgChoice;
|
|
57
|
+
organizationName = organizations.find(org => org.id === orgChoice)?.name;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
console.log(chalk.gray(` Organization: ${organizationName}\n`));
|
|
61
|
+
|
|
62
|
+
// List API keys
|
|
63
|
+
const keysResponse = await backendClient.listApiKeys(organizationId);
|
|
64
|
+
const keys = keysResponse.keys || [];
|
|
65
|
+
|
|
66
|
+
if (keys.length === 0) {
|
|
67
|
+
console.log(chalk.yellow(' No API keys found for this organization'));
|
|
68
|
+
console.log(chalk.gray('\n Run "l4yercak3 spread" to generate an API key\n'));
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Display keys
|
|
73
|
+
console.log(chalk.green(` Found ${keys.length} API key(s):\n`));
|
|
74
|
+
|
|
75
|
+
keys.forEach((key, i) => {
|
|
76
|
+
const maskedKey = key.key ? `${key.key.substring(0, 15)}...` : '[hidden]';
|
|
77
|
+
const name = key.name || `Key ${i + 1}`;
|
|
78
|
+
const created = key.createdAt ? new Date(key.createdAt).toLocaleDateString() : 'Unknown';
|
|
79
|
+
|
|
80
|
+
console.log(chalk.white(` ${i + 1}. ${name}`));
|
|
81
|
+
console.log(chalk.gray(` Key: ${maskedKey}`));
|
|
82
|
+
console.log(chalk.gray(` Created: ${created}`));
|
|
83
|
+
if (key.scopes) {
|
|
84
|
+
console.log(chalk.gray(` Scopes: ${key.scopes.join(', ')}`));
|
|
85
|
+
}
|
|
86
|
+
console.log('');
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// Show limit info
|
|
90
|
+
if (keysResponse.limitDescription) {
|
|
91
|
+
console.log(chalk.gray(` Limit: ${keysResponse.limitDescription}`));
|
|
92
|
+
}
|
|
93
|
+
if (keysResponse.canCreateMore !== undefined) {
|
|
94
|
+
if (keysResponse.canCreateMore) {
|
|
95
|
+
console.log(chalk.green(` ✅ You can create more API keys`));
|
|
96
|
+
} else {
|
|
97
|
+
console.log(chalk.yellow(` ⚠️ You've reached your API key limit`));
|
|
98
|
+
console.log(chalk.gray(' Upgrade at: https://app.l4yercak3.com/settings/billing'));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
console.log('');
|
|
102
|
+
|
|
103
|
+
} catch (error) {
|
|
104
|
+
if (error.code === 'SESSION_EXPIRED') {
|
|
105
|
+
console.log(chalk.red(`\n ❌ Session expired. Please run "l4yercak3 login" again.\n`));
|
|
106
|
+
} else if (error.code === 'NOT_AUTHORIZED') {
|
|
107
|
+
console.log(chalk.red(`\n ❌ You don't have permission to view API keys for this organization.\n`));
|
|
108
|
+
} else {
|
|
109
|
+
console.error(chalk.red(` ❌ Error listing API keys: ${error.message}\n`));
|
|
110
|
+
}
|
|
111
|
+
process.exit(1);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
module.exports = {
|
|
116
|
+
command: 'api-keys',
|
|
117
|
+
description: 'List API keys for your organizations',
|
|
118
|
+
handler: handleApiKeysList,
|
|
119
|
+
};
|
package/src/commands/login.js
CHANGED
|
@@ -9,6 +9,7 @@ const backendClient = require('../api/backend-client');
|
|
|
9
9
|
const chalk = require('chalk');
|
|
10
10
|
const inquirer = require('inquirer');
|
|
11
11
|
const projectDetector = require('../detectors');
|
|
12
|
+
const { showLogo } = require('../logo');
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Generate retro Windows 95 style HTML page
|
|
@@ -123,6 +124,11 @@ async function handleLogin() {
|
|
|
123
124
|
// Check if already logged in
|
|
124
125
|
if (configManager.isLoggedIn()) {
|
|
125
126
|
const session = configManager.getSession();
|
|
127
|
+
|
|
128
|
+
// Show logo
|
|
129
|
+
console.log('');
|
|
130
|
+
showLogo(false);
|
|
131
|
+
|
|
126
132
|
console.log(chalk.green(' ✅ You are already logged in'));
|
|
127
133
|
if (session.email) {
|
|
128
134
|
console.log(chalk.gray(` Email: ${session.email}`));
|
|
@@ -179,7 +185,11 @@ async function handleLogin() {
|
|
|
179
185
|
console.log(chalk.gray(' Session saved, but validation failed. You may need to log in again.'));
|
|
180
186
|
}
|
|
181
187
|
|
|
182
|
-
|
|
188
|
+
// Show logo after successful login
|
|
189
|
+
console.log('');
|
|
190
|
+
showLogo(false);
|
|
191
|
+
|
|
192
|
+
console.log(chalk.green(' ✅ Successfully logged in!\n'));
|
|
183
193
|
|
|
184
194
|
const finalSession = configManager.getSession();
|
|
185
195
|
if (finalSession && finalSession.email) {
|
package/src/commands/spread.js
CHANGED
|
@@ -20,15 +20,36 @@ async function createOrganization(orgName) {
|
|
|
20
20
|
// Handle different response formats
|
|
21
21
|
const organizationId = newOrg.organizationId || newOrg.id || newOrg.data?.organizationId || newOrg.data?.id;
|
|
22
22
|
const organizationName = newOrg.name || orgName;
|
|
23
|
-
|
|
23
|
+
|
|
24
24
|
if (!organizationId) {
|
|
25
25
|
throw new Error('Organization ID not found in response. Please check backend API endpoint.');
|
|
26
26
|
}
|
|
27
|
-
|
|
27
|
+
|
|
28
28
|
console.log(chalk.green(` ✅ Organization created: ${organizationName}\n`));
|
|
29
29
|
return { organizationId, organizationName };
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Helper function to generate a new API key
|
|
34
|
+
*/
|
|
35
|
+
async function generateNewApiKey(organizationId) {
|
|
36
|
+
console.log(chalk.gray(' Generating API key...'));
|
|
37
|
+
const apiKeyResponse = await backendClient.generateApiKey(
|
|
38
|
+
organizationId,
|
|
39
|
+
'CLI Generated Key',
|
|
40
|
+
['*']
|
|
41
|
+
);
|
|
42
|
+
// Handle different response formats
|
|
43
|
+
const apiKey = apiKeyResponse.key || apiKeyResponse.apiKey || apiKeyResponse.data?.key || apiKeyResponse.data?.apiKey;
|
|
44
|
+
|
|
45
|
+
if (!apiKey) {
|
|
46
|
+
throw new Error('API key not found in response. Please check backend API endpoint.');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
console.log(chalk.green(` ✅ API key generated\n`));
|
|
50
|
+
return apiKey;
|
|
51
|
+
}
|
|
52
|
+
|
|
32
53
|
async function handleSpread() {
|
|
33
54
|
// Check if logged in
|
|
34
55
|
if (!configManager.isLoggedIn()) {
|
|
@@ -181,28 +202,172 @@ async function handleSpread() {
|
|
|
181
202
|
process.exit(1);
|
|
182
203
|
}
|
|
183
204
|
|
|
184
|
-
// Step 3:
|
|
205
|
+
// Step 3: API Key Setup
|
|
185
206
|
console.log(chalk.cyan(' 🔑 API Key Setup\n'));
|
|
186
207
|
let apiKey;
|
|
187
208
|
|
|
188
209
|
try {
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
210
|
+
// First, check if organization already has API keys
|
|
211
|
+
console.log(chalk.gray(' Checking existing API keys...'));
|
|
212
|
+
let existingKeys = null;
|
|
213
|
+
|
|
214
|
+
try {
|
|
215
|
+
existingKeys = await backendClient.listApiKeys(organizationId);
|
|
216
|
+
} catch (listError) {
|
|
217
|
+
// If listing fails, continue to try generating
|
|
218
|
+
console.log(chalk.gray(' Could not check existing keys, attempting to generate...'));
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (existingKeys && existingKeys.keys && existingKeys.keys.length > 0) {
|
|
222
|
+
// Organization has existing keys
|
|
223
|
+
console.log(chalk.yellow(` ⚠️ Found ${existingKeys.keys.length} existing API key(s)`));
|
|
224
|
+
|
|
225
|
+
if (existingKeys.limitDescription) {
|
|
226
|
+
console.log(chalk.gray(` Limit: ${existingKeys.limitDescription}`));
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Show existing keys (masked)
|
|
230
|
+
existingKeys.keys.forEach((key, i) => {
|
|
231
|
+
const maskedKey = key.key ? `${key.key.substring(0, 10)}...` : key.name || `Key ${i + 1}`;
|
|
232
|
+
console.log(chalk.gray(` • ${key.name || 'Unnamed'}: ${maskedKey}`));
|
|
233
|
+
});
|
|
234
|
+
console.log('');
|
|
235
|
+
|
|
236
|
+
if (!existingKeys.canCreateMore) {
|
|
237
|
+
// At limit - offer to use existing key
|
|
238
|
+
const { useExisting } = await inquirer.prompt([
|
|
239
|
+
{
|
|
240
|
+
type: 'confirm',
|
|
241
|
+
name: 'useExisting',
|
|
242
|
+
message: 'You\'ve reached your API key limit. Use an existing key from your .env.local file?',
|
|
243
|
+
default: true,
|
|
244
|
+
},
|
|
245
|
+
]);
|
|
246
|
+
|
|
247
|
+
if (useExisting) {
|
|
248
|
+
const { manualKey } = await inquirer.prompt([
|
|
249
|
+
{
|
|
250
|
+
type: 'input',
|
|
251
|
+
name: 'manualKey',
|
|
252
|
+
message: 'Enter your existing API key:',
|
|
253
|
+
validate: (input) => input.trim().length > 0 || 'API key is required',
|
|
254
|
+
},
|
|
255
|
+
]);
|
|
256
|
+
apiKey = manualKey.trim();
|
|
257
|
+
console.log(chalk.green(` ✅ Using provided API key\n`));
|
|
258
|
+
} else {
|
|
259
|
+
console.log(chalk.yellow('\n ⚠️ To generate more API keys, upgrade your plan at https://app.l4yercak3.com/settings/billing\n'));
|
|
260
|
+
process.exit(0);
|
|
261
|
+
}
|
|
262
|
+
} else {
|
|
263
|
+
// Can create more - ask what to do
|
|
264
|
+
const { keyAction } = await inquirer.prompt([
|
|
265
|
+
{
|
|
266
|
+
type: 'list',
|
|
267
|
+
name: 'keyAction',
|
|
268
|
+
message: 'What would you like to do?',
|
|
269
|
+
choices: [
|
|
270
|
+
{ name: 'Generate a new API key', value: 'generate' },
|
|
271
|
+
{ name: 'Enter an existing API key', value: 'existing' },
|
|
272
|
+
],
|
|
273
|
+
},
|
|
274
|
+
]);
|
|
275
|
+
|
|
276
|
+
if (keyAction === 'existing') {
|
|
277
|
+
const { manualKey } = await inquirer.prompt([
|
|
278
|
+
{
|
|
279
|
+
type: 'input',
|
|
280
|
+
name: 'manualKey',
|
|
281
|
+
message: 'Enter your existing API key:',
|
|
282
|
+
validate: (input) => input.trim().length > 0 || 'API key is required',
|
|
283
|
+
},
|
|
284
|
+
]);
|
|
285
|
+
apiKey = manualKey.trim();
|
|
286
|
+
console.log(chalk.green(` ✅ Using provided API key\n`));
|
|
287
|
+
} else {
|
|
288
|
+
// Generate new key
|
|
289
|
+
apiKey = await generateNewApiKey(organizationId);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
} else {
|
|
293
|
+
// No existing keys - generate one
|
|
294
|
+
apiKey = await generateNewApiKey(organizationId);
|
|
200
295
|
}
|
|
201
|
-
|
|
202
|
-
console.log(chalk.green(` ✅ API key generated\n`));
|
|
203
296
|
} catch (error) {
|
|
204
|
-
|
|
205
|
-
|
|
297
|
+
// Handle specific error codes
|
|
298
|
+
if (error.code === 'API_KEY_LIMIT_REACHED') {
|
|
299
|
+
console.log(chalk.yellow(`\n ⚠️ ${error.message}`));
|
|
300
|
+
if (error.suggestion) {
|
|
301
|
+
console.log(chalk.gray(` ${error.suggestion}`));
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Show upgrade option if available
|
|
305
|
+
if (error.upgradeUrl) {
|
|
306
|
+
console.log(chalk.cyan(`\n 🚀 Upgrade your plan to get more API keys:`));
|
|
307
|
+
console.log(chalk.gray(` ${error.upgradeUrl}\n`));
|
|
308
|
+
|
|
309
|
+
const { action } = await inquirer.prompt([
|
|
310
|
+
{
|
|
311
|
+
type: 'list',
|
|
312
|
+
name: 'action',
|
|
313
|
+
message: 'What would you like to do?',
|
|
314
|
+
choices: [
|
|
315
|
+
{ name: 'Open upgrade page in browser', value: 'upgrade' },
|
|
316
|
+
{ name: 'Enter an existing API key', value: 'existing' },
|
|
317
|
+
{ name: 'Exit', value: 'exit' },
|
|
318
|
+
],
|
|
319
|
+
},
|
|
320
|
+
]);
|
|
321
|
+
|
|
322
|
+
if (action === 'upgrade') {
|
|
323
|
+
const { default: open } = require('open');
|
|
324
|
+
console.log(chalk.gray(' Opening browser...'));
|
|
325
|
+
await open(error.upgradeUrl);
|
|
326
|
+
console.log(chalk.gray('\n After upgrading, run "l4yercak3 spread" again.\n'));
|
|
327
|
+
process.exit(0);
|
|
328
|
+
} else if (action === 'existing') {
|
|
329
|
+
const { manualKey } = await inquirer.prompt([
|
|
330
|
+
{
|
|
331
|
+
type: 'input',
|
|
332
|
+
name: 'manualKey',
|
|
333
|
+
message: 'Enter your existing API key:',
|
|
334
|
+
validate: (input) => input.trim().length > 0 || 'API key is required',
|
|
335
|
+
},
|
|
336
|
+
]);
|
|
337
|
+
apiKey = manualKey.trim();
|
|
338
|
+
console.log(chalk.green(` ✅ Using provided API key\n`));
|
|
339
|
+
} else {
|
|
340
|
+
process.exit(0);
|
|
341
|
+
}
|
|
342
|
+
} else {
|
|
343
|
+
// No upgrade URL - fallback to manual entry
|
|
344
|
+
console.log(chalk.gray('\n You can enter an existing API key or upgrade your plan.\n'));
|
|
345
|
+
|
|
346
|
+
const { manualKey } = await inquirer.prompt([
|
|
347
|
+
{
|
|
348
|
+
type: 'input',
|
|
349
|
+
name: 'manualKey',
|
|
350
|
+
message: 'Enter your existing API key (or press Enter to exit):',
|
|
351
|
+
},
|
|
352
|
+
]);
|
|
353
|
+
|
|
354
|
+
if (manualKey.trim()) {
|
|
355
|
+
apiKey = manualKey.trim();
|
|
356
|
+
console.log(chalk.green(` ✅ Using provided API key\n`));
|
|
357
|
+
} else {
|
|
358
|
+
process.exit(0);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
} else if (error.code === 'SESSION_EXPIRED') {
|
|
362
|
+
console.log(chalk.red(`\n ❌ Session expired. Please run "l4yercak3 login" again.\n`));
|
|
363
|
+
process.exit(1);
|
|
364
|
+
} else if (error.code === 'NOT_AUTHORIZED') {
|
|
365
|
+
console.log(chalk.red(`\n ❌ You don't have permission to manage API keys for this organization.\n`));
|
|
366
|
+
process.exit(1);
|
|
367
|
+
} else {
|
|
368
|
+
console.error(chalk.red(` ❌ Error setting up API key: ${error.message}\n`));
|
|
369
|
+
process.exit(1);
|
|
370
|
+
}
|
|
206
371
|
}
|
|
207
372
|
|
|
208
373
|
// Step 4: Feature selection
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Upgrade Command
|
|
3
|
+
* Opens the upgrade page in the browser for plan upgrades
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const configManager = require('../config/config-manager');
|
|
7
|
+
const chalk = require('chalk');
|
|
8
|
+
const { default: open } = require('open');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Upgrade command handler
|
|
12
|
+
*/
|
|
13
|
+
async function handleUpgrade() {
|
|
14
|
+
// Check if logged in
|
|
15
|
+
if (!configManager.isLoggedIn()) {
|
|
16
|
+
console.log(chalk.yellow(' ⚠️ You must be logged in first'));
|
|
17
|
+
console.log(chalk.gray('\n Run "l4yercak3 login" to authenticate\n'));
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const session = configManager.getSession();
|
|
22
|
+
const backendUrl = configManager.getBackendUrl();
|
|
23
|
+
|
|
24
|
+
console.log(chalk.cyan(' 🚀 Opening upgrade page...\n'));
|
|
25
|
+
|
|
26
|
+
// Build upgrade URL with CLI token for seamless authentication
|
|
27
|
+
const upgradeUrl = `${backendUrl}/upgrade?token=${encodeURIComponent(session.token)}&reason=cli_upgrade`;
|
|
28
|
+
|
|
29
|
+
console.log(chalk.gray(` URL: ${upgradeUrl}\n`));
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
await open(upgradeUrl);
|
|
33
|
+
console.log(chalk.green(' ✅ Upgrade page opened in your browser\n'));
|
|
34
|
+
console.log(chalk.gray(' Select a plan to unlock more features:'));
|
|
35
|
+
console.log(chalk.gray(' • More API keys'));
|
|
36
|
+
console.log(chalk.gray(' • Priority support'));
|
|
37
|
+
console.log(chalk.gray(' • Advanced features\n'));
|
|
38
|
+
} catch (error) {
|
|
39
|
+
console.log(chalk.yellow(' ⚠️ Could not open browser automatically'));
|
|
40
|
+
console.log(chalk.gray(`\n Please visit: ${upgradeUrl}\n`));
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
module.exports = {
|
|
45
|
+
command: 'upgrade',
|
|
46
|
+
description: 'Upgrade your L4YERCAK3 plan',
|
|
47
|
+
handler: handleUpgrade,
|
|
48
|
+
};
|