@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibe-assurance/cli",
3
- "version": "1.7.0",
3
+ "version": "1.7.1",
4
4
  "description": "Vibe Assurance CLI - Connect AI coding agents to your governance platform via MCP",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -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(options = {}) {
60
+ async function listEnvironments() {
57
61
  const currentEnv = await getEnvironment();
58
- const showInternal = options.internal || false;
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
- // Skip internal environments unless --internal flag
66
- if (config.internal && !showInternal) {
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(' (internal)') : '';
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 (!showInternal) {
82
- console.log(chalk.dim(' Use --internal to see internal environments'));
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, options = {}) {
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: ' + getEnvironmentNames().join(', ')));
101
+ console.log(chalk.dim('Available: prod'));
98
102
  process.exit(1);
99
103
  }
100
104
 
101
- // Check if internal flag is required
102
- if (isInternalOnly(envName) && !options.internal) {
103
- console.error(chalk.red(`Environment '${envName}' requires --internal flag`));
104
- console.log('');
105
- console.log(chalk.yellow(' This environment is for internal development only.'));
106
- console.log(chalk.dim(' If you are a Vibe Assurance developer, run:'));
107
- console.log(chalk.dim(` vibe env ${envName} --internal`));
108
- console.log('');
109
- process.exit(1);
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 (prod, dev, local)');
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
- .option('--internal', 'Show internal environments (dev, local)')
165
- .action(async (options) => {
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 (internal only)')
181
- .option('--internal', 'Confirm internal environment access')
182
- .action(async (options) => {
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 development environment (internal only)')
190
- .option('--internal', 'Confirm internal environment access')
191
- .action(async (options) => {
192
- await switchEnvironment('local', options);
200
+ .description('Switch to local environment')
201
+ .action(async () => {
202
+ await switchEnvironment('local');
193
203
  });
194
204
  }
195
205
 
@@ -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
- console.log(chalk.cyan(`\nSigned in as ${chalk.bold(username)} (${email})`));
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 // CR-2026-061
244
+ setEnvironment, // CR-2026-061
245
+ isAdmin // CR-2026-061
232
246
  };