@vibe-db/cli 1.0.0 → 1.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.
package/bin/vibedb.js CHANGED
@@ -4,6 +4,7 @@ const { Command } = require('commander');
4
4
  const chalk = require('chalk');
5
5
  const signupCommand = require('../src/commands/signup');
6
6
  const loginCommand = require('../src/commands/login');
7
+ const logoutCommand = require('../src/commands/logout');
7
8
  const initCommand = require('../src/commands/init');
8
9
  const listCommand = require('../src/commands/list');
9
10
  const billingInfoCommand = require('../src/commands/billing-info');
@@ -16,7 +17,7 @@ const program = new Command();
16
17
  program
17
18
  .name('vibedb')
18
19
  .description('VibeDB CLI - Instant database provisioning for AI-assisted development')
19
- .version('1.0.0');
20
+ .version('1.1.0');
20
21
 
21
22
  program
22
23
  .command('signup')
@@ -42,6 +43,18 @@ program
42
43
  }
43
44
  });
44
45
 
46
+ program
47
+ .command('logout')
48
+ .description('Logout and remove saved credentials')
49
+ .action(async () => {
50
+ try {
51
+ await logoutCommand();
52
+ } catch (error) {
53
+ console.error(chalk.red('Unexpected error:'), error.message);
54
+ process.exit(1);
55
+ }
56
+ });
57
+
45
58
  program
46
59
  .command('init')
47
60
  .description('Download VIBEDB.md prompt file to your project')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibe-db/cli",
3
- "version": "1.0.0",
3
+ "version": "1.1.1",
4
4
  "description": "Command-line interface for VibeDB - instant database provisioning for AI-assisted development",
5
5
  "main": "bin/vibedb.js",
6
6
  "bin": {
@@ -27,7 +27,8 @@
27
27
  "chalk": "^4.1.2",
28
28
  "cli-table3": "^0.6.3",
29
29
  "commander": "^11.0.0",
30
- "inquirer": "^8.2.5"
30
+ "inquirer": "^8.2.5",
31
+ "open": "^10.0.0"
31
32
  },
32
33
  "engines": {
33
34
  "node": ">=18.0.0"
package/src/api.js CHANGED
@@ -183,6 +183,38 @@ async function getInvoices(apiKey) {
183
183
  }
184
184
  }
185
185
 
