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.
- package/README.md +467 -0
- package/bin/fraim-hub.js +17 -0
- package/dist/src/ai-hub/brand-store.js +17 -0
- package/dist/src/ai-hub/catalog.js +412 -0
- package/dist/src/ai-hub/cert-store.js +70 -0
- package/dist/src/ai-hub/cli.js +212 -0
- package/dist/src/ai-hub/configured-agents.js +283 -0
- package/dist/src/ai-hub/conversation-store.js +428 -0
- package/dist/src/ai-hub/desktop-main.js +449 -0
- package/dist/src/ai-hub/hosts.js +1669 -0
- package/dist/src/ai-hub/hub-latest-version.js +52 -0
- package/dist/src/ai-hub/hub-launch-decision.js +57 -0
- package/dist/src/ai-hub/hub-runtime-file.js +43 -0
- package/dist/src/ai-hub/managed-browser.js +269 -0
- package/dist/src/ai-hub/manager-turns.js +64 -0
- package/dist/src/ai-hub/office-sideload.js +156 -0
- package/dist/src/ai-hub/openclaw-bridge.js +250 -0
- package/dist/src/ai-hub/preferences.js +201 -0
- package/dist/src/ai-hub/remote-hub-gateway.js +88 -0
- package/dist/src/ai-hub/server.js +4013 -0
- package/dist/src/ai-hub/types.js +2 -0
- package/dist/src/ai-hub/url-safety.js +19 -0
- package/dist/src/api/admin/payments.js +33 -0
- package/dist/src/api/admin/sales-leads.js +21 -0
- package/dist/src/api/payment/create-session.js +338 -0
- package/dist/src/api/payment/dashboard-link.js +149 -0
- package/dist/src/api/payment/session-details.js +31 -0
- package/dist/src/api/payment/webhook.js +587 -0
- package/dist/src/api/sales/contact.js +44 -0
- package/dist/src/cli/api/get-provider-client.js +41 -0
- package/dist/src/cli/api/provider-client.js +107 -0
- package/dist/src/cli/commands/add-ide.js +462 -0
- package/dist/src/cli/fraim-hub.js +50 -0
- package/dist/src/cli/internal/device-flow-service.js +83 -0
- package/dist/src/cli/mcp/command-resolution.js +81 -0
- package/dist/src/cli/mcp/fraim-mcp-latest-launcher.js +136 -0
- package/dist/src/cli/mcp/ide-formats.js +317 -0
- package/dist/src/cli/mcp/mcp-server-builder.js +48 -0
- package/dist/src/cli/mcp/mcp-server-registry.js +173 -0
- package/dist/src/cli/providers/local-provider-registry.js +165 -0
- package/dist/src/cli/providers/provider-registry.js +230 -0
- package/dist/src/cli/setup/claude-code-telemetry.js +59 -0
- package/dist/src/cli/setup/codex-local-config.js +37 -0
- package/dist/src/cli/setup/ide-detector.js +383 -0
- package/dist/src/cli/setup/ide-global-integration.js +100 -0
- package/dist/src/cli/setup/ide-invocation-surfaces.js +160 -0
- package/dist/src/cli/setup/mcp-config-generator.js +225 -0
- package/dist/src/cli/setup/provider-prompts.js +339 -0
- package/dist/src/cli/utils/managed-agent-paths.js +118 -0
- package/dist/src/cli/utils/script-sync-utils.js +221 -0
- package/dist/src/cli/utils/user-config.js +100 -0
- package/dist/src/cli/utils/version-utils.js +35 -0
- package/dist/src/config/ai-manager-hiring.js +121 -0
- package/dist/src/config/feature-flags.js +25 -0
- package/dist/src/config/persona-capability-bundles.js +277 -0
- package/dist/src/config/persona-hiring.js +270 -0
- package/dist/src/config/portfolio-slug-overrides.js +17 -0
- package/dist/src/config/pricing.js +37 -0
- package/dist/src/config/stripe.js +43 -0
- package/dist/src/core/brand-store.js +232 -0
- package/dist/src/core/quality-evidence.js +331 -0
- package/dist/src/core/utils/git-utils.js +194 -0
- package/dist/src/core/utils/ports.js +32 -0
- package/dist/src/core/utils/project-fraim-paths.js +220 -0
- package/dist/src/db/payment-repository.js +61 -0
- package/dist/src/first-run/types.js +93 -0
- package/dist/src/fraim/db-service.js +2409 -0
- package/dist/src/local-mcp-server/agent-token-prices.js +167 -0
- package/dist/src/local-mcp-server/learning-context-builder.js +975 -0
- package/dist/src/middleware/rate-limit.js +110 -0
- package/dist/src/models/payment.js +2 -0
- package/dist/src/routes/payment-routes.js +186 -0
- package/dist/src/routes/persona-catalog-routes.js +84 -0
- package/dist/src/services/dashboard-access.js +27 -0
- package/dist/src/services/email-service.js +951 -0
- package/dist/src/services/persona-entitlement-service.js +352 -0
- package/dist/src/services/workspace-identity.js +21 -0
- package/dist/src/types/analytics.js +2 -0
- package/dist/src/utils/payment-calculator.js +52 -0
- package/extensions/office-word/favicon.ico +0 -0
- package/extensions/office-word/icon-64.png +0 -0
- package/extensions/office-word/manifest.xml +33 -0
- package/extensions/office-word/taskpane.html +242 -0
- package/index.js +85 -0
- package/package.json +106 -0
- package/public/ai-hub/index.html +910 -0
- package/public/ai-hub/powerpoint-taskpane/icon-64.png +0 -0
- package/public/ai-hub/powerpoint-taskpane/index.html +236 -0
- package/public/ai-hub/powerpoint-taskpane/manifest.xml +30 -0
- package/public/ai-hub/review.css +444 -0
- package/public/ai-hub/script.js +11737 -0
- package/public/ai-hub/styles.css +5102 -0
- package/public/first-run/error-frame.js +100 -0
- package/public/first-run/index.html +35 -0
- package/public/first-run/script.js +739 -0
- package/public/first-run/styles.css +929 -0
- package/public/portfolio/ashley.html +523 -0
- package/public/portfolio/auditya.html +83 -0
- package/public/portfolio/banke.html +83 -0
- package/public/portfolio/beza.html +659 -0
- package/public/portfolio/careena.html +632 -0
- package/public/portfolio/casey.html +568 -0
- package/public/portfolio/celia.html +490 -0
- package/public/portfolio/deidre.html +642 -0
- package/public/portfolio/gautam.html +597 -0
- package/public/portfolio/hari.html +469 -0
- package/public/portfolio/huxley.html +1354 -0
- package/public/portfolio/index.html +741 -0
- package/public/portfolio/maestro.html +518 -0
- package/public/portfolio/mandy.html +590 -0
- package/public/portfolio/mona.html +597 -0
- package/public/portfolio/pam.html +887 -0
- package/public/portfolio/procella.html +107 -0
- package/public/portfolio/qasm.html +569 -0
- package/public/portfolio/ricardo.html +489 -0
- package/public/portfolio/sade.html +560 -0
- package/public/portfolio/sam.html +654 -0
- package/public/portfolio/sechar.html +580 -0
- package/public/portfolio/sreya.html +599 -0
- 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;
|