@codebakers/cli 1.6.0 → 2.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.
@@ -0,0 +1,216 @@
1
+ import chalk from 'chalk';
2
+ import { createInterface } from 'readline';
3
+ import {
4
+ getApiKey,
5
+ getApiUrl,
6
+ setApiUrl,
7
+ clearApiKey,
8
+ getConfigPath,
9
+ getConfigStore,
10
+ getConfiguredServiceKeys,
11
+ clearAllServiceKeys,
12
+ getLastKeySync,
13
+ SERVICE_KEY_LABELS,
14
+ SERVICE_KEY_CATEGORIES,
15
+ type ServiceName,
16
+ } from '../config.js';
17
+
18
+ function prompt(question: string): Promise<string> {
19
+ const rl = createInterface({
20
+ input: process.stdin,
21
+ output: process.stdout,
22
+ });
23
+
24
+ return new Promise((resolve) => {
25
+ rl.question(question, (answer) => {
26
+ rl.close();
27
+ resolve(answer.trim());
28
+ });
29
+ });
30
+ }
31
+
32
+ /**
33
+ * View or modify CLI configuration
34
+ */
35
+ export async function config(action?: string): Promise<void> {
36
+ console.log(chalk.blue('\n CodeBakers Configuration\n'));
37
+
38
+ switch (action) {
39
+ case 'show':
40
+ case undefined:
41
+ showConfig();
42
+ break;
43
+ case 'path':
44
+ showConfigPath();
45
+ break;
46
+ case 'reset':
47
+ await resetConfig();
48
+ break;
49
+ case 'keys':
50
+ showServiceKeys();
51
+ break;
52
+ case 'clear-keys':
53
+ await clearKeys();
54
+ break;
55
+ case 'set-url':
56
+ await setUrl();
57
+ break;
58
+ default:
59
+ showHelp();
60
+ }
61
+ }
62
+
63
+ function showConfig(): void {
64
+ const apiKey = getApiKey();
65
+ const apiUrl = getApiUrl();
66
+ const configPath = getConfigPath();
67
+ const configuredKeys = getConfiguredServiceKeys();
68
+ const lastSync = getLastKeySync();
69
+
70
+ console.log(chalk.white(' Current Configuration:\n'));
71
+
72
+ // API Key
73
+ if (apiKey) {
74
+ const masked = `${apiKey.slice(0, 7)}...${apiKey.slice(-4)}`;
75
+ console.log(chalk.gray(' API Key: ') + chalk.green(masked));
76
+ } else {
77
+ console.log(chalk.gray(' API Key: ') + chalk.yellow('Not configured'));
78
+ }
79
+
80
+ // API URL
81
+ console.log(chalk.gray(' API URL: ') + chalk.cyan(apiUrl));
82
+
83
+ // Service Keys
84
+ console.log(chalk.gray(' Service Keys: ') + chalk.cyan(`${configuredKeys.length} configured`));
85
+
86
+ // Last Sync
87
+ if (lastSync) {
88
+ const syncDate = lastSync.toLocaleDateString();
89
+ const syncTime = lastSync.toLocaleTimeString();
90
+ console.log(chalk.gray(' Last Sync: ') + chalk.cyan(`${syncDate} ${syncTime}`));
91
+ } else {
92
+ console.log(chalk.gray(' Last Sync: ') + chalk.yellow('Never'));
93
+ }
94
+
95
+ // Config Path
96
+ console.log(chalk.gray(' Config File: ') + chalk.dim(configPath));
97
+
98
+ console.log('');
99
+
100
+ // Show available actions
101
+ console.log(chalk.white(' Available Actions:\n'));
102
+ console.log(chalk.gray(' codebakers config path ') + chalk.dim('Show config file location'));
103
+ console.log(chalk.gray(' codebakers config keys ') + chalk.dim('Show configured service keys'));
104
+ console.log(chalk.gray(' codebakers config clear-keys ') + chalk.dim('Clear all service keys'));
105
+ console.log(chalk.gray(' codebakers config set-url ') + chalk.dim('Change API URL (advanced)'));
106
+ console.log(chalk.gray(' codebakers config reset ') + chalk.dim('Reset all configuration'));
107
+ console.log('');
108
+ }
109
+
110
+ function showConfigPath(): void {
111
+ const configPath = getConfigPath();
112
+ console.log(chalk.white(' Config file location:\n'));
113
+ console.log(chalk.cyan(` ${configPath}\n`));
114
+ }
115
+
116
+ function showServiceKeys(): void {
117
+ const configuredKeys = getConfiguredServiceKeys();
118
+
119
+ if (configuredKeys.length === 0) {
120
+ console.log(chalk.yellow(' No service keys configured.\n'));
121
+ console.log(chalk.gray(' Add keys in your CodeBakers dashboard, then run `codebakers setup` to sync.\n'));
122
+ return;
123
+ }
124
+
125
+ console.log(chalk.white(` Configured Service Keys (${configuredKeys.length}):\n`));
126
+
127
+ // Group by category
128
+ for (const [category, keyNames] of Object.entries(SERVICE_KEY_CATEGORIES)) {
129
+ const categoryKeys = keyNames.filter(k => configuredKeys.includes(k));
130
+
131
+ if (categoryKeys.length > 0) {
132
+ console.log(chalk.gray(` ${category.charAt(0).toUpperCase() + category.slice(1)}:`));
133
+ for (const keyName of categoryKeys) {
134
+ console.log(chalk.green(` ✓ ${SERVICE_KEY_LABELS[keyName as ServiceName]}`));
135
+ }
136
+ }
137
+ }
138
+
139
+ console.log('');
140
+
141
+ const lastSync = getLastKeySync();
142
+ if (lastSync) {
143
+ console.log(chalk.gray(` Last synced: ${lastSync.toLocaleString()}\n`));
144
+ }
145
+ }
146
+
147
+ async function clearKeys(): Promise<void> {
148
+ console.log(chalk.yellow(' This will clear all locally stored service keys.\n'));
149
+ const confirm = await prompt(chalk.gray(' Are you sure? (y/N): '));
150
+
151
+ if (confirm.toLowerCase() !== 'y') {
152
+ console.log(chalk.gray('\n Cancelled.\n'));
153
+ return;
154
+ }
155
+
156
+ clearAllServiceKeys();
157
+ console.log(chalk.green('\n ✓ All service keys cleared.\n'));
158
+ console.log(chalk.gray(' Run `codebakers setup` to sync keys from your account.\n'));
159
+ }
160
+
161
+ async function setUrl(): Promise<void> {
162
+ console.log(chalk.yellow(' ⚠️ This is an advanced setting. Only change if instructed.\n'));
163
+
164
+ const currentUrl = getApiUrl();
165
+ console.log(chalk.gray(` Current URL: ${currentUrl}\n`));
166
+
167
+ const newUrl = await prompt(chalk.cyan(' New API URL (or press Enter to cancel): '));
168
+
169
+ if (!newUrl) {
170
+ console.log(chalk.gray('\n Cancelled.\n'));
171
+ return;
172
+ }
173
+
174
+ // Basic validation
175
+ try {
176
+ new URL(newUrl);
177
+ } catch {
178
+ console.log(chalk.red('\n Invalid URL format.\n'));
179
+ return;
180
+ }
181
+
182
+ setApiUrl(newUrl);
183
+ console.log(chalk.green(`\n ✓ API URL updated to: ${newUrl}\n`));
184
+ }
185
+
186
+ async function resetConfig(): Promise<void> {
187
+ console.log(chalk.yellow(' ⚠️ This will clear ALL configuration:\n'));
188
+ console.log(chalk.gray(' • API key'));
189
+ console.log(chalk.gray(' • Service keys'));
190
+ console.log(chalk.gray(' • All settings\n'));
191
+
192
+ const confirm = await prompt(chalk.red(' Type "RESET" to confirm: '));
193
+
194
+ if (confirm !== 'RESET') {
195
+ console.log(chalk.gray('\n Cancelled.\n'));
196
+ return;
197
+ }
198
+
199
+ clearApiKey();
200
+ clearAllServiceKeys();
201
+
202
+ console.log(chalk.green('\n ✓ Configuration reset.\n'));
203
+ console.log(chalk.gray(' Run `codebakers setup` to reconfigure.\n'));
204
+ }
205
+
206
+ function showHelp(): void {
207
+ console.log(chalk.white(' Usage: codebakers config [action]\n'));
208
+ console.log(chalk.white(' Actions:'));
209
+ console.log(chalk.gray(' (none) Show current configuration'));
210
+ console.log(chalk.gray(' path Show config file location'));
211
+ console.log(chalk.gray(' keys Show configured service keys'));
212
+ console.log(chalk.gray(' clear-keys Clear all service keys'));
213
+ console.log(chalk.gray(' set-url Change API URL (advanced)'));
214
+ console.log(chalk.gray(' reset Reset all configuration'));
215
+ console.log('');
216
+ }
@@ -3,6 +3,8 @@ import { existsSync, readdirSync, readFileSync } from 'fs';
3
3
  import { join } from 'path';
