@vibe-db/cli 1.0.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.
- package/BILLING_CLI.md +198 -0
- package/PUBLISHING.md +558 -0
- package/README.md +206 -0
- package/bin/vibedb.js +139 -0
- package/package.json +35 -0
- package/package.json.bak +35 -0
- package/src/api.js +196 -0
- package/src/commands/billing-cancel.js +89 -0
- package/src/commands/billing-info.js +101 -0
- package/src/commands/billing-invoices.js +103 -0
- package/src/commands/billing-subscribe.js +103 -0
- package/src/commands/init.js +62 -0
- package/src/commands/list.js +73 -0
- package/src/commands/login.js +53 -0
- package/src/commands/signup.js +73 -0
- package/src/config.js +78 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const api = require('../api');
|
|
3
|
+
const config = require('../config');
|
|
4
|
+
|
|
5
|
+
async function billingInfoCommand() {
|
|
6
|
+
console.log(chalk.blue.bold('\nš³ Billing Information\n'));
|
|
7
|
+
|
|
8
|
+
try {
|
|
9
|
+
// Check if logged in
|
|
10
|
+
if (!config.isLoggedIn()) {
|
|
11
|
+
console.error(chalk.red.bold('ā Not logged in'));
|
|
12
|
+
console.log(chalk.gray('\nPlease run'), chalk.cyan('vibedb login'), chalk.gray('or'), chalk.cyan('vibedb signup'), chalk.gray('first.'));
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const apiKey = config.getAPIKey();
|
|
17
|
+
|
|
18
|
+
console.log(chalk.gray('Fetching billing info...'));
|
|
19
|
+
|
|
20
|
+
const billingInfo = await api.getBillingInfo(apiKey);
|
|
21
|
+
|
|
22
|
+
console.log();
|
|
23
|
+
|
|
24
|
+
// Customer ID
|
|
25
|
+
if (billingInfo.customer_id) {
|
|
26
|
+
console.log(chalk.white('Stripe Customer ID:'), chalk.cyan(billingInfo.customer_id));
|
|
27
|
+
} else {
|
|
28
|
+
console.log(chalk.yellow('ā No Stripe customer found'));
|
|
29
|
+
console.log(chalk.gray('Your account will be linked to Stripe on your first subscription.'));
|
|
30
|
+
console.log();
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
console.log();
|
|
35
|
+
|
|
36
|
+
// Subscription info
|
|
37
|
+
if (billingInfo.subscription) {
|
|
38
|
+
const sub = billingInfo.subscription;
|
|
39
|
+
console.log(chalk.white.bold('Subscription:'));
|
|
40
|
+
console.log(chalk.gray(' Status:'), getStatusBadge(sub.status));
|
|
41
|
+
console.log(chalk.gray(' Period:'), chalk.white(formatDate(sub.current_period_start)), chalk.gray('ā'), chalk.white(formatDate(sub.current_period_end)));
|
|
42
|
+
if (sub.cancel_at_period_end) {
|
|
43
|
+
console.log(chalk.yellow(' ā Subscription will cancel at period end'));
|
|
44
|
+
}
|
|
45
|
+
console.log();
|
|
46
|
+
} else {
|
|
47
|
+
console.log(chalk.gray('Subscription:'), chalk.yellow('None'));
|
|
48
|
+
console.log(chalk.gray('Run'), chalk.cyan('vibedb billing subscribe'), chalk.gray('to start a subscription.'));
|
|
49
|
+
console.log();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Payment method
|
|
53
|
+
if (billingInfo.payment_method) {
|
|
54
|
+
const pm = billingInfo.payment_method;
|
|
55
|
+
console.log(chalk.white.bold('Payment Method:'));
|
|
56
|
+
console.log(chalk.gray(' Card:'), chalk.white(`${pm.brand} ****${pm.last4}`));
|
|
57
|
+
console.log(chalk.gray(' Expires:'), chalk.white(`${pm.exp_month}/${pm.exp_year}`));
|
|
58
|
+
console.log();
|
|
59
|
+
} else {
|
|
60
|
+
console.log(chalk.gray('Payment Method:'), chalk.yellow('None'));
|
|
61
|
+
console.log(chalk.gray('Add a payment method in the Stripe dashboard.'));
|
|
62
|
+
console.log();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Upcoming invoice
|
|
66
|
+
if (billingInfo.upcoming_invoice) {
|
|
67
|
+
const invoice = billingInfo.upcoming_invoice;
|
|
68
|
+
console.log(chalk.white.bold('Upcoming Invoice:'));
|
|
69
|
+
console.log(chalk.gray(' Amount:'), chalk.white(`$${(invoice.amount_due / 100).toFixed(2)}`));
|
|
70
|
+
console.log(chalk.gray(' Period:'), chalk.white(formatDate(invoice.period_start)), chalk.gray('ā'), chalk.white(formatDate(invoice.period_end)));
|
|
71
|
+
console.log();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
} catch (error) {
|
|
75
|
+
if (error.message.includes('503') || error.message.includes('billing_unavailable')) {
|
|
76
|
+
console.error(chalk.yellow.bold('\nā Billing not configured'));
|
|
77
|
+
console.log(chalk.gray('Stripe billing is not set up on the server.'));
|
|
78
|
+
} else {
|
|
79
|
+
console.error(chalk.red.bold('\nā Failed to fetch billing info:'), chalk.red(error.message));
|
|
80
|
+
}
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function getStatusBadge(status) {
|
|
86
|
+
const badges = {
|
|
87
|
+
'active': chalk.green('ā Active'),
|
|
88
|
+
'trialing': chalk.blue('ā Trialing'),
|
|
89
|
+
'past_due': chalk.red('ā Past Due'),
|
|
90
|
+
'canceled': chalk.gray('ā Canceled'),
|
|
91
|
+
'unpaid': chalk.red('ā Unpaid'),
|
|
92
|
+
};
|
|
93
|
+
return badges[status] || chalk.gray(`ā ${status}`);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function formatDate(dateString) {
|
|
97
|
+
const date = new Date(dateString);
|
|
98
|
+
return date.toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' });
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
module.exports = billingInfoCommand;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const Table = require('cli-table3');
|
|
3
|
+
const api = require('../api');
|
|
4
|
+
const config = require('../config');
|
|
5
|
+
|
|
6
|
+
async function billingInvoicesCommand() {
|
|
7
|
+
console.log(chalk.blue.bold('\nš Your Invoices\n'));
|
|
8
|
+
|
|
9
|
+
try {
|
|
10
|
+
// Check if logged in
|
|
11
|
+
if (!config.isLoggedIn()) {
|
|
12
|
+
console.error(chalk.red.bold('ā Not logged in'));
|
|
13
|
+
console.log(chalk.gray('\nPlease run'), chalk.cyan('vibedb login'), chalk.gray('or'), chalk.cyan('vibedb signup'), chalk.gray('first.'));
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const apiKey = config.getAPIKey();
|
|
18
|
+
|
|
19
|
+
console.log(chalk.gray('Fetching invoices...'));
|
|
20
|
+
|
|
21
|
+
const response = await api.getInvoices(apiKey);
|
|
22
|
+
const invoices = response.invoices || [];
|
|
23
|
+
|
|
24
|
+
if (invoices.length === 0) {
|
|
25
|
+
console.log(chalk.yellow('\nNo invoices found.'));
|
|
26
|
+
console.log(chalk.gray('Invoices will appear here after you subscribe.'));
|
|
27
|
+
console.log();
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Create table
|
|
32
|
+
const table = new Table({
|
|
33
|
+
head: [
|
|
34
|
+
chalk.white.bold('Date'),
|
|
35
|
+
chalk.white.bold('Amount'),
|
|
36
|
+
chalk.white.bold('Status'),
|
|
37
|
+
chalk.white.bold('Period'),
|
|
38
|
+
],
|
|
39
|
+
colWidths: [15, 12, 12, 30],
|
|
40
|
+
style: {
|
|
41
|
+
head: [],
|
|
42
|
+
border: ['gray'],
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
invoices.forEach((inv) => {
|
|
47
|
+
const statusColor =
|
|
48
|
+
inv.status === 'paid'
|
|
49
|
+
? chalk.green
|
|
50
|
+
: inv.status === 'open'
|
|
51
|
+
? chalk.yellow
|
|
52
|
+
: chalk.red;
|
|
53
|
+
|
|
54
|
+
const date = new Date(inv.period_start).toLocaleDateString('en-US', {
|
|
55
|
+
month: 'short',
|
|
56
|
+
day: 'numeric',
|
|
57
|
+
year: 'numeric'
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const amount = `$${(inv.amount_paid / 100).toFixed(2)}`;
|
|
61
|
+
|
|
62
|
+
const periodStart = new Date(inv.period_start).toLocaleDateString('en-US', {
|
|
63
|
+
month: 'short',
|
|
64
|
+
day: 'numeric'
|
|
65
|
+
});
|
|
66
|
+
const periodEnd = new Date(inv.period_end).toLocaleDateString('en-US', {
|
|
67
|
+
month: 'short',
|
|
68
|
+
day: 'numeric'
|
|
69
|
+
});
|
|
70
|
+
const period = `${periodStart} - ${periodEnd}`;
|
|
71
|
+
|
|
72
|
+
table.push([
|
|
73
|
+
date,
|
|
74
|
+
chalk.white(amount),
|
|
75
|
+
statusColor(inv.status),
|
|
76
|
+
chalk.gray(period),
|
|
77
|
+
]);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
console.log();
|
|
81
|
+
console.log(table.toString());
|
|
82
|
+
console.log();
|
|
83
|
+
console.log(chalk.gray(`Total: ${invoices.length} invoice${invoices.length === 1 ? '' : 's'}`));
|
|
84
|
+
|
|
85
|
+
// Show download link for latest invoice
|
|
86
|
+
if (invoices.length > 0 && invoices[0].invoice_pdf) {
|
|
87
|
+
console.log();
|
|
88
|
+
console.log(chalk.gray('Latest invoice PDF:'), chalk.cyan(invoices[0].invoice_pdf));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
console.log();
|
|
92
|
+
} catch (error) {
|
|
93
|
+
if (error.message.includes('503') || error.message.includes('billing_unavailable')) {
|
|
94
|
+
console.error(chalk.yellow.bold('\nā Billing not configured'));
|
|
95
|
+
console.log(chalk.gray('Stripe billing is not set up on the server.'));
|
|
96
|
+
} else {
|
|
97
|
+
console.error(chalk.red.bold('\nā Failed to fetch invoices:'), chalk.red(error.message));
|
|
98
|
+
}
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
module.exports = billingInvoicesCommand;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const inquirer = require('inquirer');
|
|
3
|
+
const api = require('../api');
|
|
4
|
+
const config = require('../config');
|
|
5
|
+
|
|
6
|
+
async function billingSubscribeCommand() {
|
|
7
|
+
console.log(chalk.blue.bold('\nš³ Subscribe to VibeDB\n'));
|
|
8
|
+
|
|
9
|
+
try {
|
|
10
|
+
// Check if logged in
|
|
11
|
+
if (!config.isLoggedIn()) {
|
|
12
|
+
console.error(chalk.red.bold('ā Not logged in'));
|
|
13
|
+
console.log(chalk.gray('\nPlease run'), chalk.cyan('vibedb login'), chalk.gray('or'), chalk.cyan('vibedb signup'), chalk.gray('first.'));
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const apiKey = config.getAPIKey();
|
|
18
|
+
|
|
19
|
+
// Check current billing info
|
|
20
|
+
console.log(chalk.gray('Checking current subscription status...\n'));
|
|
21
|
+
|
|
22
|
+
let billingInfo;
|
|
23
|
+
try {
|
|
24
|
+
billingInfo = await api.getBillingInfo(apiKey);
|
|
25
|
+
} catch (error) {
|
|
26
|
+
if (error.message.includes('503') || error.message.includes('billing_unavailable')) {
|
|
27
|
+
console.error(chalk.red.bold('ā Billing not available'));
|
|
28
|
+
console.log(chalk.gray('Stripe billing is not configured on the server.'));
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
throw error;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Check if already subscribed
|
|
35
|
+
if (billingInfo.subscription && billingInfo.subscription.status === 'active') {
|
|
36
|
+
console.log(chalk.yellow('ā You already have an active subscription'));
|
|
37
|
+
console.log(chalk.gray('Status:'), chalk.green('Active'));
|
|
38
|
+
console.log(chalk.gray('Period ends:'), chalk.white(new Date(billingInfo.subscription.current_period_end).toLocaleDateString()));
|
|
39
|
+
console.log();
|
|
40
|
+
process.exit(0);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Check if customer exists
|
|
44
|
+
if (!billingInfo.customer_id) {
|
|
45
|
+
console.error(chalk.red.bold('ā No Stripe customer found'));
|
|
46
|
+
console.log(chalk.gray('Please contact support to set up your billing.'));
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Check if payment method exists
|
|
51
|
+
if (!billingInfo.payment_method) {
|
|
52
|
+
console.log(chalk.yellow.bold('ā No payment method on file\n'));
|
|
53
|
+
console.log(chalk.gray('To subscribe, you need to add a payment method first:'));
|
|
54
|
+
console.log();
|
|
55
|
+
console.log(chalk.cyan('1.'), chalk.white('Go to:'), chalk.underline('https://dashboard.stripe.com/test/customers/' + billingInfo.customer_id));
|
|
56
|
+
console.log(chalk.cyan('2.'), chalk.white('Click "Add payment method"'));
|
|
57
|
+
console.log(chalk.cyan('3.'), chalk.white('Add a test card (use 4242 4242 4242 4242)'));
|
|
58
|
+
console.log(chalk.cyan('4.'), chalk.white('Come back and run this command again'));
|
|
59
|
+
console.log();
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Confirm subscription
|
|
64
|
+
console.log(chalk.white.bold('Subscription Details:'));
|
|
65
|
+
console.log(chalk.gray('Payment method:'), chalk.white(`${billingInfo.payment_method.brand} ****${billingInfo.payment_method.last4}`));
|
|
66
|
+
console.log(chalk.gray('Customer ID:'), chalk.cyan(billingInfo.customer_id));
|
|
67
|
+
console.log();
|
|
68
|
+
|
|
69
|
+
const { confirm } = await inquirer.prompt([
|
|
70
|
+
{
|
|
71
|
+
type: 'confirm',
|
|
72
|
+
name: 'confirm',
|
|
73
|
+
message: 'Create subscription?',
|
|
74
|
+
default: false,
|
|
75
|
+
},
|
|
76
|
+
]);
|
|
77
|
+
|
|
78
|
+
if (!confirm) {
|
|
79
|
+
console.log(chalk.gray('\nSubscription cancelled.'));
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
console.log(chalk.gray('\nCreating subscription...'));
|
|
84
|
+
|
|
85
|
+
const result = await api.createSubscription(apiKey);
|
|
86
|
+
|
|
87
|
+
console.log();
|
|
88
|
+
console.log(chalk.green.bold('ā Subscription created successfully!'));
|
|
89
|
+
console.log();
|
|
90
|
+
console.log(chalk.gray('Subscription ID:'), chalk.cyan(result.subscription_id));
|
|
91
|
+
console.log(chalk.gray('Status:'), chalk.green(result.status));
|
|
92
|
+
console.log(chalk.gray('Period ends:'), chalk.white(new Date(result.current_period_end).toLocaleDateString()));
|
|
93
|
+
console.log();
|
|
94
|
+
console.log(chalk.gray('Run'), chalk.cyan('vibedb billing info'), chalk.gray('to see full details.'));
|
|
95
|
+
console.log();
|
|
96
|
+
|
|
97
|
+
} catch (error) {
|
|
98
|
+
console.error(chalk.red.bold('\nā Failed to create subscription:'), chalk.red(error.message));
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
module.exports = billingSubscribeCommand;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
const inquirer = require('inquirer');
|
|
5
|
+
const api = require('../api');
|
|
6
|
+
const config = require('../config');
|
|
7
|
+
|
|
8
|
+
async function initCommand() {
|
|
9
|
+
console.log(chalk.blue.bold('\nš Initialize VibeDB in your project\n'));
|
|
10
|
+
|
|
11
|
+
try {
|
|
12
|
+
// Check if logged in
|
|
13
|
+
if (!config.isLoggedIn()) {
|
|
14
|
+
console.error(chalk.red.bold('ā Not logged in'));
|
|
15
|
+
console.log(chalk.gray('\nPlease run'), chalk.cyan('vibedb login'), chalk.gray('or'), chalk.cyan('vibedb signup'), chalk.gray('first.'));
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const promptFilePath = path.join(process.cwd(), 'VIBEDB.md');
|
|
20
|
+
|
|
21
|
+
// Check if file already exists
|
|
22
|
+
if (fs.existsSync(promptFilePath)) {
|
|
23
|
+
const { overwrite } = await inquirer.prompt([
|
|
24
|
+
{
|
|
25
|
+
type: 'confirm',
|
|
26
|
+
name: 'overwrite',
|
|
27
|
+
message: 'VIBEDB.md already exists. Overwrite?',
|
|
28
|
+
default: false,
|
|
29
|
+
},
|
|
30
|
+
]);
|
|
31
|
+
|
|
32
|
+
if (!overwrite) {
|
|
33
|
+
console.log(chalk.yellow('\nOperation cancelled.'));
|
|
34
|
+
process.exit(0);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
console.log(chalk.gray('Downloading prompt file...'));
|
|
39
|
+
|
|
40
|
+
const promptContent = await api.getPromptFile();
|
|
41
|
+
|
|
42
|
+
// Write to file
|
|
43
|
+
fs.writeFileSync(promptFilePath, promptContent, 'utf8');
|
|
44
|
+
|
|
45
|
+
const userConfig = config.readConfig();
|
|
46
|
+
|
|
47
|
+
console.log(chalk.green.bold('\nā VIBEDB.md downloaded successfully!\n'));
|
|
48
|
+
console.log(chalk.white('Location:'), chalk.cyan(promptFilePath));
|
|
49
|
+
console.log(chalk.white('API Key:'), chalk.cyan(userConfig.api_key));
|
|
50
|
+
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!'));
|
|
55
|
+
console.log();
|
|
56
|
+
} catch (error) {
|
|
57
|
+
console.error(chalk.red.bold('\nā Init failed:'), chalk.red(error.message));
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
module.exports = initCommand;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const Table = require('cli-table3');
|
|
3
|
+
const api = require('../api');
|
|
4
|
+
const config = require('../config');
|
|
5
|
+
|
|
6
|
+
async function listCommand() {
|
|
7
|
+
console.log(chalk.blue.bold('\nš Your Databases\n'));
|
|
8
|
+
|
|
9
|
+
try {
|
|
10
|
+
// Check if logged in
|
|
11
|
+
if (!config.isLoggedIn()) {
|
|
12
|
+
console.error(chalk.red.bold('ā Not logged in'));
|
|
13
|
+
console.log(chalk.gray('\nPlease run'), chalk.cyan('vibedb login'), chalk.gray('or'), chalk.cyan('vibedb signup'), chalk.gray('first.'));
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const apiKey = config.getAPIKey();
|
|
18
|
+
|
|
19
|
+
console.log(chalk.gray('Fetching databases...'));
|
|
20
|
+
|
|
21
|
+
const databases = await api.listDatabases(apiKey);
|
|
22
|
+
|
|
23
|
+
if (databases.length === 0) {
|
|
24
|
+
console.log(chalk.yellow('\nNo databases found.'));
|
|
25
|
+
console.log(chalk.gray('\nCreate your first database by telling your AI assistant:'));
|
|
26
|
+
console.log(chalk.cyan(' "I need a Postgres database for my app"'));
|
|
27
|
+
console.log();
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Create table
|
|
32
|
+
const table = new Table({
|
|
33
|
+
head: [
|
|
34
|
+
chalk.white.bold('ID'),
|
|
35
|
+
chalk.white.bold('Name'),
|
|
36
|
+
chalk.white.bold('Type'),
|
|
37
|
+
chalk.white.bold('Status'),
|
|
38
|
+
],
|
|
39
|
+
colWidths: [20, 25, 12, 12],
|
|
40
|
+
style: {
|
|
41
|
+
head: [],
|
|
42
|
+
border: ['gray'],
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
databases.forEach((db) => {
|
|
47
|
+
const statusColor =
|
|
48
|
+
db.status === 'ready'
|
|
49
|
+
? chalk.green
|
|
50
|
+
: db.status === 'provisioning'
|
|
51
|
+
? chalk.yellow
|
|
52
|
+
: chalk.gray;
|
|
53
|
+
|
|
54
|
+
table.push([
|
|
55
|
+
chalk.cyan(db.id),
|
|
56
|
+
db.name,
|
|
57
|
+
chalk.blue(db.type),
|
|
58
|
+
statusColor(db.status),
|
|
59
|
+
]);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
console.log();
|
|
63
|
+
console.log(table.toString());
|
|
64
|
+
console.log();
|
|
65
|
+
console.log(chalk.gray(`Total: ${databases.length} database${databases.length === 1 ? '' : 's'}`));
|
|
66
|
+
console.log();
|
|
67
|
+
} catch (error) {
|
|
68
|
+
console.error(chalk.red.bold('\nā Failed to list databases:'), chalk.red(error.message));
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
module.exports = listCommand;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
const inquirer = require('inquirer');
|
|
2
|
+
const chalk = require('chalk');
|
|
3
|
+
const api = require('../api');
|
|
4
|
+
const config = require('../config');
|
|
5
|
+
|
|
6
|
+
async function loginCommand() {
|
|
7
|
+
console.log(chalk.blue.bold('\nš VibeDB Login\n'));
|
|
8
|
+
|
|
9
|
+
try {
|
|
10
|
+
const answers = await inquirer.prompt([
|
|
11
|
+
{
|
|
12
|
+
type: 'input',
|
|
13
|
+
name: 'email',
|
|
14
|
+
message: 'Email:',
|
|
15
|
+
validate: (input) => {
|
|
16
|
+
if (!input || input.length < 3) {
|
|
17
|
+
return 'Email is required';
|
|
18
|
+
}
|
|
19
|
+
return true;
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
type: 'password',
|
|
24
|
+
name: 'password',
|
|
25
|
+
message: 'Password:',
|
|
26
|
+
mask: '*',
|
|
27
|
+
validate: (input) => {
|
|
28
|
+
if (!input) {
|
|
29
|
+
return 'Password is required';
|
|
30
|
+
}
|
|
31
|
+
return true;
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
]);
|
|
35
|
+
|
|
36
|
+
console.log(chalk.gray('\nAuthenticating...'));
|
|
37
|
+
|
|
38
|
+
const result = await api.login(answers.email.trim().toLowerCase(), answers.password);
|
|
39
|
+
|
|
40
|
+
// Save API key to config
|
|
41
|
+
config.saveAuth(result.api_key, result.email);
|
|
42
|
+
|
|
43
|
+
console.log(chalk.green.bold('\nā Login successful!\n'));
|
|
44
|
+
console.log(chalk.white('Email:'), chalk.cyan(result.email));
|
|
45
|
+
console.log(chalk.gray(`API key saved to ${config.getConfigPath()}`));
|
|
46
|
+
console.log();
|
|
47
|
+
} catch (error) {
|
|
48
|
+
console.error(chalk.red.bold('\nā Login failed:'), chalk.red(error.message));
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
module.exports = loginCommand;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
const inquirer = require('inquirer');
|
|
2
|
+
const chalk = require('chalk');
|
|
3
|
+
const api = require('../api');
|
|
4
|
+
const config = require('../config');
|
|
5
|
+
|
|
6
|
+
async function signupCommand() {
|
|
7
|
+
console.log(chalk.blue.bold('\nš VibeDB Signup\n'));
|
|
8
|
+
|
|
9
|
+
try {
|
|
10
|
+
const answers = await inquirer.prompt([
|
|
11
|
+
{
|
|
12
|
+
type: 'input',
|
|
13
|
+
name: 'email',
|
|
14
|
+
message: 'Email:',
|
|
15
|
+
validate: (input) => {
|
|
16
|
+
if (!input || input.length < 3) {
|
|
17
|
+
return 'Email is required';
|
|
18
|
+
}
|
|
19
|
+
if (!input.includes('@')) {
|
|
20
|
+
return 'Please enter a valid email address';
|
|
21
|
+
}
|
|
22
|
+
return true;
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
type: 'password',
|
|
27
|
+
name: 'password',
|
|
28
|
+
message: 'Password:',
|
|
29
|
+
mask: '*',
|
|
30
|
+
validate: (input) => {
|
|
31
|
+
if (!input || input.length < 8) {
|
|
32
|
+
return 'Password must be at least 8 characters long';
|
|
33
|
+
}
|
|
34
|
+
return true;
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
type: 'password',
|
|
39
|
+
name: 'confirmPassword',
|
|
40
|
+
message: 'Confirm password:',
|
|
41
|
+
mask: '*',
|
|
42
|
+
validate: (input, answers) => {
|
|
43
|
+
if (input !== answers.password) {
|
|
44
|
+
return 'Passwords do not match';
|
|
45
|
+
}
|
|
46
|
+
return true;
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
]);
|
|
50
|
+
|
|
51
|
+
console.log(chalk.gray('\nCreating account...'));
|
|
52
|
+
|
|
53
|
+
const result = await api.signup(answers.email.trim().toLowerCase(), answers.password);
|
|
54
|
+
|
|
55
|
+
// Save API key to config
|
|
56
|
+
config.saveAuth(result.api_key, result.email);
|
|
57
|
+
|
|
58
|
+
console.log(chalk.green.bold('\nā Account created successfully!\n'));
|
|
59
|
+
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()}`));
|
|
62
|
+
console.log(chalk.white('\nNext steps:'));
|
|
63
|
+
console.log(chalk.gray(' 1. Run'), chalk.cyan('vibedb init'), chalk.gray('to download the prompt file'));
|
|
64
|
+
console.log(chalk.gray(' 2. Add VIBEDB.md to your project'));
|
|
65
|
+
console.log(chalk.gray(' 3. Tell your AI assistant to provision a database!'));
|
|
66
|
+
console.log();
|
|
67
|
+
} catch (error) {
|
|
68
|
+
console.error(chalk.red.bold('\nā Signup failed:'), chalk.red(error.message));
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
module.exports = signupCommand;
|
package/src/config.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const os = require('os');
|
|
4
|
+
|
|
5
|
+
const CONFIG_FILE = path.join(os.homedir(), '.vibedb');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Get the config file path
|
|
9
|
+
*/
|
|
10
|
+
function getConfigPath() {
|
|
11
|
+
return CONFIG_FILE;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Read configuration from ~/.vibedb
|
|
16
|
+
*/
|
|
17
|
+
function readConfig() {
|
|
18
|
+
try {
|
|
19
|
+
if (!fs.existsSync(CONFIG_FILE)) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
const content = fs.readFileSync(CONFIG_FILE, 'utf8');
|
|
23
|
+
return JSON.parse(content);
|
|
24
|
+
} catch (error) {
|
|
25
|
+
throw new Error(`Failed to read config file: ${error.message}`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Write configuration to ~/.vibedb
|
|
31
|
+
*/
|
|
32
|
+
function writeConfig(config) {
|
|
33
|
+
try {
|
|
34
|
+
const content = JSON.stringify(config, null, 2);
|
|
35
|
+
fs.writeFileSync(CONFIG_FILE, content, 'utf8');
|
|
36
|
+
} catch (error) {
|
|
37
|
+
throw new Error(`Failed to write config file: ${error.message}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Check if user is logged in (has API key)
|
|
43
|
+
*/
|
|
44
|
+
function isLoggedIn() {
|
|
45
|
+
const config = readConfig();
|
|
46
|
+
return config && config.api_key;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Get API key from config
|
|
51
|
+
*/
|
|
52
|
+
function getAPIKey() {
|
|
53
|
+
const config = readConfig();
|
|
54
|
+
if (!config || !config.api_key) {
|
|
55
|
+
throw new Error('Not logged in. Please run "vibedb login" or "vibedb signup" first.');
|
|
56
|
+
}
|
|
57
|
+
return config.api_key;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Save API key and email to config
|
|
62
|
+
*/
|
|
63
|
+
function saveAuth(apiKey, email) {
|
|
64
|
+
const config = readConfig() || {};
|
|
65
|
+
config.api_key = apiKey;
|
|
66
|
+
config.email = email;
|
|
67
|
+
config.api_url = 'https://api.vibedb.dev';
|
|
68
|
+
writeConfig(config);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
module.exports = {
|
|
72
|
+
getConfigPath,
|
|
73
|
+
readConfig,
|
|
74
|
+
writeConfig,
|
|
75
|
+
isLoggedIn,
|
|
76
|
+
getAPIKey,
|
|
77
|
+
saveAuth,
|
|
78
|
+
};
|