186
+ /**
187
+ * Start device authorization flow
188
+ */
189
+ async function startDeviceAuth() {
190
+ try {
191
+ const response = await axios.post(`${API_BASE_URL}/v1/auth/device`);
192
+ return response.data;
193
+ } catch (error) {
194
+ if (error.response) {
195
+ const { error: errorCode, message } = error.response.data;
196
+ throw new Error(message || errorCode || 'Failed to start device authorization');
197
+ }
198
+ throw new Error(`Network error: ${error.message}`);
199
+ }
200
+ }
201
+
202
+ /**
203
+ * Poll device authorization status
204
+ */
205
+ async function pollDeviceAuth(deviceCode) {
206
+ try {
207
+ const response = await axios.get(`${API_BASE_URL}/v1/auth/device/${deviceCode}/poll`);
208
+ return response.data;
209
+ } catch (error) {
210
+ if (error.response) {
211
+ const { error: errorCode, message } = error.response.data;
212
+ throw new Error(message || errorCode || 'Failed to poll device authorization');
213
+ }
214
+ throw new Error(`Network error: ${error.message}`);
215
+ }
216
+ }
217
+
186
218
  module.exports = {
187
219
  signup,
188
220
  login,
@@ -193,4 +225,6 @@ module.exports = {
193
225
  createSubscription,
194
226
  cancelSubscription,
195
227
  getInvoices,
228
+ startDeviceAuth,
229
+ pollDeviceAuth,
196
230
  };
@@ -46,12 +46,21 @@ async function initCommand() {
46
46
 
47
47
  console.log(chalk.green.bold('\n✓ VIBEDB.md downloaded successfully!\n'));
48
48
  console.log(chalk.white('Location:'), chalk.cyan(promptFilePath));
49
- console.log(chalk.white('API Key:'), chalk.cyan(userConfig.api_key));
50
49
  console.log(chalk.white('Email:'), chalk.cyan(userConfig.email));
51
- console.log(chalk.white('\nNext steps:'));
52
- console.log(chalk.gray(' 1. Add VIBEDB.md to your project repository'));
53
- console.log(chalk.gray(' 2. Tell your AI assistant: "I need a database for my app"'));
54
- console.log(chalk.gray(' 3. The AI will read VIBEDB.md and provision a database for you!'));
50
+
51
+ console.log(chalk.yellow('\n⚠️ Security reminder:'));
52
+ console.log(chalk.gray(' Your API key is embedded in VIBEDB.md'));
53
+ console.log(chalk.gray(' Keep it secure - don\'t share or commit to public repos'));
54
+ console.log(chalk.gray(' • Add VIBEDB.md to .gitignore if needed'));
55
+
56
+ console.log(chalk.white('\nHow to use:'));
57
+ console.log(chalk.gray(' 1. Tell your AI assistant: '), chalk.cyan('"Read VIBEDB.md and provision a database"'));
58
+ console.log(chalk.gray(' 2. The AI will read the file and create a database for you'));
59
+ console.log(chalk.gray(' 3. You\'ll get connection details instantly!'));
60
+
61
+ console.log(chalk.white('\nNeed help?'));
62
+ console.log(chalk.gray(' • Visit: '), chalk.cyan('https://vibedb.dev/docs'));
63
+ console.log(chalk.gray(' • See examples in VIBEDB.md'));
55
64
  console.log();
56
65
  } catch (error) {
57
66
  console.error(chalk.red.bold('\n✗ Init failed:'), chalk.red(error.message));
@@ -3,9 +3,85 @@ const chalk = require('chalk');
3
3
  const api = require('../api');
4
4
  const config = require('../config');
5
5
 
6
- async function loginCommand() {
7
- console.log(chalk.blue.bold('\n🔐 VibeDB Login\n'));
6
+ function sleep(ms) {
7
+ return new Promise(resolve => setTimeout(resolve, ms));
8
+ }
8
9
 
10
+ function formatUserCode(code) {
11
+ // Format as XXXX-XXXX for readability
12
+ return `${code.slice(0, 4)}-${code.slice(4)}`;
13
+ }
14
+
15
+ async function loginWithGitHub() {
16
+ console.log(chalk.blue('Starting GitHub authentication...\n'));
17
+
18
+ try {
19
+ // 1. Start device auth
20
+ const data = await api.startDeviceAuth();
21
+ const { device_code, user_code, verification_uri, interval } = data;
22
+
23
+ // Build URL with pre-populated code for better UX
24
+ const urlWithCode = `${verification_uri}?code=${formatUserCode(user_code)}`;
25
+
26
+ console.log(chalk.bold('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
27
+ console.log(chalk.cyan.bold(` Open: ${urlWithCode}`));
28
+ console.log(chalk.yellow.bold(` Your code: ${formatUserCode(user_code)}`));
29
+ console.log(chalk.bold('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'));
30
+
31
+ // Try to open browser automatically with pre-populated code
32
+ try {
33
+ const open = (await import('open')).default;
34
+ await open(urlWithCode);
35
+ console.log(chalk.gray('Opened browser with code pre-filled...\n'));
36
+ } catch (err) {
37
+ console.log(chalk.gray('Please open the URL manually\n'));
38
+ }
39
+
40
+ console.log(chalk.gray('Waiting for authorization...'));
41
+
42
+ // 2. Poll for completion
43
+ const pollInterval = (interval || 5) * 1000;
44
+ let attempts = 0;
45
+ const maxAttempts = 180 / (interval || 5); // 15 minutes / interval
46
+
47
+ while (attempts < maxAttempts) {
48
+ await sleep(pollInterval);
49
+ attempts++;
50
+
51
+ try {
52
+ const pollData = await api.pollDeviceAuth(device_code);
53
+
54
+ if (pollData.status === 'complete') {
55
+ // Save API key
56
+ config.saveAuth(pollData.api_key, pollData.email);
57
+ console.log(chalk.green.bold('\n✓ Login successful!\n'));
58
+ console.log(chalk.white('Email:'), chalk.cyan(pollData.email));
59
+ console.log(chalk.gray(`API key saved to ${config.getConfigPath()}`));
60
+ console.log();
61
+ return;
62
+ }
63
+
64
+ // Still pending, continue polling
65
+ process.stdout.write(chalk.gray('.'));
66
+ } catch (err) {
67
+ if (err.response?.data?.error === 'authorization_expired') {
68
+ console.error(chalk.red.bold('\n\n✗ Authorization expired. Please try again.'));
69
+ process.exit(1);
70
+ }
71
+ // Other errors, continue polling
72
+ }
73
+ }
74
+
75
+ console.error(chalk.red.bold('\n\n✗ Authorization timed out. Please try again.'));
76
+ process.exit(1);
77
+
78
+ } catch (error) {
79
+ console.error(chalk.red.bold('\n✗ GitHub login failed:'), chalk.red(error.message));
80
+ process.exit(1);
81
+ }
82
+ }
83
+
84
+ async function loginWithEmail() {
9
85
  try {
10
86
  const answers = await inquirer.prompt([
11
87
  {
@@ -50,4 +126,37 @@ async function loginCommand() {
50
126
  }
51
127
  }
52
128
 
129
+ async function loginCommand() {
130
+ console.log(chalk.blue.bold('\n🔐 VibeDB Login\n'));
131
+
132
+ try {
133
+ const { method } = await inquirer.prompt([
134
+ {
135
+ type: 'list',
136
+ name: 'method',
137
+ message: 'How would you like to log in?',
138
+ choices: [
139
+ { name: '🐙 GitHub (Recommended)', value: 'github' },
140
+ { name: '📧 Email & Password', value: 'email' },
141
+ ],
142
+ },
143
+ ]);
144
+
145
+ console.log(); // Empty line for spacing
146
+
147
+ if (method === 'github') {
148
+ await loginWithGitHub();
149
+ } else {
150
+ await loginWithEmail();
151
+ }
152
+ } catch (error) {
153
+ if (error.isTtyError) {
154
+ console.error(chalk.red('Prompt couldn\'t be rendered in the current environment'));
155
+ } else {
156
+ console.error(chalk.red('An unexpected error occurred:', error.message));
157
+ }
158
+ process.exit(1);
159
+ }
160
+ }
161
+
53
162
  module.exports = loginCommand;
@@ -0,0 +1,47 @@
1
+ const fs = require('fs');
2
+ const chalk = require('chalk');
3
+ const inquirer = require('inquirer');
4
+ const config = require('../config');
5
+
6
+ async function logoutCommand() {
7
+ console.log(chalk.blue.bold('\n👋 VibeDB Logout\n'));
8
+
9
+ try {
10
+ // Check if logged in
11
+ if (!config.isLoggedIn()) {
12
+ console.log(chalk.yellow('You are not currently logged in.'));
13
+ process.exit(0);
14
+ }
15
+
16
+ const userConfig = config.readConfig();
17
+
18
+ // Confirm logout
19
+ const { confirm } = await inquirer.prompt([
20
+ {
21
+ type: 'confirm',
22
+ name: 'confirm',
23
+ message: `Log out of ${userConfig.email}?`,
24
+ default: true,
25
+ },
26
+ ]);
27
+
28
+ if (!confirm) {
29
+ console.log(chalk.yellow('\nLogout cancelled.'));
30
+ process.exit(0);
31
+ }
32
+
33
+ // Delete config file
34
+ const configPath = config.getConfigPath();
35
+ fs.unlinkSync(configPath);
36
+
37
+ console.log(chalk.green.bold('\n✓ Logged out successfully!\n'));
38
+ console.log(chalk.gray('Your API key has been removed from this device.'));
39
+ console.log(chalk.gray('To use VibeDB again, run:'), chalk.cyan('vibedb login'), chalk.gray('or'), chalk.cyan('vibedb signup'));
40
+ console.log();
41
+ } catch (error) {
42
+ console.error(chalk.red.bold('\n✗ Logout failed:'), chalk.red(error.message));
43
+ process.exit(1);
44
+ }
45
+ }
46
+
47
+ module.exports = logoutCommand;
@@ -3,9 +3,79 @@ const chalk = require('chalk');
3
3
  const api = require('../api');
4
4
  const config = require('../config');
5
5
 
6
- async function signupCommand() {
7
- console.log(chalk.blue.bold('\n📝 VibeDB Signup\n'));
6
+ function sleep(ms) {
7
+ return new Promise(resolve => setTimeout(resolve, ms));
8
+ }
9
+
10
+ function formatUserCode(code) {
11
+ return `${code.slice(0, 4)}-${code.slice(4)}`;
12
+ }
13
+
14
+ async function signupWithGitHub() {
15
+ console.log(chalk.blue('Starting GitHub authentication...\n'));
16
+
17
+ try {
18
+ const data = await api.startDeviceAuth();
19
+ const { device_code, user_code, verification_uri, interval } = data;
20
+
21
+ console.log(chalk.bold('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
22
+ console.log(chalk.cyan.bold(` Open: ${verification_uri}`));
23
+ console.log(chalk.yellow.bold(` Enter code: ${formatUserCode(user_code)}`));
24
+ console.log(chalk.bold('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'));
25
+
26
+ try {
27
+ const open = (await import('open')).default;
28
+ await open(verification_uri);
29
+ console.log(chalk.gray('Opened browser automatically...\n'));
30
+ } catch (err) {
31
+ console.log(chalk.gray('Please open the URL manually\n'));
32
+ }
33
+
34
+ console.log(chalk.gray('Waiting for authorization...'));
35
+
36
+ const pollInterval = (interval || 5) * 1000;
37
+ let attempts = 0;
38
+ const maxAttempts = 180 / (interval || 5);
39
+
40
+ while (attempts < maxAttempts) {
41
+ await sleep(pollInterval);
42
+ attempts++;
43
+
44
+ try {
45
+ const pollData = await api.pollDeviceAuth(device_code);
46
+
47
+ if (pollData.status === 'complete') {
48
+ config.saveAuth(pollData.api_key, pollData.email);
49
+ console.log(chalk.green.bold('\n✓ Account created successfully!\n'));
50
+ console.log(chalk.white('Email:'), chalk.cyan(pollData.email));
51
+ console.log(chalk.gray(`API key saved to ${config.getConfigPath()}`));
52
+ console.log(chalk.white('\nNext steps:'));
53
+ console.log(chalk.gray(' 1. Run'), chalk.cyan('vibedb init'), chalk.gray('to download the prompt file'));
54
+ console.log(chalk.gray(' 2. Add VIBEDB.md to your project'));
55
+ console.log(chalk.gray(' 3. Tell your AI assistant to provision a database!'));
56
+ console.log();
57
+ return;
58
+ }
59
+
60
+ process.stdout.write(chalk.gray('.'));
61
+ } catch (err) {
62
+ if (err.response?.data?.error === 'authorization_expired') {
63
+ console.error(chalk.red.bold('\n\n✗ Authorization expired. Please try again.'));
64
+ process.exit(1);
65
+ }
66
+ }
67
+ }
68
+
69
+ console.error(chalk.red.bold('\n\n✗ Authorization timed out. Please try again.'));
70
+ process.exit(1);
71
+
72
+ } catch (error) {
73
+ console.error(chalk.red.bold('\n✗ GitHub signup failed:'), chalk.red(error.message));
74
+ process.exit(1);
75
+ }
76
+ }
8
77
 
78
+ async function signupWithEmail() {
9
79
  try {
10
80
  const answers = await inquirer.prompt([
11
81
  {
@@ -52,13 +122,11 @@ async function signupCommand() {
52
122
 
53
123
  const result = await api.signup(answers.email.trim().toLowerCase(), answers.password);
54
124
 
55
- // Save API key to config
56
125
  config.saveAuth(result.api_key, result.email);
57
126
 
58
127
  console.log(chalk.green.bold('\n✓ Account created successfully!\n'));
59
128
  console.log(chalk.white('Email:'), chalk.cyan(result.email));
60
- console.log(chalk.white('API Key:'), chalk.cyan(result.api_key));
61
- console.log(chalk.gray(`\nAPI key saved to ${config.getConfigPath()}`));
129
+ console.log(chalk.gray(`API key saved to ${config.getConfigPath()}`));
62
130
  console.log(chalk.white('\nNext steps:'));
63
131
  console.log(chalk.gray(' 1. Run'), chalk.cyan('vibedb init'), chalk.gray('to download the prompt file'));
64
132
  console.log(chalk.gray(' 2. Add VIBEDB.md to your project'));
@@ -70,4 +138,37 @@ async function signupCommand() {
70
138
  }
71
139
  }
72
140
 
141
+ async function signupCommand() {
142
+ console.log(chalk.blue.bold('\n📝 VibeDB Signup\n'));
143
+
144
+ try {
145
+ const { method } = await inquirer.prompt([
146
+ {
147
+ type: 'list',
148
+ name: 'method',
149
+ message: 'How would you like to sign up?',
150
+ choices: [
151
+ { name: '🐙 GitHub (Recommended)', value: 'github' },
152
+ { name: '📧 Email & Password', value: 'email' },
153
+ ],
154
+ },
155
+ ]);
156
+
157
+ console.log();
158
+
159
+ if (method === 'github') {
160
+ await signupWithGitHub();
161
+ } else {
162
+ await signupWithEmail();
163
+ }
164
+ } catch (error) {
165
+ if (error.isTtyError) {
166
+ console.error(chalk.red('Prompt couldn\'t be rendered in the current environment'));
167
+ } else {
168
+ console.error(chalk.red('An unexpected error occurred:', error.message));
169
+ }
170
+ process.exit(1);
171
+ }
172
+ }
173
+
73
174
  module.exports = signupCommand;
package/package.json.bak DELETED
@@ -1,35 +0,0 @@
1
- {
2
- "name": "@vibedb/cli",
3
- "version": "1.0.0",
4
- "description": "Command-line interface for VibeDB - instant database provisioning for AI-assisted development",
5
- "main": "bin/vibedb.js",
6
- "bin": {
7
- "vibedb": "./bin/vibedb.js"
8
- },
9
- "scripts": {
10
- "test": "node bin/vibedb.js --help"
11
- },
12
- "keywords": [
13
- "database",
14
- "postgres",
15
- "mysql",
16
- "redis",
17
- "provisioning",
18
- "cli",
19
- "vibedb",
20
- "ai",
21
- "development"
22
- ],
23
- "author": "VibeDB",
24
- "license": "MIT",
25
- "dependencies": {
26
- "axios": "^1.6.0",
27
- "chalk": "^4.1.2",
28
- "cli-table3": "^0.6.3",
29
- "commander": "^11.0.0",
30
- "inquirer": "^8.2.5"
31
- },
32
- "engines": {
33
- "node": ">=18.0.0"
34
- }
35
- }