// Build-machine only. Clients run a private Node and locked production packages. import { cp, mkdir, readFile, writeFile, copyFile, stat, rm } from 'node:fs/promises' import { fileURLToPath } from 'node:url' import path from 'node:path' import { spawnSync } from 'node:child_process' const root = fileURLToPath(new URL('../', import.meta.url)) const source = path.join(root, 'runtime/filesystem') const dest = path.join(root, 'third_party/mcp-filesystem') const check = process.argv.includes('--check') const nodeName = process.platform === 'win32' ? 'node.exe' : 'node' function run(command, args, options = {}) { const result = spawnSync(command, args, { encoding: 'utf8', ...options }) if (result.error || result.status !== 0) throw result.error || Error(result.stderr || `${command} failed`) return result.stdout.trim() } if (!['win32', 'darwin'].includes(process.platform)) throw Error('Stage Filesystem MCP on the target Windows or macOS build machine') if (Number(process.versions.node.split('.')[0]) < 24) throw Error('Build machine needs Node >= 24; customers do not need Node') if (!check) { // npm ci uses the committed integrity-checked lockfile, never @latest on clients. if (!process.env.npm_execpath) throw Error('Run npm --prefix runtime/filesystem run stage') run(process.execPath, [process.env.npm_execpath, 'ci', '--omit=dev', '--ignore-scripts', '--no-audit', '--no-fund'], { cwd: source }) await mkdir(dest, { recursive: true }) const modules = path.resolve(dest, 'node_modules') if (path.dirname(modules) !== path.resolve(root, 'third_party/mcp-filesystem')) throw Error('Invalid staging directory') await rm(modules, { recursive: true, force: true }) await cp(path.join(source, 'node_modules'), modules, { recursive: true, dereference: true }) const nodeSource = process.platform === 'darwin' && process.env.ITUOKE_NODE_UNIVERSAL_SOURCE || process.execPath await copyFile(nodeSource, path.join(dest, nodeName)) await copyFile(path.join(source, 'package-lock.json'), path.join(dest, 'package-lock.json')) await copyFile(path.join(source, 'FILESYSTEM-LICENSE.txt'), path.join(dest, 'FILESYSTEM-LICENSE.txt')) await copyFile(path.join(source, 'FILESYSTEM-LICENSE.txt'), path.join(dest, 'FILESYSTEM-LICENSE.txt')) // Node distributions include their license adjacent to the executable. const license = process.env.ITUOKE_NODE_LICENSE || path.join(path.dirname(process.execPath), 'LICENSE') await copyFile(license, path.join(dest, 'NODE-LICENSE.txt')) await writeFile(path.join(dest, 'runtime.json'), JSON.stringify({ platform: process.platform, arch: nodeSource === process.execPath ? process.arch : 'universal', node: process.versions.node, filesystem: '2.1.0' }, null, 2)) } const manifest = JSON.parse(await readFile(path.join(dest, 'runtime.json'), 'utf8')) if (manifest.platform !== process.platform || (manifest.arch !== process.arch && manifest.arch !== 'universal') || manifest.filesystem !== '2.1.0') throw Error('Filesystem runtime platform/version mismatch; restage on this build machine') const actual = run(path.join(dest, nodeName), ['-p', 'JSON.stringify({platform:process.platform,arch:process.arch,major:Number(process.versions.node.split(".")[0])})']) const identity = JSON.parse(actual) if (identity.major < 24 || identity.platform !== manifest.platform || (manifest.arch !== 'universal' && identity.arch !== manifest.arch)) throw Error('Wrong bundled Node runtime') if (process.platform === 'darwin') { run('lipo', ['-verify_arch', ...(manifest.arch === 'universal' ? ['arm64', 'x86_64'] : [manifest.arch === 'x64' ? 'x86_64' : manifest.arch]), path.join(dest, nodeName)]) const libraries = run('otool', ['-L', path.join(dest, nodeName)]).split('\n').slice(1) if (libraries.some(line => !/^\s*\/(usr\/lib|System\/Library)\//.test(line))) throw Error('Node links to non-system dylibs; use an official self-contained Node distribution') } await stat(path.join(dest, 'node_modules/@modelcontextprotocol/server-filesystem/dist/index.js')) await stat(path.join(dest, 'NODE-LICENSE.txt')) await stat(path.join(dest, 'FILESYSTEM-LICENSE.txt')) await stat(path.join(dest, 'FILESYSTEM-LICENSE.txt')) console.log(`Filesystem MCP 2.1.0 staged: ${manifest.platform}/${manifest.arch}, Node ${manifest.node}`)