fraim-hub 2.0.204

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 (120) hide show
  1. package/README.md +467 -0
  2. package/bin/fraim-hub.js +17 -0
  3. package/dist/src/ai-hub/brand-store.js +17 -0
  4. package/dist/src/ai-hub/catalog.js +412 -0
  5. package/dist/src/ai-hub/cert-store.js +70 -0
  6. package/dist/src/ai-hub/cli.js +212 -0
  7. package/dist/src/ai-hub/configured-agents.js +283 -0
  8. package/dist/src/ai-hub/conversation-store.js +428 -0
  9. package/dist/src/ai-hub/desktop-main.js +449 -0
  10. package/dist/src/ai-hub/hosts.js +1669 -0
  11. package/dist/src/ai-hub/hub-latest-version.js +52 -0
  12. package/dist/src/ai-hub/hub-launch-decision.js +57 -0
  13. package/dist/src/ai-hub/hub-runtime-file.js +43 -0
  14. package/dist/src/ai-hub/managed-browser.js +269 -0
  15. package/dist/src/ai-hub/manager-turns.js +64 -0
  16. package/dist/src/ai-hub/office-sideload.js +156 -0
  17. package/dist/src/ai-hub/openclaw-bridge.js +250 -0
  18. package/dist/src/ai-hub/preferences.js +201 -0
  19. package/dist/src/ai-hub/remote-hub-gateway.js +88 -0
  20. package/dist/src/ai-hub/server.js +4013 -0
  21. package/dist/src/ai-hub/types.js +2 -0
  22. package/dist/src/ai-hub/url-safety.js +19 -0
  23. package/dist/src/api/admin/payments.js +33 -0
  24. package/dist/src/api/admin/sales-leads.js +21 -0
  25. package/dist/src/api/payment/create-session.js +338 -0
  26. package/dist/src/api/payment/dashboard-link.js +149 -0
  27. package/dist/src/api/payment/session-details.js +31 -0
  28. package/dist/src/api/payment/webhook.js +587 -0
  29. package/dist/src/api/sales/contact.js +44 -0
  30. package/dist/src/cli/api/get-provider-client.js +41 -0
  31. package/dist/src/cli/api/provider-client.js +107 -0
  32. package/dist/src/cli/commands/add-ide.js +462 -0
  33. package/dist/src/cli/fraim-hub.js +50 -0
  34. package/dist/src/cli/internal/device-flow-service.js +83 -0
  35. package/dist/src/cli/mcp/command-resolution.js +81 -0
  36. package/dist/src/cli/mcp/fraim-mcp-latest-launcher.js +136 -0
  37. package/dist/src/cli/mcp/ide-formats.js +317 -0
  38. package/dist/src/cli/mcp/mcp-server-builder.js +48 -0
  39. package/dist/src/cli/mcp/mcp-server-registry.js +173 -0
  40. package/dist/src/cli/providers/local-provider-registry.js +165 -0
  41. package/dist/src/cli/providers/provider-registry.js +230 -0
  42. package/dist/src/cli/setup/claude-code-telemetry.js +59 -0
  43. package/dist/src/cli/setup/codex-local-config.js +37 -0
  44. package/dist/src/cli/setup/ide-detector.js +383 -0
  45. package/dist/src/cli/setup/ide-global-integration.js +100 -0
  46. package/dist/src/cli/setup/ide-invocation-surfaces.js +160 -0
  47. package/dist/src/cli/setup/mcp-config-generator.js +225 -0
  48. package/dist/src/cli/setup/provider-prompts.js +339 -0
  49. package/dist/src/cli/utils/managed-agent-paths.js +118 -0
  50. package/dist/src/cli/utils/script-sync-utils.js +221 -0
  51. package/dist/src/cli/utils/user-config.js +100 -0
  52. package/dist/src/cli/utils/version-utils.js +35 -0
  53. package/dist/src/config/ai-manager-hiring.js +121 -0
  54. package/dist/src/config/feature-flags.js +25 -0
  55. package/dist/src/config/persona-capability-bundles.js +277 -0
  56. package/dist/src/config/persona-hiring.js +270 -0
  57. package/dist/src/config/portfolio-slug-overrides.js +17 -0
  58. package/dist/src/config/pricing.js +37 -0
  59. package/dist/src/config/stripe.js +43 -0
  60. package/dist/src/core/brand-store.js +232 -0
  61. package/dist/src/core/quality-evidence.js +331 -0
  62. package/dist/src/core/utils/git-utils.js +194 -0
  63. package/dist/src/core/utils/ports.js +32 -0
  64. package/dist/src/core/utils/project-fraim-paths.js +220 -0
  65. package/dist/src/db/payment-repository.js +61 -0
  66. package/dist/src/first-run/types.js +93 -0
  67. package/dist/src/fraim/db-service.js +2409 -0
  68. package/dist/src/local-mcp-server/agent-token-prices.js +167 -0
  69. package/dist/src/local-mcp-server/learning-context-builder.js +975 -0
  70. package/dist/src/middleware/rate-limit.js +110 -0
  71. package/dist/src/models/payment.js +2 -0
  72. package/dist/src/routes/payment-routes.js +186 -0
  73. package/dist/src/routes/persona-catalog-routes.js +84 -0
  74. package/dist/src/services/dashboard-access.js +27 -0
  75. package/dist/src/services/email-service.js +951 -0
  76. package/dist/src/services/persona-entitlement-service.js +352 -0
  77. package/dist/src/services/workspace-identity.js +21 -0
  78. package/dist/src/types/analytics.js +2 -0
  79. package/dist/src/utils/payment-calculator.js +52 -0
  80. package/extensions/office-word/favicon.ico +0 -0
  81. package/extensions/office-word/icon-64.png +0 -0
  82. package/extensions/office-word/manifest.xml +33 -0
  83. package/extensions/office-word/taskpane.html +242 -0
  84. package/index.js +85 -0
  85. package/package.json +106 -0
  86. package/public/ai-hub/index.html +910 -0
  87. package/public/ai-hub/powerpoint-taskpane/icon-64.png +0 -0
  88. package/public/ai-hub/powerpoint-taskpane/index.html +236 -0
  89. package/public/ai-hub/powerpoint-taskpane/manifest.xml +30 -0
  90. package/public/ai-hub/review.css +444 -0
  91. package/public/ai-hub/script.js +11737 -0
  92. package/public/ai-hub/styles.css +5102 -0
  93. package/public/first-run/error-frame.js +100 -0
  94. package/public/first-run/index.html +35 -0
  95. package/public/first-run/script.js +739 -0
  96. package/public/first-run/styles.css +929 -0
  97. package/public/portfolio/ashley.html +523 -0
  98. package/public/portfolio/auditya.html +83 -0
  99. package/public/portfolio/banke.html +83 -0
  100. package/public/portfolio/beza.html +659 -0
  101. package/public/portfolio/careena.html +632 -0
  102. package/public/portfolio/casey.html +568 -0
  103. package/public/portfolio/celia.html +490 -0
  104. package/public/portfolio/deidre.html +642 -0
  105. package/public/portfolio/gautam.html +597 -0
  106. package/public/portfolio/hari.html +469 -0
  107. package/public/portfolio/huxley.html +1354 -0
  108. package/public/portfolio/index.html +741 -0
  109. package/public/portfolio/maestro.html +518 -0
  110. package/public/portfolio/mandy.html +590 -0
  111. package/public/portfolio/mona.html +597 -0
  112. package/public/portfolio/pam.html +887 -0
  113. package/public/portfolio/procella.html +107 -0
  114. package/public/portfolio/qasm.html +569 -0
  115. package/public/portfolio/ricardo.html +489 -0
  116. package/public/portfolio/sade.html +560 -0
  117. package/public/portfolio/sam.html +654 -0
  118. package/public/portfolio/sechar.html +580 -0
  119. package/public/portfolio/sreya.html +599 -0
  120. package/public/portfolio/swen.html +601 -0
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.DeviceFlowService = void 0;
7
+ const axios_1 = __importDefault(require("axios"));
8
+ const chalk_1 = __importDefault(require("chalk"));
9
+ class DeviceFlowService {
10
+ constructor(config) {
11
+ this.config = config;
12
+ }
13
+ /**
14
+ * Start the Device Flow Login
15
+ */
16
+ async login() {
17
+ console.log(chalk_1.default.blue('\n🔗 Starting Authentication...'));
18
+ try {
19
+ // 1. Request device and user codes
20
+ const deviceCode = await this.requestDeviceCode();
21
+ console.log(chalk_1.default.yellow('\nACTION REQUIRED:'));
22
+ console.log(`1. Go to: ${chalk_1.default.cyan.underline(deviceCode.verification_uri)}`);
23
+ console.log(`2. Enter the code: ${chalk_1.default.bold.green(deviceCode.user_code)}`);
24
+ console.log(chalk_1.default.gray(`\nWaiting for authorization (expires in ${Math.floor(deviceCode.expires_in / 60)} minutes)...`));
25
+ // 2. Poll for the access token
26
+ const token = await this.pollForToken(deviceCode.device_code, deviceCode.interval);
27
+ console.log(chalk_1.default.green('\n✅ Authentication Successful!'));
28
+ return token;
29
+ }
30
+ catch (error) {
31
+ console.error(chalk_1.default.red(`\n❌ Authentication failed: ${error.message}`));
32
+ throw error;
33
+ }
34
+ }
35
+ async requestDeviceCode() {
36
+ const response = await axios_1.default.post(this.config.authUrl, {
37
+ client_id: this.config.clientId,
38
+ scope: this.config.scope
39
+ }, {
40
+ headers: { Accept: 'application/json' }
41
+ });
42
+ return response.data;
43
+ }
44
+ async pollForToken(deviceCode, interval) {
45
+ let currentInterval = interval * 1000;
46
+ return new Promise((resolve, reject) => {
47
+ const poll = async () => {
48
+ try {
49
+ const response = await axios_1.default.post(this.config.tokenUrl, {
50
+ client_id: this.config.clientId,
51
+ device_code: deviceCode,
52
+ grant_type: 'urn:ietf:params:oauth:grant-type:device_code'
53
+ }, {
54
+ headers: { Accept: 'application/json' }
55
+ });
56
+ if (response.data.access_token) {
57
+ resolve(response.data.access_token);
58
+ return;
59
+ }
60
+ if (response.data.error) {
61
+ const error = response.data.error;
62
+ if (error === 'authorization_pending') {
63
+ // Keep polling
64
+ setTimeout(poll, currentInterval);
65
+ }
66
+ else if (error === 'slow_down') {
67
+ currentInterval += 5000;
68
+ setTimeout(poll, currentInterval);
69
+ }
70
+ else {
71
+ reject(new Error(response.data.error_description || error));
72
+ }
73
+ }
74
+ }
75
+ catch (error) {
76
+ reject(error);
77
+ }
78
+ };
79
+ setTimeout(poll, currentInterval);
80
+ });
81
+ }
82
+ }
83
+ exports.DeviceFlowService = DeviceFlowService;
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.resolveManagedCommand = exports.getSystemCommandPath = exports.getPortableNpxCommand = void 0;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const project_fraim_paths_1 = require("../../core/utils/project-fraim-paths");
10
+ const getPortableNodeRoot = () => path_1.default.join((0, project_fraim_paths_1.getUserFraimDirPath)(), 'node');
11
+ const getPortableNpxCandidates = () => {
12
+ const nodeRoot = getPortableNodeRoot();
13
+ if (process.platform === 'win32') {
14
+ const candidates = [path_1.default.join(nodeRoot, 'npx.cmd')];
15
+ if (fs_1.default.existsSync(nodeRoot)) {
16
+ const extractedDirs = fs_1.default.readdirSync(nodeRoot, { withFileTypes: true })
17
+ .filter((entry) => entry.isDirectory() && entry.name.startsWith('node-v'))
18
+ .sort((a, b) => b.name.localeCompare(a.name));
19
+ for (const entry of extractedDirs) {
20
+ candidates.push(path_1.default.join(nodeRoot, entry.name, 'npx.cmd'));
21
+ }
22
+ }
23
+ return candidates;
24
+ }
25
+ return [
26
+ path_1.default.join(nodeRoot, 'bin', 'npx'),
27
+ path_1.default.join(nodeRoot, 'npx')
28
+ ];
29
+ };
30
+ const getPortableNpxCommand = () => {
31
+ for (const candidate of getPortableNpxCandidates()) {
32
+ if (fs_1.default.existsSync(candidate)) {
33
+ return candidate;
34
+ }
35
+ }
36
+ return null;
37
+ };
38
+ exports.getPortableNpxCommand = getPortableNpxCommand;
39
+ const getPathEntries = () => {
40
+ const rawPath = process.env.PATH || '';
41
+ return rawPath
42
+ .split(path_1.default.delimiter)
43
+ .map((entry) => entry.trim())
44
+ .filter(Boolean);
45
+ };
46
+ const getSystemCommandCandidates = (command) => {
47
+ if (!command || path_1.default.isAbsolute(command)) {
48
+ return command ? [command] : [];
49
+ }
50
+ const commandNames = process.platform === 'win32'
51
+ ? command.includes('.')
52
+ ? [command]
53
+ : [command, `${command}.cmd`, `${command}.exe`, `${command}.bat`, `${command}.com`]
54
+ : [command];
55
+ return getPathEntries().flatMap((entry) => commandNames.map((name) => path_1.default.join(entry, name)));
56
+ };
57
+ const getSystemCommandPath = (command) => {
58
+ for (const candidate of getSystemCommandCandidates(command)) {
59
+ try {
60
+ const stats = fs_1.default.statSync(candidate);
61
+ if (stats.isFile()) {
62
+ return candidate;
63
+ }
64
+ }
65
+ catch {
66
+ // Ignore missing or inaccessible PATH entries and keep scanning.
67
+ }
68
+ }
69
+ return null;
70
+ };
71
+ exports.getSystemCommandPath = getSystemCommandPath;
72
+ const resolveManagedCommand = (command) => {
73
+ if (command !== 'npx') {
74
+ return command;
75
+ }
76
+ // Prefer system-installed npx so we don't install our own Node when the
77
+ // machine already has one. Fall back to the FRAIM-managed portable copy
78
+ // only when no system npx is found. Last resort: bare command name.
79
+ return (0, exports.getSystemCommandPath)(command) || (0, exports.getPortableNpxCommand)() || command;
80
+ };
81
+ exports.resolveManagedCommand = resolveManagedCommand;
@@ -0,0 +1,136 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getFraimMcpLatestLauncherPath = getFraimMcpLatestLauncherPath;
7
+ exports.ensureFraimMcpLatestLauncher = ensureFraimMcpLatestLauncher;
8
+ const fs_1 = __importDefault(require("fs"));
9
+ const os_1 = __importDefault(require("os"));
10
+ const path_1 = __importDefault(require("path"));
11
+ const LAUNCHER_VERSION = 1;
12
+ const launcherSource = `#!/usr/bin/env node
13
+ const fs = require('fs');
14
+ const os = require('os');
15
+ const path = require('path');
16
+ const { spawnSync } = require('child_process');
17
+
18
+ const LAUNCHER_VERSION = ${LAUNCHER_VERSION};
19
+ const USER_DIR = process.env.FRAIM_USER_DIR || path.join(os.homedir(), '.fraim');
20
+ const STATE_DIR = path.join(USER_DIR, 'mcp-launcher');
21
+ const STATE_PATH = path.join(STATE_DIR, 'latest.json');
22
+ const CACHE_ROOT = path.join(USER_DIR, 'npm-cache', 'mcp');
23
+
24
+ const commandName = (name) => process.platform === 'win32' ? name + '.cmd' : name;
25
+ const npmCommand = commandName('npm');
26
+ const npxCommand = commandName('npx');
27
+ const cliArgs = process.argv.slice(2).length > 0 ? process.argv.slice(2) : ['mcp'];
28
+
29
+ function quoteCmdArg(arg) {
30
+ const value = String(arg);
31
+ return /[\\s"&|<>^]/.test(value) ? '"' + value.replace(/"/g, '""') + '"' : value;
32
+ }
33
+
34
+ function runCommand(command, args, options) {
35
+ if (process.platform !== 'win32') {
36
+ return spawnSync(command, args, options);
37
+ }
38
+
39
+ return spawnSync(process.env.ComSpec || 'cmd.exe', ['/d', '/s', '/c', [command, ...args.map(quoteCmdArg)].join(' ')], options);
40
+ }
41
+
42
+ function ensureDir(dir) {
43
+ fs.mkdirSync(dir, { recursive: true });
44
+ }
45
+
46
+ function readState() {
47
+ try {
48
+ return JSON.parse(fs.readFileSync(STATE_PATH, 'utf8'));
49
+ } catch {
50
+ return null;
51
+ }
52
+ }
53
+
54
+ function writeState(state) {
55
+ ensureDir(STATE_DIR);
56
+ fs.writeFileSync(STATE_PATH, JSON.stringify({ ...state, launcherVersion: LAUNCHER_VERSION, updatedAt: new Date().toISOString() }, null, 2));
57
+ }
58
+
59
+ function resolveLatest(packageName) {
60
+ const result = runCommand(npmCommand, ['view', packageName, 'version', '--silent'], {
61
+ encoding: 'utf8',
62
+ env: process.env
63
+ });
64
+
65
+ if (result.status !== 0) {
66
+ return null;
67
+ }
68
+
69
+ const version = String(result.stdout || '').trim();
70
+ return /^\\d+\\.\\d+\\.\\d+/.test(version) ? version : null;
71
+ }
72
+
73
+ function resolvePackage() {
74
+ const fraimVersion = resolveLatest('fraim');
75
+ if (fraimVersion) {
76
+ const state = { packageName: 'fraim', binName: 'fraim', version: fraimVersion, fromCache: false };
77
+ writeState(state);
78
+ return state;
79
+ }
80
+
81
+ const cached = readState();
82
+ if (cached && cached.packageName === 'fraim' && cached.binName === 'fraim' && cached.version) {
83
+ console.error('[fraim-mcp-launcher] Could not resolve npm latest; using cached fraim@' + cached.version + '.');
84
+ return { packageName: 'fraim', binName: 'fraim', version: cached.version, fromCache: true };
85
+ }
86
+
87
+ console.error('[fraim-mcp-launcher] Could not resolve fraim latest from npm and no cached fraim version exists.');
88
+ process.exit(1);
89
+ }
90
+
91
+ function runPackage(plan, cacheDir) {
92
+ ensureDir(cacheDir);
93
+ const env = { ...process.env, npm_config_cache: cacheDir };
94
+ const result = runCommand(npxCommand, ['--yes', '--package', plan.packageName + '@' + plan.version, plan.binName, ...cliArgs], {
95
+ stdio: 'inherit',
96
+ env
97
+ });
98
+ return typeof result.status === 'number' ? result.status : 1;
99
+ }
100
+
101
+ const plan = resolvePackage();
102
+ const versionCacheDir = path.join(CACHE_ROOT, plan.packageName + '-' + plan.version.replace(/[^a-zA-Z0-9._-]/g, '_'));
103
+ let status = runPackage(plan, versionCacheDir);
104
+
105
+ if (status !== 0 && process.platform === 'win32') {
106
+ const retryCache = path.join(CACHE_ROOT, 'retry-' + Date.now() + '-' + process.pid);
107
+ console.error('[fraim-mcp-launcher] Package execution failed; retrying once with a fresh npm cache.');
108
+ status = runPackage(plan, retryCache);
109
+ }
110
+
111
+ process.exit(status);
112
+ `;
113
+ function getFraimMcpLatestLauncherPath() {
114
+ return path_1.default.join(process.env.FRAIM_USER_DIR || path_1.default.join(os_1.default.homedir(), '.fraim'), 'bin', 'fraim-mcp-latest.js');
115
+ }
116
+ function ensureFraimMcpLatestLauncher() {
117
+ const launcherPath = getFraimMcpLatestLauncherPath();
118
+ const launcherDir = path_1.default.dirname(launcherPath);
119
+ fs_1.default.mkdirSync(launcherDir, { recursive: true });
120
+ if (!fs_1.default.existsSync(launcherPath) || fs_1.default.readFileSync(launcherPath, 'utf8') !== launcherSource) {
121
+ fs_1.default.writeFileSync(launcherPath, launcherSource, 'utf8');
122
+ if (process.platform !== 'win32') {
123
+ try {
124
+ fs_1.default.chmodSync(launcherPath, 0o755);
125
+ }
126
+ catch {
127
+ // Best effort only. IDE configs invoke this through node, so chmod is not required.
128
+ }
129
+ }
130
+ }
131
+ return {
132
+ command: process.execPath,
133
+ args: [launcherPath],
134
+ path: launcherPath
135
+ };
136
+ }
@@ -0,0 +1,317 @@
1
+ "use strict";
2
+ // IDE Format Adapters - transform logical server structure to IDE-specific formats
3
+ // Uses the centralized registry to determine server types
4
+ Object.defineProperty(exports, "__esModule", { value: true });
5
+ exports.IDE_FORMATS = exports.GrokFormat = exports.CodexFormat = exports.WindsurfFormat = exports.ClaudeCodeFormat = exports.ClaudeFormat = exports.CopilotCliFormat = exports.GeminiCliFormat = exports.VSCodeFormat = exports.KiroFormat = exports.StandardFormat = void 0;
6
+ exports.getIDEFormat = getIDEFormat;
7
+ const mcp_server_registry_1 = require("./mcp-server-registry");
8
+ const provider_registry_1 = require("../providers/provider-registry");
9
+ const command_resolution_1 = require("./command-resolution");
10
+ // Standard format (Cursor, Kiro, Antigravity, etc.)
11
+ class StandardFormat {
12
+ constructor() {
13
+ this.name = 'standard';
14
+ }
15
+ transform(servers) {
16
+ const mcpServers = {};
17
+ for (const [key, server] of servers) {
18
+ if (server.url) {
19
+ // HTTP server - use serverUrl for standard format
20
+ mcpServers[key] = {
21
+ serverUrl: server.url,
22
+ headers: server.headers
23
+ };
24
+ }
25
+ else {
26
+ // stdio server
27
+ mcpServers[key] = {
28
+ command: server.command,
29
+ ...(server.args && { args: server.args }),
30
+ ...(server.env && { env: server.env })
31
+ };
32
+ }
33
+ }
34
+ return { mcpServers };
35
+ }
36
+ }
37
+ exports.StandardFormat = StandardFormat;
38
+ // Kiro format (also used by Cursor)
39
+ class KiroFormat {
40
+ constructor() {
41
+ this.name = 'kiro';
42
+ }
43
+ transform(servers) {
44
+ const mcpServers = {};
45
+ for (const [key, server] of servers) {
46
+ if (server.url) {
47
+ // HTTP server - use url (not serverUrl)
48
+ mcpServers[key] = {
49
+ url: server.url,
50
+ headers: server.headers
51
+ };
52
+ }
53
+ else {
54
+ // stdio server
55
+ mcpServers[key] = {
56
+ command: server.command,
57
+ ...(server.args && { args: server.args }),
58
+ ...(server.env && { env: server.env })
59
+ };
60
+ }
61
+ }
62
+ return { mcpServers };
63
+ }
64
+ }
65
+ exports.KiroFormat = KiroFormat;
66
+ // VSCode format
67
+ class VSCodeFormat {
68
+ constructor() {
69
+ this.name = 'vscode';
70
+ }
71
+ transform(servers) {
72
+ const vscodeServers = {};
73
+ for (const [key, server] of servers) {
74
+ if (server.url) {
75
+ // HTTP server
76
+ vscodeServers[key] = {
77
+ type: 'http',
78
+ url: server.url,
79
+ headers: server.headers
80
+ };
81
+ }
82
+ else {
83
+ // stdio server
84
+ vscodeServers[key] = {
85
+ type: 'stdio',
86
+ command: server.command,
87
+ ...(server.args && { args: server.args }),
88
+ ...(server.env && { env: server.env })
89
+ };
90
+ }
91
+ }
92
+ return { servers: vscodeServers };
93
+ }
94
+ }
95
+ exports.VSCodeFormat = VSCodeFormat;
96
+ class GeminiCliFormat {
97
+ constructor() {
98
+ this.name = 'gemini-cli';
99
+ }
100
+ transform(servers) {
101
+ const mcpServers = {};
102
+ for (const [key, server] of servers) {
103
+ if (server.url) {
104
+ mcpServers[key] = {
105
+ ...(server.type === 'sse'
106
+ ? { url: server.url }
107
+ : { httpUrl: server.httpUrl || server.url }),
108
+ ...(server.headers && { headers: server.headers })
109
+ };
110
+ }
111
+ else {
112
+ mcpServers[key] = {
113
+ command: server.command,
114
+ ...(server.args && { args: server.args }),
115
+ ...(server.env && { env: server.env })
116
+ };
117
+ }
118
+ }
119
+ return { mcpServers };
120
+ }
121
+ }
122
+ exports.GeminiCliFormat = GeminiCliFormat;
123
+ class CopilotCliFormat {
124
+ constructor() {
125
+ this.name = 'copilot-cli';
126
+ }
127
+ transform(servers) {
128
+ const mcpServers = {};
129
+ for (const [key, server] of servers) {
130
+ // GitHub MCP is built into Copilot CLI. Avoid a duplicate user-configured
131
+ // github entry and let Copilot handle its native GitHub auth path.
132
+ if (key === 'github') {
133
+ continue;
134
+ }
135
+ if (server.url) {
136
+ mcpServers[key] = {
137
+ type: server.type === 'sse' ? 'sse' : 'http',
138
+ url: server.url,
139
+ ...(server.headers && { headers: server.headers }),
140
+ tools: ['*']
141
+ };
142
+ }
143
+ else {
144
+ mcpServers[key] = {
145
+ type: 'stdio',
146
+ command: server.command,
147
+ ...(server.args && { args: server.args }),
148
+ ...(server.env && { env: server.env }),
149
+ tools: ['*']
150
+ };
151
+ }
152
+ }
153
+ return { mcpServers };
154
+ }
155
+ }
156
+ exports.CopilotCliFormat = CopilotCliFormat;
157
+ // Claude Desktop format (excludes provider servers - Issue #132)
158
+ class ClaudeFormat {
159
+ constructor() {
160
+ this.name = 'claude';
161
+ }
162
+ async transform(servers) {
163
+ const mcpServers = {};
164
+ for (const [key, server] of servers) {
165
+ // Skip all provider servers - they break Claude Desktop
166
+ // See: https://github.com/mathursrus/FRAIM/issues/132
167
+ if (await (0, mcp_server_registry_1.isProviderServer)(key)) {
168
+ continue;
169
+ }
170
+ // Only include base servers (git, playwright, fraim)
171
+ mcpServers[key] = {
172
+ command: server.command,
173
+ ...(server.args && { args: server.args }),
174
+ ...(server.env && { env: server.env })
175
+ };
176
+ }
177
+ return { mcpServers };
178
+ }
179
+ }
180
+ exports.ClaudeFormat = ClaudeFormat;
181
+ // Claude Code format (supports HTTP servers with VSCode-style format)
182
+ class ClaudeCodeFormat {
183
+ constructor() {
184
+ this.name = 'claude-code';
185
+ }
186
+ transform(servers) {
187
+ const mcpServers = {};
188
+ for (const [key, server] of servers) {
189
+ if (server.url) {
190
+ // HTTP server - use VSCode-style format
191
+ mcpServers[key] = {
192
+ type: 'http',
193
+ url: server.url,
194
+ headers: server.headers
195
+ };
196
+ }
197
+ else {
198
+ // stdio server
199
+ mcpServers[key] = {
200
+ command: server.command,
201
+ ...(server.args && { args: server.args }),
202
+ ...(server.env && { env: server.env })
203
+ };
204
+ }
205
+ }
206
+ return { mcpServers };
207
+ }
208
+ }
209
+ exports.ClaudeCodeFormat = ClaudeCodeFormat;
210
+ // Windsurf format (uses server-fetch wrapper for HTTP servers)
211
+ class WindsurfFormat {
212
+ constructor() {
213
+ this.name = 'windsurf';
214
+ }
215
+ async transform(servers) {
216
+ const mcpServers = {};
217
+ for (const [key, server] of servers) {
218
+ if (server.url) {
219
+ // HTTP server - wrap with server-fetch
220
+ const provider = await (0, provider_registry_1.getProvider)(key);
221
+ const tokenEnvVar = provider?.id ? `${provider.id.toUpperCase()}_TOKEN` : undefined;
222
+ if (tokenEnvVar && server.headers?.Authorization) {
223
+ const token = server.headers.Authorization.replace('Bearer ', '');
224
+ mcpServers[key] = {
225
+ command: (0, command_resolution_1.resolveManagedCommand)('npx'),
226
+ args: ['-y', '@modelcontextprotocol/server-fetch', server.url],
227
+ env: {
228
+ [tokenEnvVar]: token
229
+ }
230
+ };
231
+ }
232
+ }
233
+ else {
234
+ // stdio server
235
+ mcpServers[key] = {
236
+ command: server.command,
237
+ ...(server.args && { args: server.args }),
238
+ ...(server.env && { env: server.env })
239
+ };
240
+ }
241
+ }
242
+ return { mcpServers };
243
+ }
244
+ }
245
+ exports.WindsurfFormat = WindsurfFormat;
246
+ // Codex format (TOML)
247
+ class CodexFormat {
248
+ constructor() {
249
+ this.name = 'codex';
250
+ }
251
+ transform(servers) {
252
+ const sections = [];
253
+ for (const [key, server] of servers) {
254
+ if (server.url) {
255
+ // HTTP server
256
+ const authHeader = server.headers?.Authorization;
257
+ const escapedUrl = this.escapeToml(server.url);
258
+ sections.push(`[mcp_servers.${key}]`);
259
+ sections.push(`url = "${escapedUrl}"`);
260
+ // OAuth-first providers (e.g. GitHub) have no Authorization header — IDE manages auth
261
+ if (authHeader) {
262
+ sections.push(`http_headers = { Authorization = "${this.escapeToml(authHeader)}" }`);
263
+ }
264
+ sections.push('');
265
+ }
266
+ else {
267
+ // stdio server
268
+ sections.push(`[mcp_servers.${key}]`);
269
+ sections.push(`command = "${this.escapeToml(server.command || '')}"`);
270
+ if (server.args && server.args.length > 0) {
271
+ const argsStr = server.args.map(arg => `"${this.escapeToml(arg)}"`).join(', ');
272
+ sections.push(`args = [${argsStr}]`);
273
+ }
274
+ if (server.env) {
275
+ sections.push('');
276
+ sections.push(`[mcp_servers.${key}.env]`);
277
+ for (const [envKey, envValue] of Object.entries(server.env)) {
278
+ sections.push(`${envKey} = "${this.escapeToml(envValue)}"`);
279
+ }
280
+ }
281
+ sections.push('');
282
+ }
283
+ }
284
+ return sections.join('\n');
285
+ }
286
+ escapeToml(value) {
287
+ return value.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
288
+ }
289
+ }
290
+ exports.CodexFormat = CodexFormat;
291
+ class GrokFormat extends CodexFormat {
292
+ constructor() {
293
+ super(...arguments);
294
+ this.name = 'grok';
295
+ }
296
+ }
297
+ exports.GrokFormat = GrokFormat;
298
+ // Format registry
299
+ exports.IDE_FORMATS = {
300
+ standard: new StandardFormat(),
301
+ kiro: new KiroFormat(),
302
+ vscode: new VSCodeFormat(),
303
+ 'gemini-cli': new GeminiCliFormat(),
304
+ 'copilot-cli': new CopilotCliFormat(),
305
+ claude: new ClaudeFormat(),
306
+ 'claude-code': new ClaudeCodeFormat(),
307
+ windsurf: new WindsurfFormat(),
308
+ codex: new CodexFormat(),
309
+ grok: new GrokFormat()
310
+ };
311
+ function getIDEFormat(configType) {
312
+ const format = exports.IDE_FORMATS[configType];
313
+ if (!format) {
314
+ throw new Error(`Unsupported IDE format: ${configType}`);
315
+ }
316
+ return format;
317
+ }
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ // MCP Server Builder - builds logical server structure (format-agnostic)
3
+ // Uses the centralized registry for all server definitions
4
+ Object.defineProperty(exports, "__esModule", { value: true });
5
+ exports.MCPServerBuilder = void 0;
6
+ const mcp_server_registry_1 = require("./mcp-server-registry");
7
+ class MCPServerBuilder {
8
+ constructor() {
9
+ this.servers = new Map();
10
+ }
11
+ addBaseServers(fraimKey) {
12
+ const baseServers = (0, mcp_server_registry_1.buildAllBaseServers)(fraimKey);
13
+ for (const [id, server] of baseServers) {
14
+ this.servers.set(id, server);
15
+ }
16
+ return this;
17
+ }
18
+ async addProvider(providerId, token, config) {
19
+ try {
20
+ const server = await (0, mcp_server_registry_1.buildProviderMCPServer)(providerId, token, config);
21
+ this.servers.set(providerId, server);
22
+ }
23
+ catch (error) {
24
+ // Always warn user when MCP server can't be built
25
+ // Common case: Jira without baseUrl/email config
26
+ const errorMessage = error instanceof Error ? error.message : String(error);
27
+ console.warn(`⚠️ Skipping ${providerId} MCP server: ${errorMessage}`);
28
+ }
29
+ return this;
30
+ }
31
+ // Legacy methods for backward compatibility - delegate to addProvider
32
+ async addGitHub(token) {
33
+ return await this.addProvider('github', token);
34
+ }
35
+ async addGitLab(token) {
36
+ return await this.addProvider('gitlab', token);
37
+ }
38
+ async addJira(token, config) {
39
+ return await this.addProvider('jira', token, config);
40
+ }
41
+ getServers() {
42
+ return new Map(this.servers);
43
+ }
44
+ toObject() {
45
+ return Object.fromEntries(this.servers);
46
+ }
47
+ }
48
+ exports.MCPServerBuilder = MCPServerBuilder;