ccjk 9.6.1 → 9.8.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.
Files changed (56) hide show
  1. package/dist/chunks/boost.mjs +246 -7
  2. package/dist/chunks/ccjk-mcp.mjs +1 -1
  3. package/dist/chunks/ccr.mjs +25 -28
  4. package/dist/chunks/check-updates.mjs +4 -3
  5. package/dist/chunks/claude-code-config-manager.mjs +1 -1
  6. package/dist/chunks/claude-code-incremental-manager.mjs +1 -1
  7. package/dist/chunks/claude-config.mjs +1 -1
  8. package/dist/chunks/codex-config-switch.mjs +3 -4
  9. package/dist/chunks/codex-provider-manager.mjs +1 -2
  10. package/dist/chunks/codex.mjs +204 -3
  11. package/dist/chunks/config-switch.mjs +2 -3
  12. package/dist/chunks/config.mjs +1 -1
  13. package/dist/chunks/doctor.mjs +1 -1
  14. package/dist/chunks/features.mjs +24 -15
  15. package/dist/chunks/hook-installer.mjs +44 -0
  16. package/dist/chunks/index3.mjs +32 -32
  17. package/dist/chunks/init.mjs +129 -87
  18. package/dist/chunks/installer2.mjs +1 -1
  19. package/dist/chunks/interview.mjs +1 -1
  20. package/dist/chunks/mcp.mjs +1058 -17
  21. package/dist/chunks/menu.mjs +140 -56
  22. package/dist/chunks/package.mjs +2 -210
  23. package/dist/chunks/platform.mjs +1 -1
  24. package/dist/chunks/quick-setup.mjs +35 -18
  25. package/dist/chunks/simple-config.mjs +1 -1
  26. package/dist/{shared/ccjk.q1koQxEE.mjs → chunks/smart-defaults.mjs} +77 -79
  27. package/dist/chunks/status.mjs +208 -101
  28. package/dist/chunks/thinking.mjs +1 -1
  29. package/dist/chunks/uninstall.mjs +6 -4
  30. package/dist/chunks/update.mjs +4 -7
  31. package/dist/chunks/version-checker.mjs +1 -1
  32. package/dist/cli.mjs +4 -80
  33. package/dist/index.d.mts +17 -1482
  34. package/dist/index.d.ts +17 -1482
  35. package/dist/index.mjs +12 -4191
  36. package/dist/shared/{ccjk.CSkyCZIM.mjs → ccjk.Bndhan7G.mjs} +4 -242
  37. package/dist/shared/ccjk.CeE8RLG2.mjs +62 -0
  38. package/dist/shared/ccjk.DKojSRzw.mjs +266 -0
  39. package/dist/shared/{ccjk.CItD1fpl.mjs → ccjk.DvIrK0wz.mjs} +1 -1
  40. package/dist/shared/ccjk.LsPZ2PYo.mjs +1048 -0
  41. package/package.json +1 -1
  42. package/dist/chunks/api-adapter.mjs +0 -180
  43. package/dist/chunks/cli.mjs +0 -2227
  44. package/dist/chunks/context-menu.mjs +0 -913
  45. package/dist/chunks/hooks-sync.mjs +0 -1627
  46. package/dist/chunks/mcp-market.mjs +0 -1077
  47. package/dist/chunks/mcp-server.mjs +0 -776
  48. package/dist/chunks/project-detector.mjs +0 -131
  49. package/dist/chunks/provider-registry.mjs +0 -92
  50. package/dist/chunks/setup-wizard.mjs +0 -362
  51. package/dist/chunks/tools.mjs +0 -143
  52. package/dist/chunks/workflows2.mjs +0 -232
  53. package/dist/shared/ccjk.C0pb50xH.mjs +0 -347
  54. package/dist/shared/ccjk.ChMkBmdL.mjs +0 -490
  55. package/dist/shared/ccjk.CtSfXUSh.mjs +0 -209
  56. package/dist/shared/ccjk.xfAjmbJp.mjs +0 -75
@@ -1,30 +1,26 @@
1
1
  import ansis from 'ansis';
2
- import { i18n } from './index.mjs';
2
+ import { i18n, ensureI18nInitialized } from './index.mjs';
3
3
  import 'node:child_process';
4
4
  import 'node:process';
5
5
  import { M as MCP_SERVICE_TIERS, i as isCoreService, g as getServicesByTier, c as checkMcpPerformance, f as formatPerformanceWarning, a as calculateResourceUsage, b as getOptimizationSuggestions, d as getMcpTierConfig } from './mcp-performance.mjs';
6
- import { r as readMcpConfig, b as backupMcpConfig, w as writeMcpConfig } from './claude-config.mjs';
7
- export { mcpInstall, mcpList, mcpSearch, mcpUninstall } from './mcp-market.mjs';
8
- import { M as MCP_SERVICE_CONFIGS } from '../shared/ccjk.CtSfXUSh.mjs';
9
- import 'node:fs';
6
+ import { r as readMcpConfig, b as backupMcpConfig, w as writeMcpConfig, d as deepMerge, a as buildMcpServerConfig } from './claude-config.mjs';
7
+ import inquirer from 'inquirer';
8
+ import { g as getMcpService, r as readCodexConfig, a as applyCodexPlatformCommand, w as writeCodexConfig, M as MCP_SERVICE_CONFIGS } from './codex.mjs';
9
+ import { join } from 'pathe';
10
+ import { SETTINGS_FILE, CLAUDE_DIR, ClAUDE_CONFIG_FILE, CODEX_CONFIG_FILE } from './constants.mjs';
11
+ import { writeFileAtomic, exists, ensureDir } from './fs-operations.mjs';
12
+ import { readJsonConfig, writeJsonConfig } from './json-config.mjs';
13
+ import { i as isWindows, g as getSystemRoot } from './platform.mjs';
14
+ import { existsSync, unlinkSync, statSync, mkdirSync, readFileSync } from 'node:fs';
15
+ import { homedir } from 'node:os';
10
16
  import 'node:url';
11
17
  import 'i18next';
12
18
  import 'i18next-fs-backend';
13
- import 'pathe';
14
- import './constants.mjs';
15
- import 'node:os';
16
- import './json-config.mjs';
17
19
  import 'dayjs';
