53 lines
4.2 KiB
JavaScript
53 lines
4.2 KiB
JavaScript
// 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}`)
|