#!/usr/bin/env node import { execSync } from 'child_process'; import { writeFileSync, mkdirSync, existsSync, readFileSync, copyFileSync, readdirSync, statSync, createWriteStream, rmSync } from 'fs'; import { join } from 'path'; import archiver from 'archiver'; const DIST_DIR = 'dist'; const STAGING_DIR = join(DIST_DIR, 'siwat_system_mcp'); const OUTPUT_FILE = join(STAGING_DIR, 'index.js'); const DXT_FILE = join(DIST_DIR, 'siwat_system_mcp.dxt'); // Function to create .dxt file using archiver (cross-platform) async function createDxtFile(): Promise { return new Promise((resolve, reject) => { console.log('๐Ÿ“ฆ Creating .dxt file...'); const output = createWriteStream(DXT_FILE); const archive = archiver('zip', { zlib: { level: 9 } // Maximum compression }); output.on('close', () => { console.log(`๐Ÿ“ฆ Created ${DXT_FILE} (${archive.pointer()} bytes)`); resolve(); }); archive.on('error', (err) => { reject(err); }); archive.pipe(output); // Add all files from staging directory to the archive archive.directory(STAGING_DIR, false); archive.finalize(); }); } // Function to copy assets to staging directory function copyAssets() { const assetsDir = 'assets'; if (!existsSync(assetsDir)) { console.log('๐Ÿ“‚ No assets directory found, skipping asset copy'); return; } console.log('๐Ÿ“ Copying assets to staging directory...'); // Copy all files from assets to staging root (for .dxt extension structure) try { const files = readdirSync(assetsDir); files.forEach(file => { const srcPath = join(assetsDir, file); const destPath = join(STAGING_DIR, file); if (statSync(srcPath).isFile()) { copyFileSync(srcPath, destPath); console.log(`๐Ÿ“„ Copied ${file} to ${STAGING_DIR}/`); } }); } catch (error: any) { console.warn('โš ๏ธ Warning: Could not copy some assets:', error.message); } } async function build() { try { console.log('๐Ÿš€ Starting build process...'); // Clean dist directory if it exists if (existsSync(DIST_DIR)) { console.log('๐Ÿงน Cleaning dist directory...'); rmSync(DIST_DIR, { recursive: true, force: true }); } // Ensure dist and staging directories exist mkdirSync(DIST_DIR, { recursive: true }); console.log('๐Ÿ“ Created dist directory'); mkdirSync(STAGING_DIR, { recursive: true }); console.log('๐Ÿ“ Created staging directory'); // Check if mcp-remote is available try { execSync('npm list mcp-remote', { stdio: 'ignore' }); } catch { console.log('๐Ÿ“ฆ Installing mcp-remote...'); execSync('npm install mcp-remote', { stdio: 'inherit' }); } // Install ncc if not available try { execSync('npx ncc --version', { stdio: 'ignore' }); } catch { console.log('๐Ÿ“ฆ Installing @vercel/ncc...'); execSync('npm install @vercel/ncc', { stdio: 'inherit' }); } // Find mcp-remote entry point const mcpRemoteEntry = 'node_modules/mcp-remote/dist/proxy.js'; if (!existsSync(mcpRemoteEntry)) { console.log('๐Ÿ“ฆ Installing mcp-remote...'); execSync('npm install mcp-remote', { stdio: 'inherit' }); } // Bundle with ncc console.log('๐Ÿ”จ Bundling with @vercel/ncc...'); const nccCommand = `npx ncc build ${mcpRemoteEntry} -o ${STAGING_DIR} --minify --no-source-map-register`; execSync(nccCommand, { stdio: 'inherit' }); // Copy assets to staging directory (manifest.json, icon.png, etc.) copyAssets(); // Create .dxt file by zipping the staging directory await createDxtFile(); console.log('โœ… Build completed successfully!'); console.log(`๐Ÿ“ฆ Standalone server created at: ${OUTPUT_FILE}`); console.log(`๐ŸŽฏ Extension packaged as: ${DXT_FILE}`); console.log('๐ŸŽฏ Run with: node dist/siwat_system_mcp/index.js'); } catch (error) { console.error('โŒ Build failed:', error); process.exit(1); } } // Main execution if (require.main === module) { build(); } export { build };