@vibe-assurance/cli 1.7.0 → 1.7.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/package.json +1 -1
- package/src/commands/env.js +47 -37
- 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 } = 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,35 +82,44 @@ 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
|
|
89
94
|
* @param {string} envName - Environment name (prod, dev, local)
|
|
90
|
-
* @param {Object} options - Command options
|
|
91
95
|
*/
|
|
92
|
-
async function switchEnvironment(envName
|
|
96
|
+
async function switchEnvironment(envName) {
|
|
93
97
|
const config = getEnvironmentConfig(envName);
|
|
94
98
|
|
|
95
99
|
if (!config) {
|
|
96
100
|
console.error(chalk.red(`Unknown environment: ${envName}`));
|
|
97
|
-
console.log(chalk.dim('Available: '
|
|
101
|
+
console.log(chalk.dim('Available: prod'));
|
|
98
102
|
process.exit(1);
|
|
99
103
|
}
|
|
100
104
|
|
|
101
|
-
// Check if internal
|
|
102
|
-
if (isInternalOnly(envName)
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
105
|
+
// Check if internal environment - require admin
|
|
106
|
+
if (isInternalOnly(envName)) {
|
|
107
|
+
// Must be logged in first
|
|
108
|
+
if (!await hasValidCredentials()) {
|
|
109
|
+
console.error(chalk.red('You must be logged in to switch environments.'));
|
|
110
|
+
console.log(chalk.dim('Run `vibe login` first.'));
|
|
111
|
+
process.exit(1);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Must be admin
|
|
115
|
+
if (!await isAdmin()) {
|
|
116
|
+
console.error(chalk.red('Access denied.'));
|
|
117
|
+
console.log('');
|
|
118
|
+
console.log(chalk.yellow('Internal environments are restricted to platform administrators.'));
|
|
119
|
+
console.log(chalk.dim('Contact your administrator if you need access.'));
|
|
120
|
+
console.log('');
|
|
121
|
+
process.exit(1);
|
|
122
|
+
}
|
|
110
123
|
}
|
|
111
124
|
|
|
112
125
|
const spinner = ora(`Switching to ${config.name}...`).start();
|
|
@@ -141,7 +154,7 @@ async function switchEnvironment(envName, options = {}) {
|
|
|
141
154
|
function registerEnvCommands(program) {
|
|
142
155
|
const env = program
|
|
143
156
|
.command('env')
|
|
144
|
-
.description('Manage API environment
|
|
157
|
+
.description('Manage API environment');
|
|
145
158
|
|
|
146
159
|
// vibe env (no subcommand) - show current
|
|
147
160
|
env
|
|
@@ -161,9 +174,8 @@ function registerEnvCommands(program) {
|
|
|
161
174
|
env
|
|
162
175
|
.command('list')
|
|
163
176
|
.description('List available environments')
|
|
164
|
-
.
|
|
165
|
-
|
|
166
|
-
await listEnvironments(options);
|
|
177
|
+
.action(async () => {
|
|
178
|
+
await listEnvironments();
|
|
167
179
|
});
|
|
168
180
|
|
|
169
181
|
// vibe env prod
|
|
@@ -171,25 +183,23 @@ function registerEnvCommands(program) {
|
|
|
171
183
|
.command('prod')
|
|
172
184
|
.description('Switch to production environment')
|
|
173
185
|
.action(async () => {
|
|
174
|
-
await switchEnvironment('prod'
|
|
186
|
+
await switchEnvironment('prod');
|
|
175
187
|
});
|
|
176
188
|
|
|
177
|
-
// vibe env dev
|
|
189
|
+
// vibe env dev (admin only - not shown in description)
|
|
178
190
|
env
|
|
179
191
|
.command('dev')
|
|
180
|
-
.description('Switch to development environment
|
|
181
|
-
.
|
|
182
|
-
|
|
183
|
-
await switchEnvironment('dev', options);
|
|
192
|
+
.description('Switch to development environment')
|
|
193
|
+
.action(async () => {
|
|
194
|
+
await switchEnvironment('dev');
|
|
184
195
|
});
|
|
185
196
|
|
|
186
|
-
// vibe env local
|
|
197
|
+
// vibe env local (admin only - not shown in description)
|
|
187
198
|
env
|
|
188
199
|
.command('local')
|
|
189
|
-
.description('Switch to local
|
|
190
|
-
.
|
|
191
|
-
|
|
192
|
-
await switchEnvironment('local', options);
|
|
200
|
+
.description('Switch to local environment')
|
|
201
|
+
.action(async () => {
|
|
202
|
+
await switchEnvironment('local');
|
|
193
203
|
});
|
|
194
204
|
}
|
|
195
205
|
|
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
|
};
|