@codebakers/cli 1.5.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.
- package/dist/commands/audit.d.ts +19 -0
- package/dist/commands/audit.js +730 -0
- package/dist/commands/config.d.ts +4 -0
- package/dist/commands/config.js +176 -0
- package/dist/commands/doctor.js +59 -4
- package/dist/commands/heal.d.ts +41 -0
- package/dist/commands/heal.js +734 -0
- package/dist/commands/login.js +12 -16
- package/dist/commands/provision.d.ts +55 -3
- package/dist/commands/provision.js +243 -74
- package/dist/commands/scaffold.js +221 -41
- package/dist/commands/setup.js +60 -19
- package/dist/commands/upgrade.d.ts +4 -0
- package/dist/commands/upgrade.js +90 -0
- package/dist/config.d.ts +61 -5
- package/dist/config.js +268 -5
- package/dist/index.js +44 -3
- package/dist/lib/api.d.ts +45 -0
- package/dist/lib/api.js +159 -0
- package/dist/mcp/server.js +146 -0
- package/package.json +1 -1
- package/src/commands/audit.ts +827 -0
- package/src/commands/config.ts +216 -0
- package/src/commands/doctor.ts +69 -4
- package/src/commands/heal.ts +889 -0
- package/src/commands/login.ts +14 -18
- package/src/commands/provision.ts +323 -101
- package/src/commands/scaffold.ts +257 -43
- package/src/commands/setup.ts +65 -20
- package/src/commands/upgrade.ts +110 -0
- package/src/config.ts +320 -11
- package/src/index.ts +48 -3
- package/src/lib/api.ts +183 -0
- package/src/mcp/server.ts +160 -0
|
@@ -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
|
+
}
|
package/src/commands/doctor.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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
|
}
|