@vibe-db/cli 1.2.1 → 1.3.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/README.md +84 -1
- package/bin/vibedb.js +45 -0
- package/package.json +1 -1
- package/src/api.js +54 -4
- package/src/commands/list.js +2 -1
- package/src/commands/login.js +29 -0
- package/src/commands/signup.js +33 -4
- package/src/commands/teams-current.js +42 -0
- package/src/commands/teams-list.js +84 -0
- package/src/commands/teams-switch.js +41 -0
- package/src/config.js +19 -0
package/README.md
CHANGED
|
@@ -99,6 +99,42 @@ List all your databases.
|
|
|
99
99
|
|
|
100
100
|
**Output:**
|
|
101
101
|
- Table showing all databases with ID, Name, Type, and Status
|
|
102
|
+
- Note: Shows databases for your current team only
|
|
103
|
+
|
|
104
|
+
### `vibedb teams list` (alias: `teams ls`)
|
|
105
|
+
List all teams you belong to.
|
|
106
|
+
|
|
107
|
+
**Requirements:**
|
|
108
|
+
- Must be logged in
|
|
109
|
+
|
|
110
|
+
**Output:**
|
|
111
|
+
- Table showing team ID, name, role, type, and current indicator
|
|
112
|
+
- Shows which team is currently selected
|
|
113
|
+
|
|
114
|
+
### `vibedb teams switch <team_id>`
|
|
115
|
+
Switch to a different team.
|
|
116
|
+
|
|
117
|
+
**Requirements:**
|
|
118
|
+
- Must be logged in
|
|
119
|
+
- Team ID from `vibedb teams list`
|
|
120
|
+
|
|
121
|
+
**Example:**
|
|
122
|
+
```bash
|
|
123
|
+
vibedb teams switch team_abc123
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
**Output:**
|
|
127
|
+
- Confirmation of team switch
|
|
128
|
+
- All subsequent commands use this team context
|
|
129
|
+
|
|
130
|
+
### `vibedb teams current`
|
|
131
|
+
Show currently selected team.
|
|
132
|
+
|
|
133
|
+
**Requirements:**
|
|
134
|
+
- Must be logged in
|
|
135
|
+
|
|
136
|
+
**Output:**
|
|
137
|
+
- Current team details (ID, name, role, type)
|
|
102
138
|
|
|
103
139
|
## Configuration
|
|
104
140
|
|
|
@@ -108,10 +144,13 @@ The CLI stores your credentials in `~/.vibedb`:
|
|
|
108
144
|
{
|
|
109
145
|
"api_key": "vdb_xxx...",
|
|
110
146
|
"email": "you@example.com",
|
|
111
|
-
"api_url": "https://api.vibedb.dev"
|
|
147
|
+
"api_url": "https://api.vibedb.dev",
|
|
148
|
+
"current_team_id": "team_abc123"
|
|
112
149
|
}
|
|
113
150
|
```
|
|
114
151
|
|
|
152
|
+
**Team Context:** The CLI remembers your current team selection. Use `vibedb teams switch` to change teams.
|
|
153
|
+
|
|
115
154
|
**Security Note:** Keep this file secure. Anyone with your API key can manage your databases.
|
|
116
155
|
|
|
117
156
|
## Examples
|
|
@@ -150,6 +189,50 @@ $ npx @vibedb/cli list
|
|
|
150
189
|
Total: 1 database
|
|
151
190
|
```
|
|
152
191
|
|
|
192
|
+
### Working with Teams
|
|
193
|
+
```bash
|
|
194
|
+
# List all your teams
|
|
195
|
+
$ vibedb teams list
|
|
196
|
+
|
|
197
|
+
┌──────────────────┬──────────────┬────────┬──────────┬─────────┐
|
|
198
|
+
│ ID │ Name │ Role │ Type │ Current │
|
|
199
|
+
├──────────────────┼──────────────┼────────┼──────────┼─────────┤
|
|
200
|
+
│ team_abc123 │ John's Team │ owner │ Personal │ ✓ │
|
|
201
|
+
│ team_xyz789 │ Acme Corp │ member │ Team │ │
|
|
202
|
+
└──────────────────┴──────────────┴────────┴──────────┴─────────┘
|
|
203
|
+
|
|
204
|
+
Total: 2 teams
|
|
205
|
+
|
|
206
|
+
# Switch to a different team
|
|
207
|
+
$ vibedb teams switch team_xyz789
|
|
208
|
+
|
|
209
|
+
✓ Successfully switched to team
|
|
210
|
+
Team: Acme Corp
|
|
211
|
+
Role: member
|
|
212
|
+
|
|
213
|
+
# List databases in the new team context
|
|
214
|
+
$ vibedb list
|
|
215
|
+
|
|
216
|
+
┌─────────────────┬────────────────┬──────────┬────────┐
|
|
217
|
+
│ ID │ Name │ Type │ Status │
|
|
218
|
+
├─────────────────┼────────────────┼──────────┼────────┤
|
|
219
|
+
│ db_def456 │ production-db │ postgres │ ready │
|
|
220
|
+
│ db_ghi789 │ staging-cache │ redis │ ready │
|
|
221
|
+
└─────────────────┴────────────────┴──────────┴────────┘
|
|
222
|
+
|
|
223
|
+
Total: 2 databases
|
|
224
|
+
|
|
225
|
+
# Check current team
|
|
226
|
+
$ vibedb teams current
|
|
227
|
+
|
|
228
|
+
📍 Current Team
|
|
229
|
+
|
|
230
|
+
ID: team_xyz789
|
|
231
|
+
Name: Acme Corp
|
|
232
|
+
Role: member
|
|
233
|
+
Type: Team
|
|
234
|
+
```
|
|
235
|
+
|
|
153
236
|
## Supported Databases
|
|
154
237
|
|
|
155
238
|
- **PostgreSQL** - General purpose relational database
|
package/bin/vibedb.js
CHANGED
|
@@ -12,6 +12,9 @@ const billingInfoCommand = require('../src/commands/billing-info');
|
|
|
12
12
|
const billingSubscribeCommand = require('../src/commands/billing-subscribe');
|
|
13
13
|
const billingCancelCommand = require('../src/commands/billing-cancel');
|
|
14
14
|
const billingInvoicesCommand = require('../src/commands/billing-invoices');
|
|
15
|
+
const teamsListCommand = require('../src/commands/teams-list');
|
|
16
|
+
const teamsSwitchCommand = require('../src/commands/teams-switch');
|
|
17
|
+
const teamsCurrentCommand = require('../src/commands/teams-current');
|
|
15
18
|
|
|
16
19
|
const program = new Command();
|
|
17
20
|
|
|
@@ -134,6 +137,48 @@ billing
|
|
|
134
137
|
}
|
|
135
138
|
});
|
|
136
139
|
|
|
140
|
+
// Teams commands
|
|
141
|
+
const teams = program
|
|
142
|
+
.command('teams')
|
|
143
|
+
.description('Manage teams and team membership');
|
|
144
|
+
|
|
145
|
+
teams
|
|
146
|
+
.command('list')
|
|
147
|
+
.alias('ls')
|
|
148
|
+
.description('List all teams you belong to')
|
|
149
|
+
.action(async () => {
|
|
150
|
+
try {
|
|
151
|
+
await teamsListCommand();
|
|
152
|
+
} catch (error) {
|
|
153
|
+
console.error(chalk.red('Unexpected error:'), error.message);
|
|
154
|
+
process.exit(1);
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
teams
|
|
159
|
+
.command('switch <team_id>')
|
|
160
|
+
.description('Switch to a different team')
|
|
161
|
+
.action(async (teamId) => {
|
|
162
|
+
try {
|
|
163
|
+
await teamsSwitchCommand(teamId);
|
|
164
|
+
} catch (error) {
|
|
165
|
+
console.error(chalk.red('Unexpected error:'), error.message);
|
|
166
|
+
process.exit(1);
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
teams
|
|
171
|
+
.command('current')
|
|
172
|
+
.description('Show current team')
|
|
173
|
+
.action(async () => {
|
|
174
|
+
try {
|
|
175
|
+
await teamsCurrentCommand();
|
|
176
|
+
} catch (error) {
|
|
177
|
+
console.error(chalk.red('Unexpected error:'), error.message);
|
|
178
|
+
process.exit(1);
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
|
|
137
182
|
// Global error handler
|
|
138
183
|
process.on('unhandledRejection', (error) => {
|
|
139
184
|
console.error(chalk.red.bold('\n✗ Unhandled error:'), error.message);
|
package/package.json
CHANGED
package/src/api.js
CHANGED
|
@@ -43,9 +43,13 @@ async function login(email, password) {
|
|
|
43
43
|
/**
|
|
44
44
|
* List all databases for authenticated user
|
|
45
45
|
*/
|
|
46
|
-
async function listDatabases(apiKey) {
|
|
46
|
+
async function listDatabases(apiKey, teamId = null) {
|
|
47
47
|
try {
|
|
48
|
-
const
|
|
48
|
+
const url = teamId
|
|
49
|
+
? `${API_BASE_URL}/v1/databases?team_id=${teamId}`
|
|
50
|
+
: `${API_BASE_URL}/v1/databases`;
|
|
51
|
+
|
|
52
|
+
const response = await axios.get(url, {
|
|
49
53
|
headers: {
|
|
50
54
|
Authorization: `Bearer ${apiKey}`,
|
|
51
55
|
},
|
|
@@ -78,9 +82,13 @@ async function getPromptFile() {
|
|
|
78
82
|
/**
|
|
79
83
|
* Get account information
|
|
80
84
|
*/
|
|
81
|
-
async function getAccount(apiKey) {
|
|
85
|
+
async function getAccount(apiKey, teamId = null) {
|
|
82
86
|
try {
|
|
83
|
-
const
|
|
87
|
+
const url = teamId
|
|
88
|
+
? `${API_BASE_URL}/v1/account?team_id=${teamId}`
|
|
89
|
+
: `${API_BASE_URL}/v1/account`;
|
|
90
|
+
|
|
91
|
+
const response = await axios.get(url, {
|
|
84
92
|
headers: {
|
|
85
93
|
Authorization: `Bearer ${apiKey}`,
|
|
86
94
|
},
|
|
@@ -215,6 +223,46 @@ async function pollDeviceAuth(deviceCode) {
|
|
|
215
223
|
}
|
|
216
224
|
}
|
|
217
225
|
|
|
226
|
+
/**
|
|
227
|
+
* List all teams for authenticated user
|
|
228
|
+
*/
|
|
229
|
+
async function listTeams(apiKey) {
|
|
230
|
+
try {
|
|
231
|
+
const response = await axios.get(`${API_BASE_URL}/v1/teams`, {
|
|
232
|
+
headers: {
|
|
233
|
+
Authorization: `Bearer ${apiKey}`,
|
|
234
|
+
},
|
|
235
|
+
});
|
|
236
|
+
return response.data.teams || [];
|
|
237
|
+
} catch (error) {
|
|
238
|
+
if (error.response) {
|
|
239
|
+
const { error: errorCode, message } = error.response.data;
|
|
240
|
+
throw new Error(message || errorCode || 'Failed to list teams');
|
|
241
|
+
}
|
|
242
|
+
throw new Error(`Network error: ${error.message}`);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Get team details
|
|
248
|
+
*/
|
|
249
|
+
async function getTeam(apiKey, teamId) {
|
|
250
|
+
try {
|
|
251
|
+
const response = await axios.get(`${API_BASE_URL}/v1/teams/${teamId}`, {
|
|
252
|
+
headers: {
|
|
253
|
+
Authorization: `Bearer ${apiKey}`,
|
|
254
|
+
},
|
|
255
|
+
});
|
|
256
|
+
return response.data;
|
|
257
|
+
} catch (error) {
|
|
258
|
+
if (error.response) {
|
|
259
|
+
const { error: errorCode, message } = error.response.data;
|
|
260
|
+
throw new Error(message || errorCode || 'Failed to get team');
|
|
261
|
+
}
|
|
262
|
+
throw new Error(`Network error: ${error.message}`);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
218
266
|
module.exports = {
|
|
219
267
|
signup,
|
|
220
268
|
login,
|
|
@@ -227,4 +275,6 @@ module.exports = {
|
|
|
227
275
|
getInvoices,
|
|
228
276
|
startDeviceAuth,
|
|
229
277
|
pollDeviceAuth,
|
|
278
|
+
listTeams,
|
|
279
|
+
getTeam,
|
|
230
280
|
};
|
package/src/commands/list.js
CHANGED
|
@@ -15,10 +15,11 @@ async function listCommand() {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
const apiKey = config.getAPIKey();
|
|
18
|
+
const currentTeamId = config.getCurrentTeamId();
|
|
18
19
|
|
|
19
20
|
console.log(chalk.gray('Fetching databases...'));
|
|
20
21
|
|
|
21
|
-
const databases = await api.listDatabases(apiKey);
|
|
22
|
+
const databases = await api.listDatabases(apiKey, currentTeamId);
|
|
22
23
|
|
|
23
24
|
if (databases.length === 0) {
|
|
24
25
|
console.log(chalk.yellow('\nNo databases found.'));
|
package/src/commands/login.js
CHANGED
|
@@ -54,6 +54,21 @@ async function loginWithGitHub() {
|
|
|
54
54
|
if (pollData.status === 'complete') {
|
|
55
55
|
// Save API key
|
|
56
56
|
config.saveAuth(pollData.api_key, pollData.email);
|
|
57
|
+
|
|
58
|
+
// Auto-select first team (usually personal team)
|
|
59
|
+
try {
|
|
60
|
+
const teams = await api.listTeams(pollData.api_key);
|
|
61
|
+
if (teams.length > 0) {
|
|
62
|
+
// Prefer personal team, otherwise first team
|
|
63
|
+
const personalTeam = teams.find(t => t.is_personal);
|
|
64
|
+
const defaultTeam = personalTeam || teams[0];
|
|
65
|
+
config.setCurrentTeamId(defaultTeam.id);
|
|
66
|
+
}
|
|
67
|
+
} catch (err) {
|
|
68
|
+
// Non-fatal, just log
|
|
69
|
+
console.log(chalk.gray('Note: Could not auto-select team'));
|
|
70
|
+
}
|
|
71
|
+
|
|
57
72
|
console.log(chalk.green.bold('\n✓ Login successful!\n'));
|
|
58
73
|
console.log(chalk.white('Email:'), chalk.cyan(pollData.email));
|
|
59
74
|
console.log(chalk.gray(`API key saved to ${config.getConfigPath()}`));
|
|
@@ -116,6 +131,20 @@ async function loginWithEmail() {
|
|
|
116
131
|
// Save API key to config
|
|
117
132
|
config.saveAuth(result.api_key, result.email);
|
|
118
133
|
|
|
134
|
+
// Auto-select first team (usually personal team)
|
|
135
|
+
try {
|
|
136
|
+
const teams = await api.listTeams(result.api_key);
|
|
137
|
+
if (teams.length > 0) {
|
|
138
|
+
// Prefer personal team, otherwise first team
|
|
139
|
+
const personalTeam = teams.find(t => t.is_personal);
|
|
140
|
+
const defaultTeam = personalTeam || teams[0];
|
|
141
|
+
config.setCurrentTeamId(defaultTeam.id);
|
|
142
|
+
}
|
|
143
|
+
} catch (err) {
|
|
144
|
+
// Non-fatal, just log
|
|
145
|
+
console.log(chalk.gray('Note: Could not auto-select team'));
|
|
146
|
+
}
|
|
147
|
+
|
|
119
148
|
console.log(chalk.green.bold('\n✓ Login successful!\n'));
|
|
120
149
|
console.log(chalk.white('Email:'), chalk.cyan(result.email));
|
|
121
150
|
console.log(chalk.gray(`API key saved to ${config.getConfigPath()}`));
|
package/src/commands/signup.js
CHANGED
|
@@ -18,15 +18,19 @@ async function signupWithGitHub() {
|
|
|
18
18
|
const data = await api.startDeviceAuth();
|
|
19
19
|
const { device_code, user_code, verification_uri, interval } = data;
|
|
20
20
|
|
|
21
|
+
// Build URL with pre-populated code for better UX
|
|
22
|
+
const urlWithCode = `${verification_uri}?code=${formatUserCode(user_code)}`;
|
|
23
|
+
|
|
21
24
|
console.log(chalk.bold('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
|
|
22
|
-
console.log(chalk.cyan.bold(` Open: ${
|
|
23
|
-
console.log(chalk.yellow.bold(`
|
|
25
|
+
console.log(chalk.cyan.bold(` Open: ${urlWithCode}`));
|
|
26
|
+
console.log(chalk.yellow.bold(` Your code: ${formatUserCode(user_code)}`));
|
|
24
27
|
console.log(chalk.bold('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'));
|
|
25
28
|
|
|
29
|
+
// Try to open browser automatically with pre-populated code
|
|
26
30
|
try {
|
|
27
31
|
const open = (await import('open')).default;
|
|
28
|
-
await open(
|
|
29
|
-
console.log(chalk.gray('Opened browser
|
|
32
|
+
await open(urlWithCode);
|
|
33
|
+
console.log(chalk.gray('Opened browser with code pre-filled...\n'));
|
|
30
34
|
} catch (err) {
|
|
31
35
|
console.log(chalk.gray('Please open the URL manually\n'));
|
|
32
36
|
}
|
|
@@ -46,6 +50,19 @@ async function signupWithGitHub() {
|
|
|
46
50
|
|
|
47
51
|
if (pollData.status === 'complete') {
|
|
48
52
|
config.saveAuth(pollData.api_key, pollData.email);
|
|
53
|
+
|
|
54
|
+
// Auto-select first team (usually personal team)
|
|
55
|
+
try {
|
|
56
|
+
const teams = await api.listTeams(pollData.api_key);
|
|
57
|
+
if (teams.length > 0) {
|
|
58
|
+
const personalTeam = teams.find(t => t.is_personal);
|
|
59
|
+
const defaultTeam = personalTeam || teams[0];
|
|
60
|
+
config.setCurrentTeamId(defaultTeam.id);
|
|
61
|
+
}
|
|
62
|
+
} catch (err) {
|
|
63
|
+
// Non-fatal
|
|
64
|
+
}
|
|
65
|
+
|
|
49
66
|
console.log(chalk.green.bold('\n✓ Account created successfully!\n'));
|
|
50
67
|
console.log(chalk.white('Email:'), chalk.cyan(pollData.email));
|
|
51
68
|
console.log(chalk.gray(`API key saved to ${config.getConfigPath()}`));
|
|
@@ -124,6 +141,18 @@ async function signupWithEmail() {
|
|
|
124
141
|
|
|
125
142
|
config.saveAuth(result.api_key, result.email);
|
|
126
143
|
|
|
144
|
+
// Auto-select first team (usually personal team)
|
|
145
|
+
try {
|
|
146
|
+
const teams = await api.listTeams(result.api_key);
|
|
147
|
+
if (teams.length > 0) {
|
|
148
|
+
const personalTeam = teams.find(t => t.is_personal);
|
|
149
|
+
const defaultTeam = personalTeam || teams[0];
|
|
150
|
+
config.setCurrentTeamId(defaultTeam.id);
|
|
151
|
+
}
|
|
152
|
+
} catch (err) {
|
|
153
|
+
// Non-fatal
|
|
154
|
+
}
|
|
155
|
+
|
|
127
156
|
console.log(chalk.green.bold('\n✓ Account created successfully!\n'));
|
|
128
157
|
console.log(chalk.white('Email:'), chalk.cyan(result.email));
|
|
129
158
|
console.log(chalk.gray(`API key saved to ${config.getConfigPath()}`));
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const api = require('../api');
|
|
3
|
+
const config = require('../config');
|
|
4
|
+
|
|
5
|
+
async function teamsCurrentCommand() {
|
|
6
|
+
try {
|
|
7
|
+
// Check if logged in
|
|
8
|
+
if (!config.isLoggedIn()) {
|
|
9
|
+
console.error(chalk.red.bold('✗ Not logged in'));
|
|
10
|
+
console.log(chalk.gray('\nPlease run'), chalk.cyan('vibedb login'), chalk.gray('or'), chalk.cyan('vibedb signup'), chalk.gray('first.'));
|
|
11
|
+
process.exit(1);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const apiKey = config.getAPIKey();
|
|
15
|
+
const currentTeamId = config.getCurrentTeamId();
|
|
16
|
+
|
|
17
|
+
if (!currentTeamId) {
|
|
18
|
+
console.log(chalk.yellow('\nNo team currently selected.'));
|
|
19
|
+
console.log(chalk.gray('\nSwitch to a team with:'), chalk.cyan('vibedb teams switch <team_id>'));
|
|
20
|
+
console.log(chalk.gray('List your teams with:'), chalk.cyan('vibedb teams list'));
|
|
21
|
+
console.log();
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
console.log(chalk.gray('Fetching current team...'));
|
|
26
|
+
|
|
27
|
+
const team = await api.getTeam(apiKey, currentTeamId);
|
|
28
|
+
|
|
29
|
+
console.log(chalk.blue.bold('\n📍 Current Team\n'));
|
|
30
|
+
console.log(chalk.gray('ID:'), chalk.cyan(team.id));
|
|
31
|
+
console.log(chalk.gray('Name:'), chalk.white(team.name));
|
|
32
|
+
console.log(chalk.gray('Role:'), chalk.blue(team.role));
|
|
33
|
+
console.log(chalk.gray('Type:'), team.is_personal ? chalk.gray('Personal') : chalk.white('Team'));
|
|
34
|
+
console.log();
|
|
35
|
+
} catch (error) {
|
|
36
|
+
console.error(chalk.red.bold('\n✗ Failed to get current team:'), chalk.red(error.message));
|
|
37
|
+
console.log(chalk.gray('\nThe team may have been deleted. Switch to a different team with:'), chalk.cyan('vibedb teams switch <team_id>'));
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
module.exports = teamsCurrentCommand;
|
|
@@ -0,0 +1,84 @@
|
|
|
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 teamsListCommand() {
|
|
7
|
+
console.log(chalk.blue.bold('\n👥 Your Teams\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
|
+
const currentTeamId = config.getCurrentTeamId();
|
|
19
|
+
|
|
20
|
+
console.log(chalk.gray('Fetching teams...'));
|
|
21
|
+
|
|
22
|
+
const teams = await api.listTeams(apiKey);
|
|
23
|
+
|
|
24
|
+
if (teams.length === 0) {
|
|
25
|
+
console.log(chalk.yellow('\nNo teams found.'));
|
|
26
|
+
console.log();
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Create table
|
|
31
|
+
const table = new Table({
|
|
32
|
+
head: [
|
|
33
|
+
chalk.white.bold('ID'),
|
|
34
|
+
chalk.white.bold('Name'),
|
|
35
|
+
chalk.white.bold('Role'),
|
|
36
|
+
chalk.white.bold('Type'),
|
|
37
|
+
chalk.white.bold('Current'),
|
|
38
|
+
],
|
|
39
|
+
colWidths: [30, 25, 12, 12, 10],
|
|
40
|
+
style: {
|
|
41
|
+
head: [],
|
|
42
|
+
border: ['gray'],
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
teams.forEach((team) => {
|
|
47
|
+
const isCurrent = team.id === currentTeamId;
|
|
48
|
+
const currentMark = isCurrent ? chalk.green('✓') : '';
|
|
49
|
+
const teamName = team.is_personal
|
|
50
|
+
? chalk.gray(`${team.name} (Personal)`)
|
|
51
|
+
: team.name;
|
|
52
|
+
|
|
53
|
+
table.push([
|
|
54
|
+
chalk.cyan(team.id),
|
|
55
|
+
teamName,
|
|
56
|
+
chalk.blue(team.role),
|
|
57
|
+
team.is_personal ? chalk.gray('Personal') : chalk.white('Team'),
|
|
58
|
+
currentMark,
|
|
59
|
+
]);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
console.log();
|
|
63
|
+
console.log(table.toString());
|
|
64
|
+
console.log();
|
|
65
|
+
|
|
66
|
+
if (currentTeamId) {
|
|
67
|
+
const currentTeam = teams.find(t => t.id === currentTeamId);
|
|
68
|
+
if (currentTeam) {
|
|
69
|
+
console.log(chalk.gray(`Current team: ${currentTeam.name}`));
|
|
70
|
+
}
|
|
71
|
+
} else {
|
|
72
|
+
console.log(chalk.yellow('No team selected. Use'), chalk.cyan('vibedb teams switch <team_id>'), chalk.yellow('to select a team.'));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
console.log();
|
|
76
|
+
console.log(chalk.gray(`Total: ${teams.length} team${teams.length === 1 ? '' : 's'}`));
|
|
77
|
+
console.log();
|
|
78
|
+
} catch (error) {
|
|
79
|
+
console.error(chalk.red.bold('\n✗ Failed to list teams:'), chalk.red(error.message));
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
module.exports = teamsListCommand;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const api = require('../api');
|
|
3
|
+
const config = require('../config');
|
|
4
|
+
|
|
5
|
+
async function teamsSwitchCommand(teamId) {
|
|
6
|
+
try {
|
|
7
|
+
// Check if logged in
|
|
8
|
+
if (!config.isLoggedIn()) {
|
|
9
|
+
console.error(chalk.red.bold('✗ Not logged in'));
|
|
10
|
+
console.log(chalk.gray('\nPlease run'), chalk.cyan('vibedb login'), chalk.gray('or'), chalk.cyan('vibedb signup'), chalk.gray('first.'));
|
|
11
|
+
process.exit(1);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (!teamId) {
|
|
15
|
+
console.error(chalk.red.bold('✗ Team ID required'));
|
|
16
|
+
console.log(chalk.gray('\nUsage:'), chalk.cyan('vibedb teams switch <team_id>'));
|
|
17
|
+
console.log(chalk.gray('\nList your teams with:'), chalk.cyan('vibedb teams list'));
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const apiKey = config.getAPIKey();
|
|
22
|
+
|
|
23
|
+
console.log(chalk.gray(`Switching to team ${teamId}...`));
|
|
24
|
+
|
|
25
|
+
// Verify team exists and user has access
|
|
26
|
+
const team = await api.getTeam(apiKey, teamId);
|
|
27
|
+
|
|
28
|
+
// Save as current team
|
|
29
|
+
config.setCurrentTeamId(teamId);
|
|
30
|
+
|
|
31
|
+
console.log(chalk.green.bold('\n✓ Successfully switched to team'));
|
|
32
|
+
console.log(chalk.gray('Team:'), chalk.white(team.name));
|
|
33
|
+
console.log(chalk.gray('Role:'), chalk.blue(team.role));
|
|
34
|
+
console.log();
|
|
35
|
+
} catch (error) {
|
|
36
|
+
console.error(chalk.red.bold('\n✗ Failed to switch team:'), chalk.red(error.message));
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
module.exports = teamsSwitchCommand;
|
package/src/config.js
CHANGED
|
@@ -68,6 +68,23 @@ function saveAuth(apiKey, email) {
|
|
|
68
68
|
writeConfig(config);
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
+
/**
|
|
72
|
+
* Get current team ID from config
|
|
73
|
+
*/
|
|
74
|
+
function getCurrentTeamId() {
|
|
75
|
+
const config = readConfig();
|
|
76
|
+
return config?.current_team_id || null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Set current team ID in config
|
|
81
|
+
*/
|
|
82
|
+
function setCurrentTeamId(teamId) {
|
|
83
|
+
const config = readConfig() || {};
|
|
84
|
+
config.current_team_id = teamId;
|
|
85
|
+
writeConfig(config);
|
|
86
|
+
}
|
|
87
|
+
|
|
71
88
|
module.exports = {
|
|
72
89
|
getConfigPath,
|
|
73
90
|
readConfig,
|
|
@@ -75,4 +92,6 @@ module.exports = {
|
|
|
75
92
|
isLoggedIn,
|
|
76
93
|
getAPIKey,
|
|
77
94
|
saveAuth,
|
|
95
|
+
getCurrentTeamId,
|
|
96
|
+
setCurrentTeamId,
|
|
78
97
|
};
|