4
4
  import { homedir } from 'os';
5
5
  import { isHookInstalled } from './install-hook.js';
6
+ import { getApiKey } from '../config.js';
7
+ import { checkApiKeyValidity, checkForUpdates, getCliVersion } from '../lib/api.js';
6
8
 
7
9
  interface CheckResult {
8
10
  ok: boolean;
@@ -15,10 +17,16 @@ interface CheckResult {
15
17
  */
16
18
  export async function doctor(): Promise<void> {
17
19
  console.log(chalk.blue('\n CodeBakers Doctor\n'));
18
- console.log(chalk.gray(' Checking your setup...\n'));
20
+
21
+ // Show version
22
+ const version = getCliVersion();
23
+ console.log(chalk.gray(` CLI Version: ${version}\n`));
24
+
25
+ console.log(chalk.gray(' Running health checks...\n'));
19
26
 
20
27
  const projectChecks = checkProject();
21
28
  const systemChecks = checkSystem();
29
+ const authChecks = await checkAuth();
22
30
 
23
31
  // Display project checks
24
32
  console.log(chalk.white(' Project:'));
@@ -39,8 +47,17 @@ export async function doctor(): Promise<void> {
39
47
  }
40
48
  }
41
49
 
50
+ console.log(chalk.white('\n Authentication:'));
51
+ for (const check of authChecks) {
52
+ const icon = check.ok ? chalk.green('✓') : chalk.red('✗');
53
+ console.log(` ${icon} ${check.message}`);
54
+ if (check.details && !check.ok) {
55
+ console.log(chalk.gray(` └─ ${check.details}`));
56
+ }
57
+ }
58
+
42
59
  // Summary
43
- const allChecks = [...projectChecks, ...systemChecks];
60
+ const allChecks = [...projectChecks, ...systemChecks, ...authChecks];
44
61
  const passed = allChecks.filter(c => c.ok).length;
45
62
  const total = allChecks.length;
46
63
 
@@ -63,8 +80,20 @@ export async function doctor(): Promise<void> {
63
80
  console.log(chalk.gray(' • Run: codebakers install-hook'));
64
81
  }
65
82
 
83
+ const apiKeyCheck = authChecks.find(c => c.message.includes('API key'));
84
+ if (apiKeyCheck && !apiKeyCheck.ok) {
85
+ console.log(chalk.gray(' • Run: codebakers setup'));
86
+ }
87
+
66
88
  console.log('');
67
89
  }
90
+
91
+ // Check for updates
92
+ const updateInfo = await checkForUpdates();
93
+ if (updateInfo?.updateAvailable) {
94
+ console.log(chalk.yellow(` ⚠️ Update available: ${updateInfo.currentVersion} → ${updateInfo.latestVersion}`));
95
+ console.log(chalk.gray(' Run: npm install -g @codebakers/cli@latest\n'));
96
+ }
68
97
  }
