@vibe-assurance/cli 1.7.0 → 1.7.2
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/package.json +1 -1
- package/src/commands/env.js +60 -39
- package/src/commands/login.js +5 -4
- package/src/config/credentials.js +19 -5
package/package.json
CHANGED
package/src/commands/env.js
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Environment management commands
|
|
3
3
|
* CR-2026-061: CLI Environment Switching with Production Default
|
|
4
|
+
*
|
|
5
|
+
* Security: dev/local environments are restricted to platform admins only.
|
|
6
|
+
* Regular users can only use production.
|
|
4
7
|
*/
|
|
5
8
|
|
|
6
9
|
const chalk = require('chalk');
|
|
7
10
|
const ora = require('ora');
|
|
8
|
-
const { getEnvironment, setEnvironment } = require('../config/credentials');
|
|
11
|
+
const { getEnvironment, setEnvironment, isAdmin, hasValidCredentials, deleteCredentials } = require('../config/credentials');
|
|
9
12
|
const {
|
|
10
13
|
ENVIRONMENTS,
|
|
11
|
-
DEFAULT_ENVIRONMENT,
|
|
12
14
|
getEnvironmentConfig,
|
|
13
15
|
isInternalOnly,
|
|
14
16
|
getEnvironmentNames
|
|
@@ -23,6 +25,7 @@ async function showCurrentEnv() {
|
|
|
23
25
|
try {
|
|
24
26
|
const currentEnv = await getEnvironment();
|
|
25
27
|
const config = getEnvironmentConfig(currentEnv);
|
|
28
|
+
const userIsAdmin = await isAdmin();
|
|
26
29
|
spinner.stop();
|
|
27
30
|
|
|
28
31
|
console.log('');
|
|
@@ -32,7 +35,7 @@ async function showCurrentEnv() {
|
|
|
32
35
|
console.log(` ${chalk.dim(config.url)}`);
|
|
33
36
|
|
|
34
37
|
if (config.internal) {
|
|
35
|
-
console.log(` ${chalk.yellow('⚠ Internal environment')}`);
|
|
38
|
+
console.log(` ${chalk.yellow('⚠ Internal environment (admin only)')}`);
|
|
36
39
|
}
|
|
37
40
|
|
|
38
41
|
// Check if VIBE_API_URL override is active
|
|
@@ -52,25 +55,26 @@ async function showCurrentEnv() {
|
|
|
52
55
|
|
|
53
56
|
/**
|
|
54
57
|
* CR-2026-061: List all available environments
|
|
58
|
+
* Only shows internal environments to admins
|
|
55
59
|
*/
|
|
56
|
-
async function listEnvironments(
|
|
60
|
+
async function listEnvironments() {
|
|
57
61
|
const currentEnv = await getEnvironment();
|
|
58
|
-
const
|
|
62
|
+
const userIsAdmin = await isAdmin();
|
|
59
63
|
|
|
60
64
|
console.log('');
|
|
61
65
|
console.log(chalk.cyan('Available Environments:'));
|
|
62
66
|
console.log('');
|
|
63
67
|
|
|
64
68
|
for (const [name, config] of Object.entries(ENVIRONMENTS)) {
|
|
65
|
-
//
|
|
66
|
-
if (config.internal && !
|
|
69
|
+
// Only show internal environments to admins
|
|
70
|
+
if (config.internal && !userIsAdmin) {
|
|
67
71
|
continue;
|
|
68
72
|
}
|
|
69
73
|
|
|
70
74
|
const isCurrent = name === currentEnv;
|
|
71
75
|
const prefix = isCurrent ? chalk.green('✓') : ' ';
|
|
72
76
|
const envName = isCurrent ? chalk.green.bold(name) : chalk.bold(name);
|
|
73
|
-
const label = config.internal ? chalk.yellow(' (
|
|
77
|
+
const label = config.internal ? chalk.yellow(' (admin only)') : '';
|
|
74
78
|
|
|
75
79
|
console.log(` ${prefix} ${envName}${label}`);
|
|
76
80
|
console.log(` ${chalk.dim(config.description)}`);
|
|
@@ -78,40 +82,61 @@ async function listEnvironments(options = {}) {
|
|
|
78
82
|
console.log('');
|
|
79
83
|
}
|
|
80
84
|
|
|
81
|
-
if (!
|
|
82
|
-
console.log(chalk.dim('
|
|
85
|
+
if (!userIsAdmin) {
|
|
86
|
+
console.log(chalk.dim(' Only production environment is available.'));
|
|
83
87
|
console.log('');
|
|
84
88
|
}
|
|
85
89
|
}
|
|
86
90
|
|
|
87
91
|
/**
|
|
88
92
|
* CR-2026-061: Switch to a specific environment
|
|
93
|
+
* Internal environments require admin role
|
|
94
|
+
* Switching environments clears credentials and requires re-login
|
|
89
95
|
* @param {string} envName - Environment name (prod, dev, local)
|
|
90
|
-
* @param {Object} options - Command options
|
|
91
96
|
*/
|
|
92
|
-
async function switchEnvironment(envName
|
|
97
|
+
async function switchEnvironment(envName) {
|
|
93
98
|
const config = getEnvironmentConfig(envName);
|
|
99
|
+
const currentEnv = await getEnvironment();
|
|
94
100
|
|
|
95
101
|
if (!config) {
|
|
96
102
|
console.error(chalk.red(`Unknown environment: ${envName}`));
|
|
97
|
-
console.log(chalk.dim('Available: '
|
|
103
|
+
console.log(chalk.dim('Available: prod'));
|
|
98
104
|
process.exit(1);
|
|
99
105
|
}
|
|
100
106
|
|
|
101
|
-
//
|
|
102
|
-
if (
|
|
103
|
-
console.
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
107
|
+
// Already on this environment?
|
|
108
|
+
if (envName === currentEnv) {
|
|
109
|
+
console.log(chalk.yellow(`Already using ${config.name} environment.`));
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Check if internal environment - require admin
|
|
114
|
+
if (isInternalOnly(envName)) {
|
|
115
|
+
// Must be logged in first
|
|
116
|
+
if (!await hasValidCredentials()) {
|
|
117
|
+
console.error(chalk.red('You must be logged in to switch environments.'));
|
|
118
|
+
console.log(chalk.dim('Run `vibe login` first.'));
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Must be admin
|
|
123
|
+
if (!await isAdmin()) {
|
|
124
|
+
console.error(chalk.red('Access denied.'));
|
|
125
|
+
console.log('');
|
|
126
|
+
console.log(chalk.yellow('Internal environments are restricted to platform administrators.'));
|
|
127
|
+
console.log(chalk.dim('Contact your administrator if you need access.'));
|
|
128
|
+
console.log('');
|
|
129
|
+
process.exit(1);
|
|
130
|
+
}
|
|
110
131
|
}
|
|
111
132
|
|
|
112
133
|
const spinner = ora(`Switching to ${config.name}...`).start();
|
|
113
134
|
|
|
114
135
|
try {
|
|
136
|
+
// Clear existing credentials - different environments have different auth
|
|
137
|
+
await deleteCredentials();
|
|
138
|
+
|
|
139
|
+
// Store only the new environment (no auth tokens)
|
|
115
140
|
await setEnvironment(envName);
|
|
116
141
|
spinner.succeed(`Switched to ${chalk.bold(config.name)}`);
|
|
117
142
|
|
|
@@ -121,11 +146,10 @@ async function switchEnvironment(envName, options = {}) {
|
|
|
121
146
|
if (config.internal) {
|
|
122
147
|
console.log('');
|
|
123
148
|
console.log(chalk.yellow('⚠ You are now using an internal environment.'));
|
|
124
|
-
console.log(chalk.dim(' Run `vibe env prod` to switch back to production.'));
|
|
125
149
|
}
|
|
126
150
|
|
|
127
151
|
console.log('');
|
|
128
|
-
console.log(chalk.
|
|
152
|
+
console.log(chalk.cyan('You have been logged out. Run `vibe login` to authenticate with this environment.'));
|
|
129
153
|
console.log('');
|
|
130
154
|
} catch (err) {
|
|
131
155
|
spinner.fail('Failed to switch environment');
|
|
@@ -141,7 +165,7 @@ async function switchEnvironment(envName, options = {}) {
|
|
|
141
165
|
function registerEnvCommands(program) {
|
|
142
166
|
const env = program
|
|
143
167
|
.command('env')
|
|
144
|
-
.description('Manage API environment
|
|
168
|
+
.description('Manage API environment');
|
|
145
169
|
|
|
146
170
|
// vibe env (no subcommand) - show current
|
|
147
171
|
env
|
|
@@ -161,9 +185,8 @@ function registerEnvCommands(program) {
|
|
|
161
185
|
env
|
|
162
186
|
.command('list')
|
|
163
187
|
.description('List available environments')
|
|
164
|
-
.
|
|
165
|
-
|
|
166
|
-
await listEnvironments(options);
|
|
188
|
+
.action(async () => {
|
|
189
|
+
await listEnvironments();
|
|
167
190
|
});
|
|
168
191
|
|
|
169
192
|
// vibe env prod
|
|
@@ -171,25 +194,23 @@ function registerEnvCommands(program) {
|
|
|
171
194
|
.command('prod')
|
|
172
195
|
.description('Switch to production environment')
|
|
173
196
|
.action(async () => {
|
|
174
|
-
await switchEnvironment('prod'
|
|
197
|
+
await switchEnvironment('prod');
|
|
175
198
|
});
|
|
176
199
|
|
|
177
|
-
// vibe env dev
|
|
200
|
+
// vibe env dev (admin only - not shown in description)
|
|
178
201
|
env
|
|
179
202
|
.command('dev')
|
|
180
|
-
.description('Switch to development environment
|
|
181
|
-
.
|
|
182
|
-
|
|
183
|
-
await switchEnvironment('dev', options);
|
|
203
|
+
.description('Switch to development environment')
|
|
204
|
+
.action(async () => {
|
|
205
|
+
await switchEnvironment('dev');
|
|
184
206
|
});
|
|
185
207
|
|
|
186
|
-
// vibe env local
|
|
208
|
+
// vibe env local (admin only - not shown in description)
|
|
187
209
|
env
|
|
188
210
|
.command('local')
|
|
189
|
-
.description('Switch to local
|
|
190
|
-
.
|
|
191
|
-
|
|
192
|
-
await switchEnvironment('local', options);
|
|
211
|
+
.description('Switch to local environment')
|
|
212
|
+
.action(async () => {
|
|
213
|
+
await switchEnvironment('local');
|
|
193
214
|
});
|
|
194
215
|
}
|
|
195
216
|
|
package/src/commands/login.js
CHANGED
|
@@ -193,13 +193,14 @@ async function selectProject() {
|
|
|
193
193
|
}
|
|
194
194
|
});
|
|
195
195
|
|
|
196
|
-
const { username, email, projects, defaultProjectId } = response.data;
|
|
196
|
+
const { username, email, role, projects, defaultProjectId } = response.data;
|
|
197
197
|
spinner.stop();
|
|
198
198
|
|
|
199
|
-
// Store and display user info
|
|
199
|
+
// Store and display user info (CR-2026-061: now includes role for admin checks)
|
|
200
200
|
if (username && email) {
|
|
201
|
-
await setUserInfo(username, email);
|
|
202
|
-
|
|
201
|
+
await setUserInfo(username, email, role || 'user');
|
|
202
|
+
const roleLabel = role === 'admin' ? chalk.magenta(' [admin]') : '';
|
|
203
|
+
console.log(chalk.cyan(`\nSigned in as ${chalk.bold(username)} (${email})${roleLabel}`));
|
|
203
204
|
}
|
|
204
205
|
|
|
205
206
|
if (!projects || projects.length === 0) {
|
|
@@ -159,14 +159,15 @@ async function setProjectId(projectId, projectName) {
|
|
|
159
159
|
|
|
160
160
|
/**
|
|
161
161
|
* Get stored user info
|
|
162
|
-
* @returns {Promise<{username: string, email: string}|null>} User info or null
|
|
162
|
+
* @returns {Promise<{username: string, email: string, role: string}|null>} User info or null
|
|
163
163
|
*/
|
|
164
164
|
async function getUserInfo() {
|
|
165
165
|
const creds = await getCredentials();
|
|
166
166
|
if (creds?.username && creds?.email) {
|
|
167
167
|
return {
|
|
168
168
|
username: creds.username,
|
|
169
|
-
email: creds.email
|
|
169
|
+
email: creds.email,
|
|
170
|
+
role: creds.role || 'user'
|
|
170
171
|
};
|
|
171
172
|
}
|
|
172
173
|
return null;
|
|
@@ -174,21 +175,33 @@ async function getUserInfo() {
|
|
|
174
175
|
|
|
175
176
|
/**
|
|
176
177
|
* Set user info (updates existing credentials)
|
|
178
|
+
* CR-2026-061: Now includes role for admin checks
|
|
177
179
|
* @param {string} username - Username to store
|
|
178
180
|
* @param {string} email - Email to store
|
|
181
|
+
* @param {string} role - User role ('user' or 'admin')
|
|
179
182
|
* @returns {Promise<void>}
|
|
180
183
|
*/
|
|
181
|
-
async function setUserInfo(username, email) {
|
|
184
|
+
async function setUserInfo(username, email, role = 'user') {
|
|
182
185
|
const creds = await getCredentials();
|
|
183
186
|
if (creds) {
|
|
184
187
|
await storeCredentials({
|
|
185
188
|
...creds,
|
|
186
189
|
username,
|
|
187
|
-
email
|
|
190
|
+
email,
|
|
191
|
+
role
|
|
188
192
|
});
|
|
189
193
|
}
|
|
190
194
|
}
|
|
191
195
|
|
|
196
|
+
/**
|
|
197
|
+
* CR-2026-061: Check if user is a platform admin
|
|
198
|
+
* @returns {Promise<boolean>} True if user is admin
|
|
199
|
+
*/
|
|
200
|
+
async function isAdmin() {
|
|
201
|
+
const creds = await getCredentials();
|
|
202
|
+
return creds?.role === 'admin';
|
|
203
|
+
}
|
|
204
|
+
|
|
192
205
|
/**
|
|
193
206
|
* CR-2026-061: Get stored environment
|
|
194
207
|
* @returns {Promise<string>} Environment name (prod, dev, local)
|
|
@@ -228,5 +241,6 @@ module.exports = {
|
|
|
228
241
|
getUserInfo,
|
|
229
242
|
setUserInfo,
|
|
230
243
|
getEnvironment, // CR-2026-061
|
|
231
|
-
setEnvironment
|
|
244
|
+
setEnvironment, // CR-2026-061
|
|
245
|
+
isAdmin // CR-2026-061
|
|
232
246
|
};
|