install.js•4.16 kB
const fs = require('fs')
const path = require('path')
const zlib = require('zlib')
const https = require('https')
const PROJECT_NAME = 'mcp-time'
const NPM_VERSION = '0.1.0'
// Lookup table for all platforms and binary distribution packages
const BINARY_DISTRIBUTION_PACKAGES = {
'linux-x64': `${PROJECT_NAME}-linux-x64`,
'linux-arm64': `${PROJECT_NAME}-linux-arm64`,
'darwin-x64': `${PROJECT_NAME}-darwin-x64`,
'darwin-arm64': `${PROJECT_NAME}-darwin-arm64`,
'windows-x64': `${PROJECT_NAME}-windows-x64`,
}
// Windows binaries end with .exe so we need to special case them.
const binaryName = process.platform === 'win32' ? `${PROJECT_NAME}.exe` : PROJECT_NAME
// Determine package name for this platform
const platformSpecificPackageName =
BINARY_DISTRIBUTION_PACKAGES[`${process.platform}-${process.arch}`]
// Compute the path we want to emit the fallback binary to
const fallbackBinaryPath = path.join(__dirname, binaryName)
function makeRequest(url) {
return new Promise((resolve, reject) => {
https
.get(url, (response) => {
if (response.statusCode >= 200 && response.statusCode < 300) {
const chunks = []
response.on('data', (chunk) => chunks.push(chunk))
response.on('end', () => {
resolve(Buffer.concat(chunks))
})
} else if (
response.statusCode >= 300 &&
response.statusCode < 400 &&
response.headers.location
) {
// Follow redirects
makeRequest(response.headers.location).then(resolve, reject)
} else {
reject(
new Error(
`npm responded with status code ${response.statusCode} when downloading the package!`
)
)
}
})
.on('error', (error) => {
reject(error)
})
})
}
function extractFileFromTarball(tarballBuffer, filepath) {
// Tar archives are organized in 512 byte blocks.
// Blocks can either be header blocks or data blocks.
// Header blocks contain file names of the archive in the first 100 bytes, terminated by a null byte.
// The size of a file is contained in bytes 124-135 of a header block and in octal format.
// The following blocks will be data blocks containing the file.
let offset = 0
while (offset < tarballBuffer.length) {
const header = tarballBuffer.subarray(offset, offset + 512)
offset += 512
const fileName = header.toString('utf-8', 0, 100).replace(/\0.*/g, '')
const fileSize = parseInt(header.toString('utf-8', 124, 136).replace(/\0.*/g, ''), 8)
if (fileName === filepath) {
return tarballBuffer.subarray(offset, offset + fileSize)
}
// Clamp offset to the uppoer multiple of 512
offset = (offset + fileSize + 511) & ~511
}
}
async function downloadBinaryFromNpm() {
// Download the tarball of the right binary distribution package
const tarballDownloadBuffer = await makeRequest(
`https://registry.npmjs.org/${platformSpecificPackageName}/-/${platformSpecificPackageName}-${NPM_VERSION}.tgz`
)
const tarballBuffer = zlib.unzipSync(tarballDownloadBuffer)
// Extract binary from package and write to disk
fs.writeFileSync(
fallbackBinaryPath,
extractFileFromTarball(tarballBuffer, `package/bin/${binaryName}`),
{ mode: 0o755 } // Make binary file executable
)
}
function isPlatformSpecificPackageInstalled() {
try {
// Resolving will fail if the optionalDependency was not installed
require.resolve(`${platformSpecificPackageName}/bin/${binaryName}`)
return true
} catch (e) {
return false
}
}
if (!platformSpecificPackageName) {
throw new Error(`Platform not supported: ${process.platform}-${process.arch}`)
}
// Skip downloading the binary if it was already installed via optionalDependencies
if (!isPlatformSpecificPackageInstalled()) {
console.log('Platform specific package not found. Will manually download binary.')
downloadBinaryFromNpm().catch((err) => {
console.error('Failed to download binary:', err)
process.exit(1)
})
} else {
console.log(
'Platform specific package already installed. Skipping manual download.'
)
}