134 lines
4.0 KiB
JavaScript
134 lines
4.0 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* Prepare offline wheels for windows-mcp under resources/windows-mcp/wheels.
|
|
*
|
|
* Runtime installation will happen on end-user machine to avoid non-relocatable
|
|
* uv tool launcher issues when app is installed in a different path.
|
|
*
|
|
* Note: Bundled uv no longer provides `uv pip download`; we use
|
|
* `uv run ... python -m pip download` with a pinned Python (3.13+) so wheel tags
|
|
* match package `Requires-Python` and typical `uv tool install` on Windows.
|
|
*/
|
|
|
|
const path = require('path');
|
|
const fs = require('fs');
|
|
const { execFileSync } = require('child_process');
|
|
const { getProjectRoot, resolveFromProject } = require('../utils/project-paths');
|
|
|
|
const projectRoot = getProjectRoot();
|
|
const resDir = path.join(projectRoot, 'resources', 'windows-mcp');
|
|
const wheelsDir = path.join(resDir, 'wheels');
|
|
const manifestPath = path.join(resDir, 'manifest.json');
|
|
|
|
const PACKAGE_NAME = 'windows-mcp';
|
|
/** Interpreter for pip download (windows-mcp currently requires Python >= 3.13). 若修改须同步 windowsMcp.ts 的 WINDOWS_MCP_UV_PYTHON。 */
|
|
const PIP_DOWNLOAD_PYTHON = '3.13';
|
|
|
|
function getUvBinPath() {
|
|
const uvBinName = process.platform === 'win32' ? 'uv.exe' : 'uv';
|
|
return resolveFromProject('resources', 'uv', 'bin', uvBinName);
|
|
}
|
|
|
|
function runPipDownload(uvBin, destDir, packageName, env) {
|
|
execFileSync(
|
|
uvBin,
|
|
[
|
|
'run',
|
|
'--no-project',
|
|
'--isolated',
|
|
'--python',
|
|
PIP_DOWNLOAD_PYTHON,
|
|
'-w',
|
|
'pip',
|
|
'python',
|
|
'-m',
|
|
'pip',
|
|
'download',
|
|
'--dest',
|
|
destDir,
|
|
'--only-binary',
|
|
':all:',
|
|
packageName,
|
|
],
|
|
{ stdio: 'inherit', env },
|
|
);
|
|
}
|
|
|
|
function resolvePinnedVersion(files) {
|
|
const wheel = files.find((name) => /^windows_mcp-([^-]+)-.*\.whl$/i.test(name));
|
|
if (!wheel) {
|
|
return null;
|
|
}
|
|
const match = wheel.match(/^windows_mcp-([^-]+)-.*\.whl$/i);
|
|
return match ? match[1] : null;
|
|
}
|
|
|
|
function main() {
|
|
if (process.platform !== 'win32') {
|
|
console.log('[prepare-windows-mcp] Skipped: not Windows platform');
|
|
return;
|
|
}
|
|
|
|
const uvBin = getUvBinPath();
|
|
if (!fs.existsSync(uvBin)) {
|
|
console.error('[prepare-windows-mcp] uv not found at:', uvBin);
|
|
process.exit(1);
|
|
}
|
|
|
|
console.log('[prepare-windows-mcp] Using uv:', uvBin);
|
|
|
|
if (fs.existsSync(resDir)) {
|
|
console.log('[prepare-windows-mcp] Removing old resources/windows-mcp ...');
|
|
fs.rmSync(resDir, { recursive: true, force: true });
|
|
}
|
|
fs.mkdirSync(wheelsDir, { recursive: true });
|
|
|
|
console.log('[prepare-windows-mcp] Downloading wheels for offline installation ...');
|
|
try {
|
|
runPipDownload(uvBin, wheelsDir, PACKAGE_NAME, {
|
|
...process.env,
|
|
UV_PYTHON_DOWNLOADS: process.env.UV_PYTHON_DOWNLOADS ?? 'automatic',
|
|
});
|
|
} catch (error) {
|
|
console.error('[prepare-windows-mcp] Failed to download windows-mcp wheels:', error.message);
|
|
process.exit(1);
|
|
}
|
|
|
|
const files = fs
|
|
.readdirSync(wheelsDir)
|
|
.filter((name) => {
|
|
const full = path.join(wheelsDir, name);
|
|
return fs.statSync(full).isFile() && name.toLowerCase().endsWith('.whl');
|
|
})
|
|
.sort();
|
|
|
|
if (files.length === 0) {
|
|
console.error('[prepare-windows-mcp] No files downloaded into wheels directory');
|
|
process.exit(1);
|
|
}
|
|
|
|
const version = resolvePinnedVersion(files);
|
|
if (!version) {
|
|
console.error('[prepare-windows-mcp] Could not resolve pinned windows-mcp version from downloaded wheels');
|
|
console.error('[prepare-windows-mcp] Downloaded files:', files.join(', '));
|
|
process.exit(1);
|
|
}
|
|
|
|
const manifest = {
|
|
packageName: PACKAGE_NAME,
|
|
version,
|
|
resolvedSpec: `${PACKAGE_NAME}==${version}`,
|
|
generatedAt: new Date().toISOString(),
|
|
files,
|
|
};
|
|
|
|
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2), 'utf-8');
|
|
|
|
console.log(`[prepare-windows-mcp] ✓ Downloaded ${files.length} files`);
|
|
console.log(`[prepare-windows-mcp] ✓ Pinned ${manifest.resolvedSpec}`);
|
|
console.log(`[prepare-windows-mcp] ✓ Wrote manifest: ${manifestPath}`);
|
|
console.log('[prepare-windows-mcp] ✓ resources/windows-mcp ready');
|
|
}
|
|
|
|
main();
|