@kaitranntt/ccs 5.3.0 → 5.4.0-beta.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/VERSION +1 -1
- package/dist/auth/auth-commands.d.ts +5 -1
- package/dist/auth/auth-commands.d.ts.map +1 -1
- package/dist/auth/auth-commands.js +146 -99
- package/dist/auth/auth-commands.js.map +1 -1
- package/dist/ccs.js +7 -7
- package/dist/ccs.js.map +1 -1
- package/dist/commands/api-command.d.ts +11 -0
- package/dist/commands/api-command.d.ts.map +1 -0
- package/dist/commands/{profile-command.js → api-command.js} +142 -106
- package/dist/commands/api-command.js.map +1 -0
- package/dist/commands/help-command.d.ts +1 -1
- package/dist/commands/help-command.d.ts.map +1 -1
- package/dist/commands/help-command.js +174 -88
- package/dist/commands/help-command.js.map +1 -1
- package/dist/delegation/delegation-handler.js +2 -2
- package/dist/delegation/delegation-handler.js.map +1 -1
- package/dist/delegation/headless-executor.d.ts.map +1 -1
- package/dist/delegation/headless-executor.js +7 -4
- package/dist/delegation/headless-executor.js.map +1 -1
- package/dist/delegation/result-formatter.d.ts +6 -25
- package/dist/delegation/result-formatter.d.ts.map +1 -1
- package/dist/delegation/result-formatter.js +77 -116
- package/dist/delegation/result-formatter.js.map +1 -1
- package/dist/management/doctor.d.ts.map +1 -1
- package/dist/management/doctor.js +127 -105
- package/dist/management/doctor.js.map +1 -1
- package/dist/types/utils.d.ts +43 -0
- package/dist/types/utils.d.ts.map +1 -1
- package/dist/utils/error-manager.d.ts +9 -9
- package/dist/utils/error-manager.d.ts.map +1 -1
- package/dist/utils/error-manager.js +88 -92
- package/dist/utils/error-manager.js.map +1 -1
- package/dist/utils/helpers.d.ts +1 -0
- package/dist/utils/helpers.d.ts.map +1 -1
- package/dist/utils/helpers.js +12 -0
- package/dist/utils/helpers.js.map +1 -1
- package/dist/utils/shell-executor.js +2 -2
- package/dist/utils/shell-executor.js.map +1 -1
- package/dist/utils/ui.d.ts +147 -0
- package/dist/utils/ui.d.ts.map +1 -0
- package/dist/utils/ui.js +570 -0
- package/dist/utils/ui.js.map +1 -0
- package/package.json +5 -1
- package/dist/commands/profile-command.d.ts +0 -11
- package/dist/commands/profile-command.d.ts.map +0 -1
- package/dist/commands/profile-command.js.map +0 -1
|
@@ -33,8 +33,8 @@ const fs = __importStar(require("fs"));
|
|
|
33
33
|
const path = __importStar(require("path"));
|
|
34
34
|
const os = __importStar(require("os"));
|
|
35
35
|
const child_process_1 = require("child_process");
|
|
36
|
-
const helpers_1 = require("../utils/helpers");
|
|
37
36
|
const claude_detector_1 = require("../utils/claude-detector");
|
|
37
|
+
const ui_1 = require("../utils/ui");
|
|
38
38
|
const package_json_1 = __importDefault(require("../../package.json"));
|
|
39
39
|
const cliproxy_1 = require("../cliproxy");
|
|
40
40
|
let ora;
|
|
@@ -56,8 +56,6 @@ catch (_e) {
|
|
|
56
56
|
};
|
|
57
57
|
};
|
|
58
58
|
}
|
|
59
|
-
// Import cli-table3
|
|
60
|
-
const Table = require('cli-table3');
|
|
61
59
|
/**
|
|
62
60
|
* Health check results
|
|
63
61
|
*/
|
|
@@ -104,34 +102,36 @@ class Doctor {
|
|
|
104
102
|
* Run all health checks
|
|
105
103
|
*/
|
|
106
104
|
async runAllChecks() {
|
|
107
|
-
|
|
105
|
+
await (0, ui_1.initUI)();
|
|
106
|
+
// Hero header box
|
|
107
|
+
console.log((0, ui_1.box)(`CCS Health Check v${this.ccsVersion}`, { borderStyle: 'round', padding: 0 }));
|
|
108
108
|
console.log('');
|
|
109
109
|
// Store CCS version in details
|
|
110
110
|
this.results.details['CCS Version'] = { status: 'OK', info: `v${this.ccsVersion}` };
|
|
111
111
|
// Group 1: System
|
|
112
|
-
console.log((0,
|
|
112
|
+
console.log((0, ui_1.header)('SYSTEM'));
|
|
113
113
|
await this.checkClaudeCli();
|
|
114
114
|
this.checkCcsDirectory();
|
|
115
115
|
console.log('');
|
|
116
116
|
// Group 2: Configuration
|
|
117
|
-
console.log((0,
|
|
117
|
+
console.log((0, ui_1.header)('CONFIGURATION'));
|
|
118
118
|
this.checkConfigFiles();
|
|
119
119
|
this.checkClaudeSettings();
|
|
120
120
|
console.log('');
|
|
121
121
|
// Group 3: Profiles & Delegation
|
|
122
|
-
console.log((0,
|
|
122
|
+
console.log((0, ui_1.header)('PROFILES & DELEGATION'));
|
|
123
123
|
this.checkProfiles();
|
|
124
124
|
this.checkInstances();
|
|
125
125
|
this.checkDelegation();
|
|
126
126
|
console.log('');
|
|
127
127
|
// Group 4: System Health
|
|
128
|
-
console.log((0,
|
|
128
|
+
console.log((0, ui_1.header)('SYSTEM HEALTH'));
|
|
129
129
|
this.checkPermissions();
|
|
130
130
|
this.checkCcsSymlinks();
|
|
131
131
|
this.checkSettingsSymlinks();
|
|
132
132
|
console.log('');
|
|
133
133
|
// Group 5: CLIProxy (OAuth profiles)
|
|
134
|
-
console.log((0,
|
|
134
|
+
console.log((0, ui_1.header)('CLIPROXY (OAUTH PROFILES)'));
|
|
135
135
|
await this.checkCLIProxy();
|
|
136
136
|
console.log('');
|
|
137
137
|
this.showReport();
|
|
@@ -144,7 +144,8 @@ class Doctor {
|
|
|
144
144
|
const spinner = ora('Checking Claude CLI').start();
|
|
145
145
|
const claudeCli = (0, claude_detector_1.detectClaudeCli)();
|
|
146
146
|
if (!claudeCli) {
|
|
147
|
-
spinner.fail(
|
|
147
|
+
spinner.fail();
|
|
148
|
+
console.log(` ${(0, ui_1.fail)('Claude CLI'.padEnd(22))} Not found in PATH`);
|
|
148
149
|
this.results.addCheck('Claude CLI', 'error', 'Claude CLI not found in PATH', 'Install from: https://docs.claude.com/en/docs/claude-code/installation', { status: 'ERROR', info: 'Not installed' });
|
|
149
150
|
return;
|
|
150
151
|
}
|
|
@@ -169,14 +170,16 @@ class Doctor {
|
|
|
169
170
|
// Extract version from output
|
|
170
171
|
const versionMatch = result.match(/(\d+\.\d+\.\d+)/);
|
|
171
172
|
const version = versionMatch ? versionMatch[1] : 'unknown';
|
|
172
|
-
spinner.succeed(
|
|
173
|
+
spinner.succeed();
|
|
174
|
+
console.log(` ${(0, ui_1.ok)('Claude CLI'.padEnd(22))} v${version} (${claudeCli})`);
|
|
173
175
|
this.results.addCheck('Claude CLI', 'success', `Found: ${claudeCli}`, undefined, {
|
|
174
176
|
status: 'OK',
|
|
175
177
|
info: `v${version} (${claudeCli})`,
|
|
176
178
|
});
|
|
177
179
|
}
|
|
178
180
|
catch (_err) {
|
|
179
|
-
spinner.fail(
|
|
181
|
+
spinner.fail();
|
|
182
|
+
console.log(` ${(0, ui_1.fail)('Claude CLI'.padEnd(22))} Not found or not working`);
|
|
180
183
|
this.results.addCheck('Claude CLI', 'error', 'Claude CLI not found or not working', 'Install from: https://docs.claude.com/en/docs/claude-code/installation', { status: 'ERROR', info: 'Not installed' });
|
|
181
184
|
}
|
|
182
185
|
}
|
|
@@ -186,14 +189,16 @@ class Doctor {
|
|
|
186
189
|
checkCcsDirectory() {
|
|
187
190
|
const spinner = ora('Checking ~/.ccs/ directory').start();
|
|
188
191
|
if (fs.existsSync(this.ccsDir)) {
|
|
189
|
-
spinner.succeed(
|
|
192
|
+
spinner.succeed();
|
|
193
|
+
console.log(` ${(0, ui_1.ok)('CCS Directory'.padEnd(22))} ~/.ccs/`);
|
|
190
194
|
this.results.addCheck('CCS Directory', 'success', undefined, undefined, {
|
|
191
195
|
status: 'OK',
|
|
192
196
|
info: '~/.ccs/',
|
|
193
197
|
});
|
|
194
198
|
}
|
|
195
199
|
else {
|
|
196
|
-
spinner.fail(
|
|
200
|
+
spinner.fail();
|
|
201
|
+
console.log(` ${(0, ui_1.fail)('CCS Directory'.padEnd(22))} Not found`);
|
|
197
202
|
this.results.addCheck('CCS Directory', 'error', '~/.ccs/ directory not found', 'Run: npm install -g @kaitranntt/ccs --force', { status: 'ERROR', info: 'Not found' });
|
|
198
203
|
}
|
|
199
204
|
}
|
|
@@ -220,7 +225,8 @@ class Doctor {
|
|
|
220
225
|
for (const file of files) {
|
|
221
226
|
const spinner = ora(`Checking ${file.name}`).start();
|
|
222
227
|
if (!fs.existsSync(file.path)) {
|
|
223
|
-
spinner.fail(
|
|
228
|
+
spinner.fail();
|
|
229
|
+
console.log(` ${(0, ui_1.fail)(file.name.padEnd(22))} Not found`);
|
|
224
230
|
this.results.addCheck(file.name, 'error', `${file.name} not found`, 'Run: npm install -g @kaitranntt/ccs --force', { status: 'ERROR', info: 'Not found' });
|
|
225
231
|
continue;
|
|
226
232
|
}
|
|
@@ -229,38 +235,40 @@ class Doctor {
|
|
|
229
235
|
const content = fs.readFileSync(file.path, 'utf8');
|
|
230
236
|
JSON.parse(content);
|
|
231
237
|
// Extract useful info based on file type
|
|
232
|
-
let
|
|
238
|
+
let fileInfo = 'Valid';
|
|
233
239
|
let status = 'OK';
|
|
234
240
|
if (file.profile) {
|
|
235
241
|
// For settings files, check if API key is configured
|
|
236
242
|
const validation = DelegationValidator.validate(file.profile);
|
|
237
243
|
if (validation.valid) {
|
|
238
|
-
|
|
244
|
+
fileInfo = 'Key configured';
|
|
239
245
|
status = 'OK';
|
|
240
246
|
}
|
|
241
247
|
else if (validation.error && validation.error.includes('placeholder')) {
|
|
242
|
-
|
|
248
|
+
fileInfo = 'Placeholder key';
|
|
243
249
|
status = 'WARN';
|
|
244
250
|
}
|
|
245
251
|
else {
|
|
246
|
-
|
|
252
|
+
fileInfo = 'Valid JSON';
|
|
247
253
|
status = 'OK';
|
|
248
254
|
}
|
|
249
255
|
}
|
|
250
|
-
const statusIcon = status === 'OK' ? (0, helpers_1.colored)('[OK]', 'green') : (0, helpers_1.colored)('[!]', 'yellow');
|
|
251
256
|
if (status === 'WARN') {
|
|
252
|
-
spinner.warn(
|
|
257
|
+
spinner.warn();
|
|
258
|
+
console.log(` ${(0, ui_1.warn)(file.name.padEnd(22))} ${fileInfo}`);
|
|
253
259
|
}
|
|
254
260
|
else {
|
|
255
|
-
spinner.succeed(
|
|
261
|
+
spinner.succeed();
|
|
262
|
+
console.log(` ${(0, ui_1.ok)(file.name.padEnd(22))} ${fileInfo}`);
|
|
256
263
|
}
|
|
257
264
|
this.results.addCheck(file.name, status === 'OK' ? 'success' : 'warning', undefined, undefined, {
|
|
258
265
|
status: status,
|
|
259
|
-
info:
|
|
266
|
+
info: fileInfo,
|
|
260
267
|
});
|
|
261
268
|
}
|
|
262
269
|
catch (e) {
|
|
263
|
-
spinner.fail(
|
|
270
|
+
spinner.fail();
|
|
271
|
+
console.log(` ${(0, ui_1.fail)(file.name.padEnd(22))} Invalid JSON`);
|
|
264
272
|
this.results.addCheck(file.name, 'error', `Invalid JSON: ${e.message}`, `Backup and recreate: mv ${file.path} ${file.path}.backup && npm install -g @kaitranntt/ccs --force`, { status: 'ERROR', info: 'Invalid JSON' });
|
|
265
273
|
}
|
|
266
274
|
}
|
|
@@ -271,8 +279,10 @@ class Doctor {
|
|
|
271
279
|
checkClaudeSettings() {
|
|
272
280
|
const spinner = ora('Checking ~/.claude/settings.json').start();
|
|
273
281
|
const settingsPath = path.join(this.claudeDir, 'settings.json');
|
|
282
|
+
const settingsName = '~/.claude/settings.json';
|
|
274
283
|
if (!fs.existsSync(settingsPath)) {
|
|
275
|
-
spinner.warn(
|
|
284
|
+
spinner.warn();
|
|
285
|
+
console.log(` ${(0, ui_1.warn)(settingsName.padEnd(22))} Not found`);
|
|
276
286
|
this.results.addCheck('Claude Settings', 'warning', '~/.claude/settings.json not found', 'Run: claude /login');
|
|
277
287
|
return;
|
|
278
288
|
}
|
|
@@ -280,11 +290,13 @@ class Doctor {
|
|
|
280
290
|
try {
|
|
281
291
|
const content = fs.readFileSync(settingsPath, 'utf8');
|
|
282
292
|
JSON.parse(content);
|
|
283
|
-
spinner.succeed(
|
|
293
|
+
spinner.succeed();
|
|
294
|
+
console.log(` ${(0, ui_1.ok)(settingsName.padEnd(22))} Valid`);
|
|
284
295
|
this.results.addCheck('Claude Settings', 'success');
|
|
285
296
|
}
|
|
286
297
|
catch (e) {
|
|
287
|
-
spinner.warn(
|
|
298
|
+
spinner.warn();
|
|
299
|
+
console.log(` ${(0, ui_1.warn)(settingsName.padEnd(22))} Invalid JSON`);
|
|
288
300
|
this.results.addCheck('Claude Settings', 'warning', `Invalid JSON: ${e.message}`, 'Run: claude /login');
|
|
289
301
|
}
|
|
290
302
|
}
|
|
@@ -295,26 +307,30 @@ class Doctor {
|
|
|
295
307
|
const spinner = ora('Checking profiles').start();
|
|
296
308
|
const configPath = path.join(this.ccsDir, 'config.json');
|
|
297
309
|
if (!fs.existsSync(configPath)) {
|
|
298
|
-
spinner.info(
|
|
310
|
+
spinner.info();
|
|
311
|
+
console.log(` ${(0, ui_1.info)('Profiles'.padEnd(22))} config.json not found`);
|
|
299
312
|
return;
|
|
300
313
|
}
|
|
301
314
|
try {
|
|
302
315
|
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
303
316
|
if (!config.profiles || typeof config.profiles !== 'object') {
|
|
304
|
-
spinner.fail(
|
|
317
|
+
spinner.fail();
|
|
318
|
+
console.log(` ${(0, ui_1.fail)('Profiles'.padEnd(22))} Missing profiles object`);
|
|
305
319
|
this.results.addCheck('Profiles', 'error', 'config.json missing profiles object', 'Run: npm install -g @kaitranntt/ccs --force', { status: 'ERROR', info: 'Missing profiles object' });
|
|
306
320
|
return;
|
|
307
321
|
}
|
|
308
322
|
const profileCount = Object.keys(config.profiles).length;
|
|
309
323
|
const profileNames = Object.keys(config.profiles).join(', ');
|
|
310
|
-
spinner.succeed(
|
|
324
|
+
spinner.succeed();
|
|
325
|
+
console.log(` ${(0, ui_1.ok)('Profiles'.padEnd(22))} ${profileCount} configured (${profileNames})`);
|
|
311
326
|
this.results.addCheck('Profiles', 'success', `${profileCount} profiles configured`, undefined, {
|
|
312
327
|
status: 'OK',
|
|
313
328
|
info: `${profileCount} configured (${profileNames.length > 30 ? profileNames.substring(0, 27) + '...' : profileNames})`,
|
|
314
329
|
});
|
|
315
330
|
}
|
|
316
331
|
catch (e) {
|
|
317
|
-
spinner.fail(
|
|
332
|
+
spinner.fail();
|
|
333
|
+
console.log(` ${(0, ui_1.fail)('Profiles'.padEnd(22))} ${e.message}`);
|
|
318
334
|
this.results.addCheck('Profiles', 'error', e.message, undefined, {
|
|
319
335
|
status: 'ERROR',
|
|
320
336
|
info: e.message,
|
|
@@ -328,7 +344,8 @@ class Doctor {
|
|
|
328
344
|
const spinner = ora('Checking instances').start();
|
|
329
345
|
const instancesDir = path.join(this.ccsDir, 'instances');
|
|
330
346
|
if (!fs.existsSync(instancesDir)) {
|
|
331
|
-
spinner.info(
|
|
347
|
+
spinner.info();
|
|
348
|
+
console.log(` ${(0, ui_1.info)('Instances'.padEnd(22))} No account profiles`);
|
|
332
349
|
this.results.addCheck('Instances', 'success', 'No account profiles configured');
|
|
333
350
|
return;
|
|
334
351
|
}
|
|
@@ -336,11 +353,13 @@ class Doctor {
|
|
|
336
353
|
return fs.statSync(path.join(instancesDir, name)).isDirectory();
|
|
337
354
|
});
|
|
338
355
|
if (instances.length === 0) {
|
|
339
|
-
spinner.info(
|
|
356
|
+
spinner.info();
|
|
357
|
+
console.log(` ${(0, ui_1.info)('Instances'.padEnd(22))} No account profiles`);
|
|
340
358
|
this.results.addCheck('Instances', 'success', 'No account profiles');
|
|
341
359
|
return;
|
|
342
360
|
}
|
|
343
|
-
spinner.succeed(
|
|
361
|
+
spinner.succeed();
|
|
362
|
+
console.log(` ${(0, ui_1.ok)('Instances'.padEnd(22))} ${instances.length} account profiles`);
|
|
344
363
|
this.results.addCheck('Instances', 'success', `${instances.length} account profiles`);
|
|
345
364
|
}
|
|
346
365
|
/**
|
|
@@ -353,7 +372,8 @@ class Doctor {
|
|
|
353
372
|
const hasCcsCommand = fs.existsSync(path.join(ccsClaudeCommandsDir, 'ccs.md'));
|
|
354
373
|
const hasContinueCommand = fs.existsSync(path.join(ccsClaudeCommandsDir, 'ccs', 'continue.md'));
|
|
355
374
|
if (!hasCcsCommand || !hasContinueCommand) {
|
|
356
|
-
spinner.warn(
|
|
375
|
+
spinner.warn();
|
|
376
|
+
console.log(` ${(0, ui_1.warn)('Delegation'.padEnd(22))} Not installed`);
|
|
357
377
|
this.results.addCheck('Delegation', 'warning', 'Delegation commands not found', 'Install with: npm install -g @kaitranntt/ccs --force', { status: 'WARN', info: 'Not installed' });
|
|
358
378
|
return;
|
|
359
379
|
}
|
|
@@ -367,11 +387,13 @@ class Doctor {
|
|
|
367
387
|
}
|
|
368
388
|
}
|
|
369
389
|
if (readyProfiles.length === 0) {
|
|
370
|
-
spinner.warn(
|
|
390
|
+
spinner.warn();
|
|
391
|
+
console.log(` ${(0, ui_1.warn)('Delegation'.padEnd(22))} No profiles ready`);
|
|
371
392
|
this.results.addCheck('Delegation', 'warning', 'Delegation installed but no profiles configured', 'Configure profiles with valid API keys (not placeholders)', { status: 'WARN', info: 'No profiles ready' });
|
|
372
393
|
return;
|
|
373
394
|
}
|
|
374
|
-
spinner.succeed(
|
|
395
|
+
spinner.succeed();
|
|
396
|
+
console.log(` ${(0, ui_1.ok)('Delegation'.padEnd(22))} ${readyProfiles.length} profiles ready (${readyProfiles.join(', ')})`);
|
|
375
397
|
this.results.addCheck('Delegation', 'success', `${readyProfiles.length} profile(s) ready: ${readyProfiles.join(', ')}`, undefined, { status: 'OK', info: `${readyProfiles.length} profiles ready` });
|
|
376
398
|
}
|
|
377
399
|
/**
|
|
@@ -383,14 +405,16 @@ class Doctor {
|
|
|
383
405
|
try {
|
|
384
406
|
fs.writeFileSync(testFile, 'test', 'utf8');
|
|
385
407
|
fs.unlinkSync(testFile);
|
|
386
|
-
spinner.succeed(
|
|
408
|
+
spinner.succeed();
|
|
409
|
+
console.log(` ${(0, ui_1.ok)('Permissions'.padEnd(22))} Write access verified`);
|
|
387
410
|
this.results.addCheck('Permissions', 'success', undefined, undefined, {
|
|
388
411
|
status: 'OK',
|
|
389
412
|
info: 'Write access verified',
|
|
390
413
|
});
|
|
391
414
|
}
|
|
392
415
|
catch (_e) {
|
|
393
|
-
spinner.fail(
|
|
416
|
+
spinner.fail();
|
|
417
|
+
console.log(` ${(0, ui_1.fail)('Permissions'.padEnd(22))} Cannot write to ~/.ccs/`);
|
|
394
418
|
this.results.addCheck('Permissions', 'error', 'Cannot write to ~/.ccs/', 'Fix: sudo chown -R $USER ~/.ccs ~/.claude && chmod 755 ~/.ccs ~/.claude', { status: 'ERROR', info: 'Cannot write to ~/.ccs/' });
|
|
395
419
|
}
|
|
396
420
|
}
|
|
@@ -405,19 +429,22 @@ class Doctor {
|
|
|
405
429
|
const health = manager.checkHealth();
|
|
406
430
|
if (health.healthy) {
|
|
407
431
|
const itemCount = manager.ccsItems.length;
|
|
408
|
-
spinner.succeed(
|
|
432
|
+
spinner.succeed();
|
|
433
|
+
console.log(` ${(0, ui_1.ok)('CCS Symlinks'.padEnd(22))} ${itemCount}/${itemCount} items linked`);
|
|
409
434
|
this.results.addCheck('CCS Symlinks', 'success', 'All CCS items properly symlinked', undefined, {
|
|
410
435
|
status: 'OK',
|
|
411
436
|
info: `${itemCount}/${itemCount} items synced`,
|
|
412
437
|
});
|
|
413
438
|
}
|
|
414
439
|
else {
|
|
415
|
-
spinner.warn(
|
|
440
|
+
spinner.warn();
|
|
441
|
+
console.log(` ${(0, ui_1.warn)('CCS Symlinks'.padEnd(22))} ${health.issues.length} issues found`);
|
|
416
442
|
this.results.addCheck('CCS Symlinks', 'warning', health.issues.join(', '), 'Run: ccs sync', { status: 'WARN', info: `${health.issues.length} issues` });
|
|
417
443
|
}
|
|
418
444
|
}
|
|
419
445
|
catch (e) {
|
|
420
|
-
spinner.warn(
|
|
446
|
+
spinner.warn();
|
|
447
|
+
console.log(` ${(0, ui_1.warn)('CCS Symlinks'.padEnd(22))} Could not check`);
|
|
421
448
|
this.results.addCheck('CCS Symlinks', 'warning', 'Could not check CCS symlinks: ' + e.message, 'Run: ccs sync', { status: 'WARN', info: 'Could not check' });
|
|
422
449
|
}
|
|
423
450
|
}
|
|
@@ -426,33 +453,38 @@ class Doctor {
|
|
|
426
453
|
*/
|
|
427
454
|
checkSettingsSymlinks() {
|
|
428
455
|
const spinner = ora('Checking settings.json symlinks').start();
|
|
456
|
+
const settingsLabel = 'settings.json';
|
|
429
457
|
try {
|
|
430
458
|
const sharedDir = path.join(this.homedir, '.ccs', 'shared');
|
|
431
459
|
const sharedSettings = path.join(sharedDir, 'settings.json');
|
|
432
460
|
const claudeSettings = path.join(this.claudeDir, 'settings.json');
|
|
433
461
|
// Check shared settings exists and points to ~/.claude/
|
|
434
462
|
if (!fs.existsSync(sharedSettings)) {
|
|
435
|
-
spinner.warn(
|
|
463
|
+
spinner.warn();
|
|
464
|
+
console.log(` ${(0, ui_1.warn)(settingsLabel.padEnd(22))} Not found (shared)`);
|
|
436
465
|
this.results.addCheck('Settings Symlinks', 'warning', 'Shared settings.json not found', 'Run: ccs sync');
|
|
437
466
|
return;
|
|
438
467
|
}
|
|
439
468
|
const sharedStats = fs.lstatSync(sharedSettings);
|
|
440
469
|
if (!sharedStats.isSymbolicLink()) {
|
|
441
|
-
spinner.warn(
|
|
470
|
+
spinner.warn();
|
|
471
|
+
console.log(` ${(0, ui_1.warn)(settingsLabel.padEnd(22))} Not a symlink (shared)`);
|
|
442
472
|
this.results.addCheck('Settings Symlinks', 'warning', 'Shared settings.json is not a symlink', 'Run: ccs sync');
|
|
443
473
|
return;
|
|
444
474
|
}
|
|
445
475
|
const sharedTarget = fs.readlinkSync(sharedSettings);
|
|
446
476
|
const resolvedShared = path.resolve(path.dirname(sharedSettings), sharedTarget);
|
|
447
477
|
if (resolvedShared !== claudeSettings) {
|
|
448
|
-
spinner.warn(
|
|
478
|
+
spinner.warn();
|
|
479
|
+
console.log(` ${(0, ui_1.warn)(settingsLabel.padEnd(22))} Wrong target (shared)`);
|
|
449
480
|
this.results.addCheck('Settings Symlinks', 'warning', `Points to ${resolvedShared} instead of ${claudeSettings}`, 'Run: ccs sync');
|
|
450
481
|
return;
|
|
451
482
|
}
|
|
452
483
|
// Check each instance
|
|
453
484
|
const instancesDir = path.join(this.ccsDir, 'instances');
|
|
454
485
|
if (!fs.existsSync(instancesDir)) {
|
|
455
|
-
spinner.succeed(
|
|
486
|
+
spinner.succeed();
|
|
487
|
+
console.log(` ${(0, ui_1.ok)(settingsLabel.padEnd(22))} Shared symlink valid`);
|
|
456
488
|
this.results.addCheck('Settings Symlinks', 'success', 'Shared symlink valid', undefined, {
|
|
457
489
|
status: 'OK',
|
|
458
490
|
info: 'Shared symlink valid',
|
|
@@ -487,11 +519,13 @@ class Doctor {
|
|
|
487
519
|
}
|
|
488
520
|
}
|
|
489
521
|
if (broken > 0) {
|
|
490
|
-
spinner.warn(
|
|
522
|
+
spinner.warn();
|
|
523
|
+
console.log(` ${(0, ui_1.warn)(settingsLabel.padEnd(22))} ${broken} broken instance(s)`);
|
|
491
524
|
this.results.addCheck('Settings Symlinks', 'warning', `${broken} instance(s) have broken symlinks`, 'Run: ccs sync', { status: 'WARN', info: `${broken} broken instance(s)` });
|
|
492
525
|
}
|
|
493
526
|
else {
|
|
494
|
-
spinner.succeed(
|
|
527
|
+
spinner.succeed();
|
|
528
|
+
console.log(` ${(0, ui_1.ok)(settingsLabel.padEnd(22))} ${instances.length} instance(s) valid`);
|
|
495
529
|
this.results.addCheck('Settings Symlinks', 'success', 'All instance symlinks valid', undefined, {
|
|
496
530
|
status: 'OK',
|
|
497
531
|
info: `${instances.length} instance(s) valid`,
|
|
@@ -499,7 +533,8 @@ class Doctor {
|
|
|
499
533
|
}
|
|
500
534
|
}
|
|
501
535
|
catch (err) {
|
|
502
|
-
spinner.warn(
|
|
536
|
+
spinner.warn();
|
|
537
|
+
console.log(` ${(0, ui_1.warn)(settingsLabel.padEnd(22))} Check failed`);
|
|
503
538
|
this.results.addCheck('Settings Symlinks', 'warning', `Failed to check: ${err.message}`, 'Run: ccs sync', { status: 'WARN', info: 'Check failed' });
|
|
504
539
|
}
|
|
505
540
|
}
|
|
@@ -511,28 +546,32 @@ class Doctor {
|
|
|
511
546
|
const binarySpinner = ora('Checking CLIProxy binary').start();
|
|
512
547
|
if ((0, cliproxy_1.isCLIProxyInstalled)()) {
|
|
513
548
|
const binaryPath = (0, cliproxy_1.getCLIProxyPath)();
|
|
514
|
-
binarySpinner.succeed(
|
|
549
|
+
binarySpinner.succeed();
|
|
550
|
+
console.log(` ${(0, ui_1.ok)('CLIProxy Binary'.padEnd(22))} v${cliproxy_1.CLIPROXY_VERSION}`);
|
|
515
551
|
this.results.addCheck('CLIProxy Binary', 'success', undefined, undefined, {
|
|
516
552
|
status: 'OK',
|
|
517
553
|
info: `v${cliproxy_1.CLIPROXY_VERSION} (${binaryPath})`,
|
|
518
554
|
});
|
|
519
555
|
}
|
|
520
556
|
else {
|
|
521
|
-
binarySpinner.info(
|
|
557
|
+
binarySpinner.info();
|
|
558
|
+
console.log(` ${(0, ui_1.info)('CLIProxy Binary'.padEnd(22))} Not installed (downloads on first use)`);
|
|
522
559
|
this.results.addCheck('CLIProxy Binary', 'success', 'Not installed yet', 'Run: ccs gemini "test" (will download automatically)', { status: 'OK', info: 'Not installed (downloads on first use)' });
|
|
523
560
|
}
|
|
524
561
|
// 2. Config file exists?
|
|
525
562
|
const configSpinner = ora('Checking CLIProxy config').start();
|
|
526
563
|
const configPath = (0, cliproxy_1.getConfigPath)();
|
|
527
564
|
if (fs.existsSync(configPath)) {
|
|
528
|
-
configSpinner.succeed(
|
|
565
|
+
configSpinner.succeed();
|
|
566
|
+
console.log(` ${(0, ui_1.ok)('CLIProxy Config'.padEnd(22))} cliproxy/config.yaml`);
|
|
529
567
|
this.results.addCheck('CLIProxy Config', 'success', undefined, undefined, {
|
|
530
568
|
status: 'OK',
|
|
531
569
|
info: 'cliproxy/config.yaml',
|
|
532
570
|
});
|
|
533
571
|
}
|
|
534
572
|
else {
|
|
535
|
-
configSpinner.info(
|
|
573
|
+
configSpinner.info();
|
|
574
|
+
console.log(` ${(0, ui_1.info)('CLIProxy Config'.padEnd(22))} Not created (on first use)`);
|
|
536
575
|
this.results.addCheck('CLIProxy Config', 'success', 'Not created yet', undefined, {
|
|
537
576
|
status: 'OK',
|
|
538
577
|
info: 'Generated on first use',
|
|
@@ -545,14 +584,16 @@ class Doctor {
|
|
|
545
584
|
const providerName = status.provider.charAt(0).toUpperCase() + status.provider.slice(1);
|
|
546
585
|
if (status.authenticated) {
|
|
547
586
|
const lastAuth = status.lastAuth ? ` (${status.lastAuth.toLocaleDateString()})` : '';
|
|
548
|
-
authSpinner.succeed(
|
|
587
|
+
authSpinner.succeed();
|
|
588
|
+
console.log(` ${(0, ui_1.ok)(`${providerName} Auth`.padEnd(22))} Authenticated${lastAuth}`);
|
|
549
589
|
this.results.addCheck(`${providerName} Auth`, 'success', undefined, undefined, {
|
|
550
590
|
status: 'OK',
|
|
551
591
|
info: `Authenticated${lastAuth}`,
|
|
552
592
|
});
|
|
553
593
|
}
|
|
554
594
|
else {
|
|
555
|
-
authSpinner.info(
|
|
595
|
+
authSpinner.info();
|
|
596
|
+
console.log(` ${(0, ui_1.info)(`${providerName} Auth`.padEnd(22))} Not authenticated`);
|
|
556
597
|
this.results.addCheck(`${providerName} Auth`, 'success', 'Not authenticated', `Run: ccs ${status.provider} --auth`, { status: 'OK', info: 'Not authenticated (run ccs <profile> to login)' });
|
|
557
598
|
}
|
|
558
599
|
}
|
|
@@ -560,14 +601,16 @@ class Doctor {
|
|
|
560
601
|
const portSpinner = ora(`Checking port ${cliproxy_1.CLIPROXY_DEFAULT_PORT}`).start();
|
|
561
602
|
const portAvailable = await (0, cliproxy_1.isPortAvailable)(cliproxy_1.CLIPROXY_DEFAULT_PORT);
|
|
562
603
|
if (portAvailable) {
|
|
563
|
-
portSpinner.succeed(
|
|
604
|
+
portSpinner.succeed();
|
|
605
|
+
console.log(` ${(0, ui_1.ok)('CLIProxy Port'.padEnd(22))} ${cliproxy_1.CLIPROXY_DEFAULT_PORT} available`);
|
|
564
606
|
this.results.addCheck('CLIProxy Port', 'success', undefined, undefined, {
|
|
565
607
|
status: 'OK',
|
|
566
608
|
info: `Port ${cliproxy_1.CLIPROXY_DEFAULT_PORT} available`,
|
|
567
609
|
});
|
|
568
610
|
}
|
|
569
611
|
else {
|
|
570
|
-
portSpinner.warn(
|
|
612
|
+
portSpinner.warn();
|
|
613
|
+
console.log(` ${(0, ui_1.warn)('CLIProxy Port'.padEnd(22))} ${cliproxy_1.CLIPROXY_DEFAULT_PORT} in use`);
|
|
571
614
|
this.results.addCheck('CLIProxy Port', 'warning', `Port ${cliproxy_1.CLIPROXY_DEFAULT_PORT} is in use`, `Check: lsof -i :${cliproxy_1.CLIPROXY_DEFAULT_PORT}`, { status: 'WARN', info: `Port ${cliproxy_1.CLIPROXY_DEFAULT_PORT} in use` });
|
|
572
615
|
}
|
|
573
616
|
}
|
|
@@ -576,74 +619,53 @@ class Doctor {
|
|
|
576
619
|
*/
|
|
577
620
|
showReport() {
|
|
578
621
|
console.log('');
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
const
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
colWidths: [20, 10, 35],
|
|
590
|
-
wordWrap: true,
|
|
591
|
-
chars: {
|
|
592
|
-
top: '═',
|
|
593
|
-
'top-mid': '╤',
|
|
594
|
-
'top-left': '╔',
|
|
595
|
-
'top-right': '╗',
|
|
596
|
-
bottom: '═',
|
|
597
|
-
'bottom-mid': '╧',
|
|
598
|
-
'bottom-left': '╚',
|
|
599
|
-
'bottom-right': '╝',
|
|
600
|
-
left: '║',
|
|
601
|
-
'left-mid': '╟',
|
|
602
|
-
mid: '─',
|
|
603
|
-
'mid-mid': '┼',
|
|
604
|
-
right: '║',
|
|
605
|
-
'right-mid': '╢',
|
|
606
|
-
middle: '│',
|
|
607
|
-
},
|
|
622
|
+
console.log((0, ui_1.header)('HEALTH CHECK SUMMARY'));
|
|
623
|
+
console.log('');
|
|
624
|
+
// Build summary table rows
|
|
625
|
+
const rows = Object.entries(this.results.details).map(([component, detail]) => {
|
|
626
|
+
const statusIndicator = detail.status === 'OK'
|
|
627
|
+
? (0, ui_1.color)('[OK]', 'success')
|
|
628
|
+
: detail.status === 'ERROR'
|
|
629
|
+
? (0, ui_1.color)('[X]', 'error')
|
|
630
|
+
: (0, ui_1.color)('[!]', 'warning');
|
|
631
|
+
return [component, statusIndicator, detail.info || ''];
|
|
608
632
|
});
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
}
|
|
614
|
-
console.log(table.toString());
|
|
633
|
+
console.log((0, ui_1.table)(rows, {
|
|
634
|
+
head: ['Component', 'Status', 'Details'],
|
|
635
|
+
colWidths: [20, 12, 35],
|
|
636
|
+
}));
|
|
615
637
|
console.log('');
|
|
616
|
-
// Show errors
|
|
638
|
+
// Show errors if present
|
|
617
639
|
if (this.results.hasErrors()) {
|
|
618
|
-
console.log((0,
|
|
640
|
+
console.log((0, ui_1.header)('ERRORS'));
|
|
619
641
|
this.results.errors.forEach((err) => {
|
|
620
|
-
console.log(`
|
|
642
|
+
console.log(` ${(0, ui_1.fail)(err.name)}: ${err.message}`);
|
|
621
643
|
if (err.fix) {
|
|
622
|
-
console.log(`
|
|
644
|
+
console.log(` Fix: ${(0, ui_1.color)(err.fix, 'command')}`);
|
|
623
645
|
}
|
|
624
646
|
});
|
|
625
647
|
console.log('');
|
|
626
648
|
}
|
|
649
|
+
// Show warnings if present
|
|
627
650
|
if (this.results.hasWarnings()) {
|
|
628
|
-
console.log((0,
|
|
629
|
-
this.results.warnings.forEach((
|
|
630
|
-
console.log(`
|
|
631
|
-
if (
|
|
632
|
-
console.log(`
|
|
651
|
+
console.log((0, ui_1.header)('WARNINGS'));
|
|
652
|
+
this.results.warnings.forEach((w) => {
|
|
653
|
+
console.log(` ${(0, ui_1.warn)(w.name)}: ${w.message}`);
|
|
654
|
+
if (w.fix) {
|
|
655
|
+
console.log(` Fix: ${(0, ui_1.color)(w.fix, 'command')}`);
|
|
633
656
|
}
|
|
634
657
|
});
|
|
635
658
|
console.log('');
|
|
636
659
|
}
|
|
637
660
|
// Final status
|
|
638
661
|
if (this.results.isHealthy() && !this.results.hasWarnings()) {
|
|
639
|
-
console.log((0,
|
|
662
|
+
console.log((0, ui_1.ok)('All checks passed! Installation is healthy.'));
|
|
640
663
|
}
|
|
641
664
|
else if (this.results.hasErrors()) {
|
|
642
|
-
console.log((0,
|
|
643
|
-
console.log('Run suggested fixes above to resolve issues.');
|
|
665
|
+
console.log((0, ui_1.fail)('Installation has errors. Run suggested fixes above.'));
|
|
644
666
|
}
|
|
645
667
|
else {
|
|
646
|
-
console.log((0,
|
|
668
|
+
console.log((0, ui_1.ok)(`Installation healthy (${this.results.warnings.length} warning${this.results.warnings.length !== 1 ? 's' : ''})`));
|
|
647
669
|
}
|
|
648
670
|
console.log('');
|
|
649
671
|
}
|