18
- import './fs-operations.mjs';
19
- import 'node:crypto';
20
- import 'node:fs/promises';
21
- import './platform.mjs';
22
- import 'tinyexec';
23
- import 'inquirer';
24
- import './codex.mjs';
25
20
  import 'ora';
26
21
  import 'semver';
27
22
  import 'smol-toml';
23
+ import 'tinyexec';
28
24
  import './ccjk-config.mjs';
29
25
  import './config.mjs';
30
26
  import '../shared/ccjk.BFQ7yr5S.mjs';
@@ -32,6 +28,8 @@ import './prompts.mjs';
32
28
  import './package.mjs';
33
29
  import '../shared/ccjk.DHbrGcgg.mjs';
34
30
  import 'inquirer-toggle';
31
+ import 'node:crypto';
32
+ import 'node:fs/promises';
35
33
 
36
34
  function getReleasableServices(configuredServices, tier) {
37
35
  return configuredServices.filter((serviceId) => {
@@ -287,6 +285,1049 @@ async function mcpDoctor(options = {}) {
287
285
  console.log("");
288
286
  }
289
287
 
288
+ const DEFAULT_API_URL = "https://api.api.claudehome.cn/v1/mcp-marketplace";
289
+ const REQUEST_TIMEOUT = 3e4;
290
+ const MAX_RETRY_ATTEMPTS = 3;
291
+ const RETRY_DELAY = 1e3;
292
+ const DEFAULT_CACHE_TTL = 36e5;
293
+ const DEFAULT_THROTTLE_INTERVAL = 100;
294
+ const CACHE_VERSION = "1.0.0";
295
+ const CACHE_BASE_DIR = join(homedir(), ".ccjk", "mcp-marketplace", "cache");
296
+ class MarketplaceClient {
297
+ baseUrl;
298
+ apiKey;
299
+ timeout;
300
+ offlineMode;
301
+ enableLogging;
302
+ maxRetries;
303
+ retryDelay;
304
+ cacheTTL;
305
+ enableDeduplication;
306
+ throttleInterval;
307
+ // In-memory cache
308
+ memoryCache;
309
+ // Pending requests for deduplication
310
+ pendingRequests;
311
+ // Last request timestamp for throttling
312
+ lastRequestTime;
313
+ // File-based cache
314
+ cacheDir;
315
+ cacheFile;
316
+ constructor(options = {}) {
317
+ this.baseUrl = options.baseUrl || DEFAULT_API_URL;
318
+ this.apiKey = options.apiKey;
319
+ this.timeout = options.timeout || REQUEST_TIMEOUT;
320
+ this.offlineMode = options.offlineMode || false;
321
+ this.enableLogging = options.enableLogging || false;
322
+ this.maxRetries = options.maxRetries || MAX_RETRY_ATTEMPTS;
323
+ this.retryDelay = options.retryDelay || RETRY_DELAY;
324
+ this.cacheTTL = options.cacheTTL || DEFAULT_CACHE_TTL;
325
+ this.enableDeduplication = options.enableDeduplication !== false;
326
+ this.throttleInterval = options.throttleInterval || DEFAULT_THROTTLE_INTERVAL;
327
+ this.memoryCache = /* @__PURE__ */ new Map();
328
+ this.pendingRequests = /* @__PURE__ */ new Map();
329
+ this.lastRequestTime = 0;
330
+ this.cacheDir = CACHE_BASE_DIR;
331
+ this.cacheFile = join(this.cacheDir, "marketplace.json");
332
+ }
333
+ // ==========================================================================
334
+ // Public API Methods
335
+ // ==========================================================================
336
+ /**
337
+ * Search packages with filters and sorting
338
+ */
339
+ async search(options = {}) {
340
+ this.log("Searching packages with options:", options);
341
+ const params = this.buildSearchParams(options);
342
+ const response = await this.request("/search", {
343
+ method: "GET",
344
+ params
345
+ });
346
+ if (response.success && response.data) {
347
+ return response.data;
348
+ }
349
+ return {
350
+ packages: [],
351
+ total: 0,
352
+ page: options.page || 1,
353
+ limit: options.limit || 20,
354
+ totalPages: 0,
355
+ hasMore: false
356
+ };
357
+ }
358
+ /**
359
+ * Get detailed information about a specific package
360
+ */
361
+ async getPackage(id) {
362
+ this.log("Getting package:", id);
363
+ const encodedId = encodeURIComponent(id);
364
+ const response = await this.request(`/packages/${encodedId}`, {
365
+ method: "GET"
366
+ });
367
+ return response.success && response.data ? response.data : null;
368
+ }
369
+ /**
370
+ * Get version history for a package
371
+ */
372
+ async getVersions(id) {
373
+ this.log("Getting versions for package:", id);
374
+ const encodedId = encodeURIComponent(id);
375
+ const response = await this.request(`/packages/${encodedId}/versions`, {
376
+ method: "GET"
377
+ });
378
+ return response.success && response.data ? response.data : [];
379
+ }
380
+ /**
381
+ * Get trending/popular packages
382
+ */
383
+ async getTrending(limit = 10) {
384
+ this.log("Getting trending packages, limit:", limit);
385
+ const response = await this.request("/trending", {
386
+ method: "GET",
387
+ params: { limit }
388
+ });
389
+ return response.success && response.data ? response.data : [];
390
+ }
391
+ /**
392
+ * Get personalized recommendations based on installed packages
393
+ */
394
+ async getRecommendations(installed) {
395
+ this.log("Getting recommendations for installed packages:", installed);
396
+ const response = await this.request("/recommendations", {
397
+ method: "POST",
398
+ body: JSON.stringify({ installed })
399
+ });
400
+ return response.success && response.data ? response.data : [];
401
+ }
402
+ /**
403
+ * Get all available categories
404
+ */
405
+ async getCategories() {
406
+ this.log("Getting categories");
407
+ const response = await this.request("/categories", {
408
+ method: "GET"
409
+ });
410
+ return response.success && response.data ? response.data : [];
411
+ }
412
+ /**
413
+ * Check for updates for installed packages
414
+ */
415
+ async checkUpdates(installed) {
416
+ this.log("Checking updates for", installed.length, "packages");
417
+ const response = await this.request("/updates/check", {
418
+ method: "POST",
419
+ body: JSON.stringify({ packages: installed })
420
+ });
421
+ return response.success && response.data ? response.data : [];
422
+ }
423
+ // ==========================================================================
424
+ // Cache Management
425
+ // ==========================================================================
426
+ /**
427
+ * Clear all cached data (memory and file)
428
+ */
429
+ clearCache() {
430
+ this.memoryCache.clear();
431
+ this.pendingRequests.clear();
432
+ try {
433
+ if (existsSync(this.cacheFile)) {
434
+ unlinkSync(this.cacheFile);
435
+ }
436
+ } catch (error) {
437
+ this.log("Failed to clear file cache:", error);
438
+ }
439
+ this.log("Cache cleared");
440
+ }
441
+ /**
442
+ * Clear expired cache entries
443
+ */
444
+ clearExpiredCache() {
445
+ const now = Date.now();
446
+ const keysToDelete = [];
447
+ this.memoryCache.forEach((entry, key) => {
448
+ if (now - entry.timestamp > entry.ttl) {
449
+ keysToDelete.push(key);
450
+ }
451
+ });
452
+ keysToDelete.forEach((key) => this.memoryCache.delete(key));
453
+ this.log("Cleared", keysToDelete.length, "expired cache entries");
454
+ }
455
+ /**
456
+ * Get cache statistics
457
+ */
458
+ getCacheStats() {
459
+ const fileCache = this.loadFileCache();
460
+ let cacheSize = 0;
461
+ try {
462
+ if (existsSync(this.cacheFile)) {
463
+ cacheSize = statSync(this.cacheFile).size;
464
+ }
465
+ } catch {
466
+ }
467
+ return {
468
+ totalPackages: fileCache?.packages.length || 0,
469
+ cacheSize,
470
+ lastUpdated: fileCache?.lastUpdated || null,
471
+ expiresAt: fileCache?.expiresAt || null,
472
+ isExpired: this.isFileCacheExpired(),
473
+ cachedCategories: fileCache?.categories.length || 0
474
+ };
475
+ }
476
+ /**
477
+ * Set offline mode
478
+ */
479
+ setOfflineMode(enabled) {
480
+ this.offlineMode = enabled;
481
+ this.log("Offline mode:", enabled ? "enabled" : "disabled");
482
+ }
483
+ // ==========================================================================
484
+ // Private Request Methods
485
+ // ==========================================================================
486
+ /**
487
+ * Make an HTTP request with caching, deduplication, and throttling
488
+ */
489
+ async request(endpoint, options = {}) {
490
+ const url = this.buildUrl(endpoint, options.params);
491
+ const cacheKey = `${options.method || "GET"}:${url}`;
492
+ if (!options.skipCache && (options.method === "GET" || !options.method)) {
493
+ const cached = this.getFromMemoryCache(cacheKey);
494
+ if (cached) {
495
+ this.log("Memory cache hit:", cacheKey);
496
+ return {
497
+ success: true,
498
+ data: cached,
499
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
500
+ };
501
+ }
502
+ }
503
+ if (this.enableDeduplication && (options.method === "GET" || !options.method)) {
504
+ const pending = this.pendingRequests.get(cacheKey);
505
+ if (pending) {
506
+ this.log("Deduplicating request:", cacheKey);
507
+ return pending.promise;
508
+ }
509
+ }
510
+ if (this.offlineMode) {
511
+ const cached = this.getFromMemoryCache(cacheKey);
512
+ if (cached) {
513
+ this.log("Offline mode: returning cached data");
514
+ return {
515
+ success: true,
516
+ data: cached,
517
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
518
+ };
519
+ }
520
+ return {
521
+ success: false,
522
+ error: {
523
+ code: "OFFLINE_MODE",
524
+ message: "Offline mode enabled and no cached data available"
525
+ },
526
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
527
+ };
528
+ }
529
+ await this.throttle();
530
+ const requestPromise = this.executeRequest(url, options, cacheKey);
531
+ if (this.enableDeduplication && (options.method === "GET" || !options.method)) {
532
+ this.pendingRequests.set(cacheKey, {
533
+ promise: requestPromise,
534
+ timestamp: Date.now()
535
+ });
536
+ requestPromise.finally(() => {
537
+ this.pendingRequests.delete(cacheKey);
538
+ });
539
+ }
540
+ return requestPromise;
541
+ }
542
+ /**
543
+ * Execute HTTP request with retry logic
544
+ */
545
+ async executeRequest(url, options, cacheKey) {
546
+ let lastError = null;
547
+ for (let attempt = 1; attempt <= this.maxRetries; attempt++) {
548
+ try {
549
+ this.log(`Request attempt ${attempt}/${this.maxRetries}:`, url);
550
+ const response = await this.makeRequest(url, options);
551
+ if (response.success && response.data && (options.method === "GET" || !options.method)) {
552
+ this.setMemoryCache(cacheKey, response.data, this.cacheTTL);
553
+ }
554
+ return response;
555
+ } catch (error) {
556
+ lastError = error instanceof Error ? error : new Error(String(error));
557
+ this.log(`Request failed (attempt ${attempt}):`, lastError.message);
558
+ if (lastError.name === "AbortError") {
559
+ break;
560
+ }
561
+ if (attempt < this.maxRetries) {
562
+ await this.sleep(this.retryDelay * attempt);
563
+ }
564
+ }
565
+ }
566
+ return {
567
+ success: false,
568
+ error: {
569
+ code: "REQUEST_FAILED",
570
+ message: lastError?.message || "Request failed after all retries"
571
+ },
572
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
573
+ };
574
+ }
575
+ /**
576
+ * Make a single HTTP request
577
+ */
578
+ async makeRequest(url, options) {
579
+ const controller = new AbortController();
580
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
581
+ try {
582
+ const response = await fetch(url, {
583
+ method: options.method || "GET",
584
+ headers: this.getHeaders(),
585
+ body: options.body,
586
+ signal: controller.signal
587
+ });
588
+ clearTimeout(timeoutId);
589
+ this.lastRequestTime = Date.now();
590
+ const data = await response.json();
591
+ if (!response.ok) {
592
+ return {
593
+ success: false,
594
+ error: data.error || {
595
+ code: `HTTP_${response.status}`,
596
+ message: `HTTP ${response.status}: ${response.statusText}`
597
+ },
598
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
599
+ };
600
+ }
601
+ return {
602
+ ...data,
603
+ success: true,
604
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
605
+ };
606
+ } catch (error) {
607
+ clearTimeout(timeoutId);
608
+ if (error instanceof Error && error.name === "AbortError") {
609
+ throw error;
610
+ }
611
+ return {
612
+ success: false,
613
+ error: {
614
+ code: "NETWORK_ERROR",
615
+ message: error instanceof Error ? error.message : String(error)
616
+ },
617
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
618
+ };
619
+ }
620
+ }
621
+ // ==========================================================================
622
+ // Helper Methods
623
+ // ==========================================================================
624
+ /**
625
+ * Build full URL with query parameters
626
+ */
627
+ buildUrl(endpoint, params) {
628
+ const url = new URL(endpoint, this.baseUrl);
629
+ if (params) {
630
+ for (const [key, value] of Object.entries(params)) {
631
+ if (value !== void 0 && value !== null) {
632
+ if (Array.isArray(value)) {
633
+ value.forEach((v) => url.searchParams.append(key, String(v)));
634
+ } else {
635
+ url.searchParams.append(key, String(value));
636
+ }
637
+ }
638
+ }
639
+ }
640
+ return url.toString();
641
+ }
642
+ /**
643
+ * Build search parameters from options
644
+ */
645
+ buildSearchParams(options) {
646
+ return {
647
+ q: options.query,
648
+ category: options.category,
649
+ tags: options.tags,
650
+ sortBy: options.sortBy,
651
+ sortOrder: options.sortOrder,
652
+ verified: options.verified,
653
+ verificationStatus: options.verificationStatus,
654
+ author: options.author,
655
+ platform: options.platform,
656
+ codeTool: options.codeTool,
657
+ minRating: options.minRating,
658
+ page: options.page || 1,
659
+ limit: options.limit || 20
660
+ };
661
+ }
662
+ /**
663
+ * Get request headers
664
+ */
665
+ getHeaders() {
666
+ const headers = {
667
+ "Content-Type": "application/json",
668
+ "User-Agent": "CCJK-MCP-Marketplace-Client/1.0"
669
+ };
670
+ if (this.apiKey) {
671
+ headers.Authorization = `Bearer ${this.apiKey}`;
672
+ }
673
+ return headers;
674
+ }
675
+ /**
676
+ * Throttle requests
677
+ */
678
+ async throttle() {
679
+ const now = Date.now();
680
+ const elapsed = now - this.lastRequestTime;
681
+ const remaining = this.throttleInterval - elapsed;
682
+ if (remaining > 0) {
683
+ await this.sleep(remaining);
684
+ }
685
+ }
686
+ /**
687
+ * Sleep for specified milliseconds
688
+ */
689
+ sleep(ms) {
690
+ return new Promise((resolve) => setTimeout(resolve, ms));
691
+ }
692
+ /**
693
+ * Log message (if logging is enabled)
694
+ */
695
+ log(...args) {
696
+ if (this.enableLogging) {
697
+ console.log("[MarketplaceClient]", ...args);
698
+ }
699
+ }
700
+ // ==========================================================================
701
+ // Memory Cache Methods
702
+ // ==========================================================================
703
+ /**
704
+ * Get data from memory cache
705
+ */
706
+ getFromMemoryCache(key) {
707
+ const entry = this.memoryCache.get(key);
708
+ if (!entry) {
709
+ return null;
710
+ }
711
+ const now = Date.now();
712
+ if (now - entry.timestamp > entry.ttl) {
713
+ this.memoryCache.delete(key);
714
+ return null;
715
+ }
716
+ return entry.data;
717
+ }
718
+ /**
719
+ * Set data in memory cache
720
+ */
721
+ setMemoryCache(key, data, ttl) {
722
+ this.memoryCache.set(key, {
723
+ data,
724
+ timestamp: Date.now(),
725
+ ttl
726
+ });
727
+ }
728
+ // ==========================================================================
729
+ // File Cache Methods
730
+ // ==========================================================================
731
+ /**
732
+ * Ensure cache directory exists
733
+ */
734
+ ensureCacheDir() {
735
+ if (!existsSync(this.cacheDir)) {
736
+ mkdirSync(this.cacheDir, { recursive: true });
737
+ }
738
+ }
739
+ /**
740
+ * Load cache from file
741
+ */
742
+ loadFileCache() {
743
+ try {
744
+ if (!existsSync(this.cacheFile)) {
745
+ return null;
746
+ }
747
+ const content = readFileSync(this.cacheFile, "utf-8");
748
+ const cache = JSON.parse(content);
749
+ if (cache.version !== CACHE_VERSION) {
750
+ return null;
751
+ }
752
+ return cache;
753
+ } catch {
754
+ return null;
755
+ }
756
+ }
757
+ /**
758
+ * Save cache to file
759
+ */
760
+ saveFileCache(packages, categories) {
761
+ try {
762
+ this.ensureCacheDir();
763
+ const now = (/* @__PURE__ */ new Date()).toISOString();
764
+ const expiresAt = new Date(Date.now() + this.cacheTTL).toISOString();
765
+ const cache = {
766
+ version: CACHE_VERSION,
767
+ packages,
768
+ categories,
769
+ createdAt: now,
770
+ expiresAt,
771
+ lastUpdated: now
772
+ };
773
+ writeFileAtomic(this.cacheFile, JSON.stringify(cache, null, 2), "utf-8");
774
+ } catch (error) {
775
+ this.log("Failed to save file cache:", error);
776
+ }
777
+ }
778
+ /**
779
+ * Check if file cache is expired
780
+ */
781
+ isFileCacheExpired() {
782
+ const cache = this.loadFileCache();
783
+ if (!cache) {
784
+ return true;
785
+ }
786
+ const expiresAt = new Date(cache.expiresAt).getTime();
787
+ return Date.now() >= expiresAt;
788
+ }
789
+ }
790
+ let defaultClientInstance = null;
791
+ function getDefaultMarketplaceClient() {
792
+ if (!defaultClientInstance) {
793
+ defaultClientInstance = new MarketplaceClient();
794
+ }
795
+ return defaultClientInstance;
796
+ }
797
+
798
+ function readClaudeConfig(configPath = SETTINGS_FILE) {
799
+ try {
800
+ if (!exists(configPath)) {
801
+ return null;
802
+ }
803
+ return readJsonConfig(configPath) || null;
804
+ } catch (error) {
805
+ console.error(`Failed to read Claude config from ${configPath}:`, error);
806
+ return null;
807
+ }
808
+ }
809
+ function writeClaudeConfig(config, options = {}, configPath = SETTINGS_FILE) {
810
+ try {
811
+ ensureDir(CLAUDE_DIR);
812
+ writeJsonConfig(configPath, config, {
813
+ atomic: options.atomic !== false,
814
+ pretty: true,
815
+ backup: options.backup
816
+ });
817
+ } catch (error) {
818
+ console.error(`Failed to write Claude config to ${configPath}:`, error);
819
+ throw new Error(`Failed to write Claude config: ${error instanceof Error ? error.message : String(error)}`);
820
+ }
821
+ }
822
+ function updateClaudeConfig(updates, options = {}, configPath = SETTINGS_FILE) {
823
+ const existingConfig = readClaudeConfig(configPath) || {};
824
+ const mergedConfig = deepMerge(existingConfig, updates, {
825
+ mergeArrays: options.merge === "preserve",
826
+ arrayMergeStrategy: "unique"
827
+ });
828
+ writeClaudeConfig(mergedConfig, options, configPath);
829
+ return mergedConfig;
830
+ }
831
+
832
+ const claudeConfig = {
833
+ __proto__: null,
834
+ readClaudeConfig: readClaudeConfig,
835
+ updateClaudeConfig: updateClaudeConfig,
836
+ writeClaudeConfig: writeClaudeConfig
837
+ };
838
+
839
+ function detectActiveTool() {
840
+ const hasClaudeConfig = exists(ClAUDE_CONFIG_FILE);
841
+ const hasCodexConfig = exists(CODEX_CONFIG_FILE);
842
+ if (hasClaudeConfig) {
843
+ return "claude-code";
844
+ }
845
+ if (hasCodexConfig) {
846
+ return "codex";
847
+ }
848
+ return "claude-code";
849
+ }
850
+ async function installMcpService(serviceId, tool, apiKey) {
851
+ ensureI18nInitialized();
852
+ const service = await getMcpService(serviceId);
853
+ if (!service) {
854
+ return {
855
+ success: false,
856
+ serviceId,
857
+ serviceName: serviceId,
858
+ error: i18n.t("mcp:installer.serviceNotFound", { id: serviceId })
859
+ };
860
+ }
861
+ if (service.requiresApiKey && !apiKey) {
862
+ const promptMessage = service.apiKeyPrompt || i18n.t("mcp:apiKeyPrompt");
863
+ const { inputApiKey } = await inquirer.prompt([{
864
+ type: "input",
865
+ name: "inputApiKey",
866
+ message: promptMessage,
867
+ validate: (input) => !!input || i18n.t("api:keyRequired")
868
+ }]);
869
+ if (!inputApiKey) {
870
+ return {
871
+ success: false,
872
+ serviceId,
873
+ serviceName: service.name,
874
+ error: i18n.t("mcp:installer.apiKeyRequired")
875
+ };
876
+ }
877
+ apiKey = inputApiKey;
878
+ }
879
+ const targetTool = tool || detectActiveTool();
880
+ try {
881
+ if (targetTool === "codex") {
882
+ await installMcpServiceForCodex(serviceId, service.config, apiKey, service.apiKeyEnvVar);
883
+ } else {
884
+ await installMcpServiceForClaudeCode(serviceId, service.config, apiKey, service.apiKeyEnvVar);
885
+ }
886
+ return {
887
+ success: true,
888
+ serviceId,
889
+ serviceName: service.name
890
+ };
891
+ } catch (error) {
892
+ return {
893
+ success: false,
894
+ serviceId,
895
+ serviceName: service.name,
896
+ error: error instanceof Error ? error.message : String(error)
897
+ };
898
+ }
899
+ }
900
+ async function installMcpServiceForClaudeCode(serviceId, baseConfig, apiKey, apiKeyEnvVar) {
901
+ let config = readMcpConfig();
902
+ if (!config) {
903
+ config = { mcpServers: {} };
904
+ }
905
+ const serverConfig = buildMcpServerConfig(
906
+ baseConfig,
907
+ apiKey,
908
+ apiKeyEnvVar ? `YOUR_${apiKeyEnvVar}` : "YOUR_API_KEY",
909
+ apiKeyEnvVar
910
+ );
911
+ if (!config.mcpServers) {
912
+ config.mcpServers = {};
913
+ }
914
+ config.mcpServers[serviceId] = serverConfig;
915
+ writeMcpConfig(config);
916
+ await autoAuthorizeMcpService(serviceId);
917
+ }
918
+ async function installMcpServiceForCodex(serviceId, baseConfig, apiKey, apiKeyEnvVar) {
919
+ const existingConfig = readCodexConfig();
920
+ let command = baseConfig.command || serviceId;
921
+ let args = (baseConfig.args || []).map((arg) => String(arg));
922
+ if (serviceId === "serena") {
923
+ const idx = args.indexOf("--context");
924
+ if (idx >= 0 && idx + 1 < args.length) {
925
+ args[idx + 1] = "codex";
926
+ } else {
927
+ args.push("--context", "codex");
928
+ }
929
+ }
930
+ const serviceConfig = { id: serviceId.toLowerCase(), command, args };
931
+ applyCodexPlatformCommand(serviceConfig);
932
+ command = serviceConfig.command;
933
+ args = serviceConfig.args || [];
934
+ const env = { ...baseConfig.env || {} };
935
+ if (isWindows()) {
936
+ const systemRoot = getSystemRoot();
937
+ if (systemRoot) {
938
+ env.SYSTEMROOT = systemRoot;
939
+ }
940
+ }
941
+ if (apiKey && apiKeyEnvVar) {
942
+ env[apiKeyEnvVar] = apiKey;
943
+ }
944
+ const newService = {
945
+ id: serviceId.toLowerCase(),
946
+ command,
947
+ args,
948
+ env: Object.keys(env).length > 0 ? env : void 0,
949
+ startup_timeout_sec: 30
950
+ };
951
+ const existingServices = existingConfig?.mcpServices || [];
952
+ const mergedMap = /* @__PURE__ */ new Map();
953
+ for (const svc of existingServices) {
954
+ mergedMap.set(svc.id.toLowerCase(), { ...svc });
955
+ }
956
+ mergedMap.set(newService.id, newService);
957
+ const finalServices = Array.from(mergedMap.values());
958
+ const configData = {
959
+ model: existingConfig?.model || null,
960
+ modelProvider: existingConfig?.modelProvider || null,
961
+ providers: existingConfig?.providers || [],
962
+ mcpServices: finalServices,
963
+ otherConfig: existingConfig?.otherConfig || []
964
+ };
965
+ writeCodexConfig(configData);
966
+ }
967
+ async function uninstallMcpService(serviceId, tool) {
968
+ ensureI18nInitialized();
969
+ const targetTool = tool || detectActiveTool();
970
+ try {
971
+ if (targetTool === "codex") {
972
+ await uninstallMcpServiceFromCodex(serviceId);
973
+ } else {
974
+ await uninstallMcpServiceFromClaudeCode(serviceId);
975
+ }
976
+ return {
977
+ success: true,
978
+ serviceId
979
+ };
980
+ } catch (error) {
981
+ return {
982
+ success: false,
983
+ serviceId,
984
+ error: error instanceof Error ? error.message : String(error)
985
+ };
986
+ }
987
+ }
988
+ async function uninstallMcpServiceFromClaudeCode(serviceId) {
989
+ const config = readMcpConfig();
990
+ if (!config || !config.mcpServers) {
991
+ throw new Error(i18n.t("mcp:installer.noConfig"));
992
+ }
993
+ const normalizedId = serviceId.toLowerCase();
994
+ const existingKey = Object.keys(config.mcpServers).find(
995
+ (key) => key.toLowerCase() === normalizedId
996
+ );
997
+ if (!existingKey) {
998
+ throw new Error(i18n.t("mcp:installer.serviceNotInstalled", { id: serviceId }));
999
+ }
1000
+ delete config.mcpServers[existingKey];
1001
+ writeMcpConfig(config);
1002
+ await removeAuthorizeMcpService(serviceId);
1003
+ }
1004
+ async function uninstallMcpServiceFromCodex(serviceId) {
1005
+ const existingConfig = readCodexConfig();
1006
+ if (!existingConfig || !existingConfig.mcpServices) {
1007
+ throw new Error(i18n.t("mcp:installer.noConfig"));
1008
+ }
1009
+ const normalizedId = serviceId.toLowerCase();
1010
+ const serviceIndex = existingConfig.mcpServices.findIndex(
1011
+ (svc) => svc.id.toLowerCase() === normalizedId
1012
+ );
1013
+ if (serviceIndex === -1) {
1014
+ throw new Error(i18n.t("mcp:installer.serviceNotInstalled", { id: serviceId }));
1015
+ }
1016
+ existingConfig.mcpServices.splice(serviceIndex, 1);
1017
+ writeCodexConfig(existingConfig);
1018
+ }
1019
+ async function listInstalledMcpServices(tool) {
1020
+ ensureI18nInitialized();
1021
+ const targetTool = tool || detectActiveTool();
1022
+ if (targetTool === "codex") {
1023
+ return listInstalledMcpServicesFromCodex();
1024
+ } else {
1025
+ return listInstalledMcpServicesFromClaudeCode();
1026
+ }
1027
+ }
1028
+ function listInstalledMcpServicesFromClaudeCode() {
1029
+ const config = readMcpConfig();
1030
+ if (!config || !config.mcpServers) {
1031
+ return [];
1032
+ }
1033
+ const services = [];
1034
+ for (const [id, serverConfig] of Object.entries(config.mcpServers)) {
1035
+ const knownService = MCP_SERVICE_CONFIGS.find(
1036
+ (s) => s.id.toLowerCase() === id.toLowerCase()
1037
+ );
1038
+ services.push({
1039
+ id,
1040
+ name: knownService?.id || id,
1041
+ command: serverConfig.command,
1042
+ args: serverConfig.args,
1043
+ url: serverConfig.url,
1044
+ type: serverConfig.type || "stdio"
1045
+ });
1046
+ }
1047
+ return services;
1048
+ }
1049
+ function listInstalledMcpServicesFromCodex() {
1050
+ const config = readCodexConfig();
1051
+ if (!config || !config.mcpServices) {
1052
+ return [];
1053
+ }
1054
+ const services = [];
1055
+ for (const svc of config.mcpServices) {
1056
+ const knownService = MCP_SERVICE_CONFIGS.find(
1057
+ (s) => s.id.toLowerCase() === svc.id.toLowerCase()
1058
+ );
1059
+ services.push({
1060
+ id: svc.id,
1061
+ name: knownService?.id || svc.id,
1062
+ command: svc.command,
1063
+ args: svc.args,
1064
+ type: "stdio"
1065
+ });
1066
+ }
1067
+ return services;
1068
+ }
1069
+ async function isMcpServiceInstalled(serviceId, tool) {
1070
+ const installedServices = await listInstalledMcpServices(tool);
1071
+ const normalizedId = serviceId.toLowerCase();
1072
+ return installedServices.some((svc) => svc.id.toLowerCase() === normalizedId);
1073
+ }
1074
+ async function displayInstalledMcpServices(tool) {
1075
+ ensureI18nInitialized();
1076
+ const targetTool = tool || detectActiveTool();
1077
+ const services = await listInstalledMcpServices(targetTool);
1078
+ if (services.length === 0) {
1079
+ console.log(ansis.yellow(i18n.t("mcp:installer.noServicesInstalled")));
1080
+ return;
1081
+ }
1082
+ console.log(ansis.green.bold(`
1083
+ ${i18n.t("mcp:installer.installedServices", { tool: targetTool })}
1084
+ `));
1085
+ services.forEach((service, idx) => {
1086
+ console.log(`${ansis.green(`${idx + 1}.`)} ${ansis.bold(service.name)} ${ansis.dim(`[${service.id}]`)}`);
1087
+ if (service.command) {
1088
+ console.log(` ${ansis.dim(`Command: ${service.command}`)}`);
1089
+ }
1090
+ if (service.url) {
1091
+ console.log(` ${ansis.dim(`URL: ${service.url}`)}`);
1092
+ }
1093
+ console.log("");
1094
+ });
1095
+ }
1096
+ async function autoAuthorizeMcpService(serviceId) {
1097
+ const mcpPermission = `mcp__${serviceId.toLowerCase().replace(/-/g, "_")}`;
1098
+ const { readClaudeConfig } = await Promise.resolve().then(function () { return claudeConfig; });
1099
+ const currentSettings = readClaudeConfig() || {};
1100
+ if (!currentSettings.permissions) {
1101
+ currentSettings.permissions = {};
1102
+ }
1103
+ if (!currentSettings.permissions.allow) {
1104
+ currentSettings.permissions.allow = [];
1105
+ }
1106
+ if (!currentSettings.permissions.allow.includes(mcpPermission)) {
1107
+ currentSettings.permissions.allow.push(mcpPermission);
1108
+ updateClaudeConfig({
1109
+ permissions: currentSettings.permissions
1110
+ });
1111
+ }
1112
+ }
1113
+ async function removeAuthorizeMcpService(serviceId) {
1114
+ const mcpPermission = `mcp__${serviceId.toLowerCase().replace(/-/g, "_")}`;
1115
+ const { readClaudeConfig } = await Promise.resolve().then(function () { return claudeConfig; });
1116
+ const currentSettings = readClaudeConfig() || {};
1117
+ if (currentSettings.permissions?.allow) {
1118
+ const index = currentSettings.permissions.allow.indexOf(mcpPermission);
1119
+ if (index !== -1) {
1120
+ currentSettings.permissions.allow.splice(index, 1);
1121
+ updateClaudeConfig({
1122
+ permissions: currentSettings.permissions
1123
+ });
1124
+ }
1125
+ }
1126
+ }
1127
+
1128
+ function getLocalFallbackServices() {
1129
+ return [
1130
+ // CCJK managed services (from mcp-services config)
1131
+ ...MCP_SERVICE_CONFIGS.map((svc) => ({
1132
+ name: svc.id,
1133
+ description: svc.id,
1134
+ // Will be replaced with i18n
1135
+ package: svc.config.command || svc.id,
1136
+ category: "ccjk",
1137
+ serviceId: svc.id,
1138
+ requiresApiKey: svc.requiresApiKey
1139
+ })),
1140
+ // External MCP servers from Awesome MCP Servers (fallback)
1141
+ { name: "Filesystem", description: "Secure file operations", package: "@modelcontextprotocol/server-filesystem", category: "core" },
1142
+ { name: "GitHub", description: "Repository management", package: "@modelcontextprotocol/server-github", category: "dev" },
1143
+ { name: "PostgreSQL", description: "Database operations", package: "@modelcontextprotocol/server-postgres", category: "database" },
1144
+ { name: "Puppeteer", description: "Browser automation", package: "@modelcontextprotocol/server-puppeteer", category: "automation" },
1145
+ { name: "Brave Search", description: "Web search", package: "@modelcontextprotocol/server-brave-search", category: "search" },
1146
+ { name: "Google Maps", description: "Location services", package: "@modelcontextprotocol/server-google-maps", category: "api" },
1147
+ { name: "Slack", description: "Team communication", package: "@modelcontextprotocol/server-slack", category: "communication" },
1148
+ { name: "Memory", description: "Knowledge graph", package: "@modelcontextprotocol/server-memory", category: "ai" }
1149
+ ];
1150
+ }
1151
+ function convertToMcpServer(pkg) {
1152
+ const lang = i18n.language;
1153
+ return {
1154
+ name: pkg.name,
1155
+ description: pkg.description[lang] || pkg.description.en,
1156
+ package: pkg.id,
1157
+ category: pkg.category,
1158
+ stars: pkg.rating,
1159
+ serviceId: pkg.id,
1160
+ requiresApiKey: pkg.permissions.some((p) => p.type === "env")
1161
+ };
1162
+ }
1163
+ async function mcpSearch(keyword, options = {}) {
1164
+ const client = getDefaultMarketplaceClient();
1165
+ try {
1166
+ const searchOptions = {
1167
+ query: keyword,
1168
+ category: options.category,
1169
+ verified: options.verified,
1170
+ sortBy: options.sortBy || "relevance",
1171
+ limit: options.limit || 50
1172
+ };
1173
+ const result = await client.search(searchOptions);
1174
+ if (result.packages.length === 0) {
1175
+ console.log(ansis.yellow(`
1176
+ ${i18n.t("mcp:market.noResults", { keyword })}`));
1177
+ return;
1178
+ }
1179
+ console.log(ansis.green.bold(`
1180
+ ${i18n.t("mcp:market.searchResults", { count: result.total, keyword })}
1181
+ `));
1182
+ result.packages.forEach((pkg, idx) => {
1183
+ const lang = i18n.language;
1184
+ const verifiedBadge = pkg.verified ? ansis.green("\u2713") : ansis.dim("\u25CB");
1185
+ console.log(`${ansis.green(`${idx + 1}.`)} ${ansis.bold(pkg.name)} ${verifiedBadge} ${ansis.dim(`[${pkg.category}]`)}`);
1186
+ console.log(` ${pkg.description[lang] || pkg.description.en}`);
1187
+ console.log(` ${ansis.dim(`\u{1F4E5} ${pkg.downloads.toLocaleString()} | \u2B50 ${pkg.rating.toFixed(1)}/5.0`)}`);
1188
+ console.log(` ${ansis.dim(pkg.id)}
1189
+ `);
1190
+ });
1191
+ if (result.hasMore) {
1192
+ console.log(ansis.dim(`
1193
+ ${i18n.t("mcp:market.moreResults", { total: result.total, shown: result.packages.length })}`));
1194
+ }
1195
+ } catch {
1196
+ console.log(ansis.yellow(`
1197
+ ${i18n.t("mcp:market.apiUnavailable")}`));
1198
+ console.log(ansis.dim(i18n.t("mcp:market.usingLocalData")));
1199
+ const localServers = getLocalFallbackServices();
1200
+ const results = localServers.filter(
1201
+ (s) => s.name.toLowerCase().includes(keyword.toLowerCase()) || s.description.toLowerCase().includes(keyword.toLowerCase()) || s.category.toLowerCase().includes(keyword.toLowerCase())
1202
+ );
1203
+ if (results.length === 0) {
1204
+ console.log(ansis.yellow(`
1205
+ ${i18n.t("mcp:market.noResults", { keyword })}`));
1206
+ return;
1207
+ }
1208
+ console.log(ansis.green.bold(`
1209
+ ${i18n.t("mcp:market.searchResults", { count: results.length, keyword })}
1210
+ `));
1211
+ results.forEach((server, idx) => {
1212
+ console.log(`${ansis.green(`${idx + 1}.`)} ${ansis.bold(server.name)} ${ansis.dim(`[${server.category}]`)}`);
1213
+ console.log(` ${server.description}`);
1214
+ console.log(` ${ansis.dim(server.package)}
1215
+ `);
1216
+ });
1217
+ }
1218
+ }
1219
+ async function mcpInstall(serverName, options = {}) {
1220
+ const client = getDefaultMarketplaceClient();
1221
+ let server = null;
1222
+ const localServers = getLocalFallbackServices();
1223
+ server = localServers.find((s) => s.name.toLowerCase() === serverName.toLowerCase()) || null;
1224
+ if (!server) {
1225
+ try {
1226
+ const pkg = await client.getPackage(serverName);
1227
+ if (pkg) {
1228
+ server = convertToMcpServer(pkg);
1229
+ }
1230
+ } catch {
1231
+ }
1232
+ }
1233
+ if (!server) {
1234
+ console.log(ansis.red(`
1235
+ ${i18n.t("mcp:market.serverNotFound", { name: serverName })}`));
1236
+ return;
1237
+ }
1238
+ if (server.serviceId) {
1239
+ const isInstalled = await isMcpServiceInstalled(server.serviceId, options.tool);
1240
+ if (isInstalled) {
1241
+ console.log(ansis.yellow(`
1242
+ ${i18n.t("mcp:installer.alreadyInstalled", { name: server.name })}`));
1243
+ return;
1244
+ }
1245
+ console.log(ansis.green(`
1246
+ ${i18n.t("mcp:market.installing", { name: server.name })}`));
1247
+ if (server.requiresApiKey) {
1248
+ console.log(ansis.dim(i18n.t("mcp:installer.requiresApiKey")));
1249
+ }
1250
+ console.log("");
1251
+ const { confirm } = await inquirer.prompt([{
1252
+ type: "confirm",
1253
+ name: "confirm",
1254
+ message: i18n.t("mcp:market.confirmInstall"),
1255
+ default: true
1256
+ }]);
1257
+ if (!confirm) {
1258
+ console.log(ansis.yellow(i18n.t("mcp:market.cancelled")));
1259
+ return;
1260
+ }
1261
+ const result = await installMcpService(server.serviceId, options.tool);
1262
+ if (result.success) {
1263
+ console.log(ansis.green(`
1264
+ ${i18n.t("mcp:market.installSuccess", { name: server.name })}`));
1265
+ console.log(ansis.dim(i18n.t("mcp:installer.restartRequired")));
1266
+ } else {
1267
+ console.log(ansis.red(`
1268
+ ${i18n.t("mcp:installer.installFailed", { name: server.name })}`));
1269
+ if (result.error) {
1270
+ console.log(ansis.dim(result.error));
1271
+ }
1272
+ }
1273
+ } else {
1274
+ console.log(ansis.green(`
1275
+ ${i18n.t("mcp:market.installing", { name: server.name })}`));
1276
+ console.log(ansis.dim(`Package: ${server.package}
1277
+ `));
1278
+ const { confirm } = await inquirer.prompt([{
1279
+ type: "confirm",
1280
+ name: "confirm",
1281
+ message: i18n.t("mcp:market.confirmInstall"),
1282
+ default: true
1283
+ }]);
1284
+ if (!confirm) {
1285
+ console.log(ansis.yellow(i18n.t("mcp:market.cancelled")));
1286
+ return;
1287
+ }
1288
+ console.log(ansis.green(`
1289
+ ${i18n.t("mcp:market.installSuccess", { name: server.name })}`));
1290
+ console.log(ansis.dim(i18n.t("mcp:market.manualConfig")));
1291
+ }
1292
+ }
1293
+ async function mcpUninstall(serverName, options = {}) {
1294
+ const localServers = getLocalFallbackServices();
1295
+ const server = localServers.find((s) => s.name.toLowerCase() === serverName.toLowerCase());
1296
+ const serviceId = server?.serviceId || serverName;
1297
+ const isInstalled = await isMcpServiceInstalled(serviceId, options.tool);
1298
+ if (!isInstalled) {
1299
+ console.log(ansis.yellow(`
1300
+ ${i18n.t("mcp:installer.serviceNotInstalled", { id: serverName })}`));
1301
+ return;
1302
+ }
1303
+ const displayName = server?.name || serverName;
1304
+ const { confirm } = await inquirer.prompt([{
1305
+ type: "confirm",
1306
+ name: "confirm",
1307
+ message: i18n.t("mcp:market.confirmUninstall", { name: displayName }),
1308
+ default: false
1309
+ }]);
1310
+ if (!confirm) {
1311
+ console.log(ansis.yellow(i18n.t("mcp:market.cancelled")));
1312
+ return;
1313
+ }
1314
+ const result = await uninstallMcpService(serviceId, options.tool);
1315
+ if (result.success) {
1316
+ console.log(ansis.green(`
1317
+ ${i18n.t("mcp:installer.uninstallSuccess", { name: displayName })}`));
1318
+ console.log(ansis.dim(i18n.t("mcp:installer.restartRequired")));
1319
+ } else {
1320
+ console.log(ansis.red(`
1321
+ ${i18n.t("mcp:installer.uninstallFailed", { name: displayName })}`));
1322
+ if (result.error) {
1323
+ console.log(ansis.dim(result.error));
1324
+ }
1325
+ }
1326
+ }
1327
+ async function mcpList(options = {}) {
1328
+ await displayInstalledMcpServices(options.tool);
1329
+ }
1330
+
290
1331
  const MCP_PROFILES = [
291
1332
  {
292
1333
  id: "minimal",
@@ -502,4 +1543,4 @@ function mcpHelp(options = {}) {
502
1543
  console.log(ansis.dim(isZh ? "\u{1F4A1} \u63A8\u8350: \u4F7F\u7528 minimal \u9884\u8BBE\u53EF\u663E\u8457\u63D0\u5347\u6027\u80FD" : "\u{1F4A1} Tip: Use minimal profile for best performance"));
503
1544
  }
504
1545
 
505
- export { listProfiles, mcpDoctor, mcpHelp, mcpRelease, mcpStatus, useProfile };
1546
+ export { listProfiles, mcpDoctor, mcpHelp, mcpInstall, mcpList, mcpRelease, mcpSearch, mcpStatus, mcpUninstall, useProfile };