69
98
 
70
99
  /**
@@ -111,7 +140,7 @@ function checkProject(): CheckResult[] {
111
140
  results.push({
112
141
  ok: false,
113
142
  message: `Only ${moduleCount} modules found (expected 10+)`,
114
- details: 'Run: codebakers install to add missing modules'
143
+ details: 'Run: codebakers upgrade to get all modules'
115
144
  });
116
145
  } else {
117
146
  results.push({
@@ -217,6 +246,41 @@ function checkSystem(): CheckResult[] {
217
246
  return results;
218
247
  }
219
248
 
249
+ /**
250
+ * Check authentication status
251
+ */
252
+ async function checkAuth(): Promise<CheckResult[]> {
253
+ const results: CheckResult[] = [];
254
+
255
+ // Check if API key is configured
256
+ const apiKey = getApiKey();
257
+ if (!apiKey) {
258
+ results.push({
259
+ ok: false,
260
+ message: 'API key not configured',
261
+ details: 'Run: codebakers setup'
262
+ });
263
+ return results;
264
+ }
265
+
266
+ results.push({ ok: true, message: 'API key configured' });
267
+
268
+ // Validate API key against server
269
+ const validity = await checkApiKeyValidity();
270
+
271
+ if (validity.valid) {
272
+ results.push({ ok: true, message: 'API key is valid' });
273
+ } else {
274
+ results.push({
275
+ ok: false,
276
+ message: 'API key is invalid or expired',
277
+ details: validity.error?.recoverySteps?.[0] || 'Run: codebakers setup'
278
+ });
279
+ }
280
+
281
+ return results;
282
+ }
283
+
220
284
  /**
221
285
  * Quick check - returns true if basic setup is complete
222
286
  */
@@ -226,6 +290,7 @@ export function isSetupComplete(): boolean {
226
290
  const hasClaudeMd = existsSync(join(cwd, 'CLAUDE.md'));
227
291
  const hasClaudeDir = existsSync(join(cwd, '.claude'));
228
292
  const hasHook = isHookInstalled();
293
+ const hasApiKey = !!getApiKey();
229
294
 
230
- return hasClaudeMd && hasClaudeDir && hasHook;
295
+ return hasClaudeMd && hasClaudeDir && hasHook && hasApiKey;
231
296
  }