UNPKG

@kya-os/mcp-i

Version:

The TypeScript MCP framework with identity features built-in

212 lines (211 loc) 8.88 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.compile = compile; const webpack_1 = require("webpack"); const get_webpack_config_1 = require("./get-webpack-config"); // Simplified - remove chalk styling for now const chalk = { bold: { green: (text) => text, }, }; const parse_xmcp_config_1 = require("./parse-xmcp-config"); const generate_import_code_1 = require("./generate-import-code"); const fs_1 = __importDefault(require("fs")); const constants_1 = require("../utils/constants"); const fs_utils_1 = require("../utils/fs-utils"); const path_1 = __importDefault(require("path")); // Use fs.rmSync for cleanup (Node.js 14.14+) const deleteSync = (path) => { try { if (fs_1.default.existsSync(path)) { fs_1.default.rmSync(path, { recursive: true, force: true }); console.log(`Cleaned: ${path}`); } } catch (error) { console.log(`Could not clean ${path}:`, error instanceof Error ? error.message : String(error)); } }; const dotenv_1 = __importDefault(require("dotenv")); const generate_env_code_1 = require("./generate-env-code"); const file_watcher_1 = require("../utils/file-watcher"); const on_first_build_1 = require("./on-first-build"); const cli_icons_1 = require("../utils/cli-icons"); const compiler_context_1 = require("./compiler-context"); const start_http_server_1 = require("./start-http-server"); const path_validation_1 = require("../utils/path-validation"); const utils_1 = require("./config/utils"); dotenv_1.default.config(); async function compile({ onBuild } = {}) { // Initialize compiler context if not already set const mode = process.env.NODE_ENV === "production" ? "production" : "development"; // Use compiler context provider to set up the context properly return new Promise((resolve, reject) => { (0, compiler_context_1.compilerContextProvider)({ mode, projectRoot: process.cwd(), platforms: {}, }, () => { compileInternal({ onBuild }).then(resolve).catch(reject); }); }); } async function compileInternal({ onBuild } = {}) { const { mode } = compiler_context_1.compilerContext.getContext(); const { toolPaths } = compiler_context_1.compilerContext.getContext(); const startTime = Date.now(); let compilerStarted = false; const xmpcConfig = await (0, parse_xmcp_config_1.getConfig)(); compiler_context_1.compilerContext.setContext({ xmcpConfig: xmpcConfig, }); let webpackConfig = (0, get_webpack_config_1.getWebpackConfig)(xmpcConfig); if (xmpcConfig.webpack) { webpackConfig = xmpcConfig.webpack(webpackConfig); } return new Promise((resolve, reject) => { const watcher = new file_watcher_1.Watcher({ // keep the watcher running on dev mode after "onReady" persistent: mode === "development", ignored: /(^|[\/\\])\../, ignoreInitial: false, }); let toolsPath = (0, path_validation_1.isValidPath)((0, utils_1.getResolvedPathsConfig)(xmpcConfig).tools, "tools"); // handle tools watcher.watch(`${toolsPath}/**/*.ts`, { onAdd: (path) => { toolPaths.add(path); if (compilerStarted) { generateCode(); } }, onUnlink: (path) => { toolPaths.delete(path); if (compilerStarted) { generateCode(); } }, }); // if adapter is not enabled, handle middleware if (!xmpcConfig.experimental?.adapter) { // handle middleware watcher.watch("./src/middleware.ts", { onAdd: () => { compiler_context_1.compilerContext.setContext({ hasMiddleware: true, }); if (compilerStarted) { generateCode(); } }, onUnlink: () => { compiler_context_1.compilerContext.setContext({ hasMiddleware: false, }); if (compilerStarted) { generateCode(); } }, }); } // start compiler watcher.onReady(() => { let firstBuild = true; compilerStarted = true; // delete existing runtime folder deleteSync(constants_1.runtimeFolderPath); (0, fs_utils_1.createFolder)(constants_1.runtimeFolderPath); generateCode(); const compiler = (0, webpack_1.webpack)(webpackConfig); // Webpack returns null if config is invalid if (!compiler) { reject(new Error("Failed to create webpack compiler - invalid config")); return; } const handleCompilation = (err, stats) => { if (err) { console.error(err); reject(err); return; } if (stats?.hasErrors()) { const errorMsg = stats.toString({ colors: true, chunks: false, }); console.error(errorMsg); reject(new Error('Webpack compilation failed')); return; } if (firstBuild) { firstBuild = false; const endTime = Date.now(); const duration = endTime - startTime; console.log(`${cli_icons_1.greenCheck} Compiled in ${chalk.bold.green(`${duration}ms`)}`); (0, on_first_build_1.onFirstBuild)(mode, xmpcConfig); onBuild?.(); // In production mode, close watcher and compiler, then resolve if (mode === "production") { watcher.close().then(() => { // Close webpack compiler to release all resources compiler.close((compilerCloseErr) => { if (compilerCloseErr) { console.error('Error closing webpack compiler:', compilerCloseErr); reject(compilerCloseErr); } else { resolve(); } }); }).catch((closeErr) => { console.error('Error closing watcher:', closeErr); reject(closeErr); }); } } else { // on dev mode, webpack will recompile the code, so we need to start the http server after the first one if (mode === "development" && xmpcConfig["http"] && !xmpcConfig.experimental?.adapter) { (0, start_http_server_1.startHttpServer)(); } } }; // Use compiler.run() for production (single build) or compiler.watch() for development (continuous builds) if (mode === "production") { compiler.run(handleCompilation); } else { const watching = compiler.watch({}, handleCompilation); // Store watching instance for proper cleanup process.on('SIGINT', () => { if (watching) { watching.close(() => { process.exit(0); }); } else { process.exit(0); } }); } }); }); } function generateCode() { const fileContent = (0, generate_import_code_1.generateImportCode)(); fs_1.default.writeFileSync(path_1.default.join(constants_1.runtimeFolderPath, "import-map.js"), fileContent); // Generate runtime exports for global access const runtimeExportsCode = (0, generate_env_code_1.generateEnvCode)(); const envFilePath = path_1.default.join(constants_1.rootFolder, "xmcp-env.d.ts"); // Delete existing file if it exists if (fs_1.default.existsSync(envFilePath)) { fs_1.default.unlinkSync(envFilePath); } fs_1.default.writeFileSync(envFilePath, runtimeExportsCode); }