Files
proffesor-for-testing__agen…/scripts/build-mcp.mjs
Dragan Spiridonov 122f223c9a fix(security): migrate from @xenova/transformers to @huggingface/transformers
@xenova/transformers v2.17.2 (last publish 2024-05-29) pins
onnxruntime-web@1.14.0, which pulls onnx-proto → protobufjs <=7.5.5.
That chain has 8 GHSA advisories including a critical arbitrary-code-
execution one (GHSA-xq3m-2v4x-88gg). Our top-level `overrides` block
forced protobufjs@^7.5.6 in our own tree, hiding the vulns from
`npm audit` in this repo — but `overrides` are root-only by design and
do not propagate to downstream consumers. End users running
`npm install agentic-qe` were getting 5 vulns (4 high, 1 critical).

@huggingface/transformers@4.2.0 is the official maintained successor
(same project, renamed). It pulls onnxruntime-web@1.26 which depends
on protobufjs@^7.2.4 directly (no onnx-proto), so the version range
resolves cleanly to the patched line.

API changes handled:
- pipeline()/Tensor: drop-in compatible, just the import path changes
- `quantized: bool` → `dtype: DataType` (mapped from our QuantizationType
  enum: 'none'→'fp32', 'fp16'→'fp16', 'int8'→'int8', 'binary'→'q8';
  binary stays a post-process step downstream)
- progress_callback signature: ProgressInfo is now a discriminated union;
  `progress` only exists on the "progress" variant — narrowed accordingly

Also added onnxruntime-node and onnxruntime-web to the CLI and MCP
bundle externals lists. @huggingface/transformers ships both runtimes
(node for Node.js, web as fallback); esbuild can't bundle the
native .node binary so it must remain external.

End-user verification (npm pack + clean install in /tmp/aqe-canary):
- 183 packages (was 180 pre-migration; same order of magnitude)
- 0 vulnerabilities (was 5: 4 high, 1 critical)
- aqe init --auto runs in 93 ms; all 13 phases pass; optional-peers
  hint renders correctly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 09:27:22 +00:00

192 lines
6.6 KiB
JavaScript

#!/usr/bin/env node
/**
* Build script for MCP bundle
* Injects version from root package.json at build time
*
* Uses createRequire shim for native/CJS modules to fix ESM resolution
* in Node.js 22+ where legacyMainResolve fails for packages without
* an "exports" field (e.g. better-sqlite3 with main: "lib/index.js").
*/
import { build } from 'esbuild';
import { readFileSync } from 'fs';
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';
const __dirname = dirname(fileURLToPath(import.meta.url));
// Read version from ROOT package.json (published package)
const rootPkgPath = join(__dirname, '..', 'package.json');
const rootPkg = JSON.parse(readFileSync(rootPkgPath, 'utf-8'));
const version = rootPkg.version;
console.log(`Building MCP with version: ${version}`);
// Native modules with CJS-only exports or native addons.
// These fail with ESM static import in Node.js 22+ due to missing
// "exports" field in their package.json — legacyMainResolve cannot
// resolve the entry point. We intercept these and use createRequire().
const nativeModules = [
'better-sqlite3',
'hnswlib-node',
'@ruvector/attention',
'@ruvector/gnn',
'@ruvector/sona',
'@ruvector/attention-darwin-arm64',
'@ruvector/attention-linux-arm64-gnu',
'@ruvector/gnn-darwin-arm64',
'@ruvector/gnn-linux-arm64-gnu',
'@ruvector/rvf-node',
'@ruvector/rvf-node-darwin-arm64',
'@ruvector/rvf-node-darwin-x64',
'@ruvector/rvf-node-linux-arm64-gnu',
'@ruvector/rvf-node-linux-x64-gnu',
'@ruvector/rvf-node-win32-x64-msvc',
'@huggingface/transformers',
'onnxruntime-node',
'onnxruntime-web',
'vibium',
'@claude-flow/browser',
'prime-radiant-advanced-wasm',
'pg',
'pg-native',
'web-tree-sitter',
];
// Pure JS externals that work fine with ESM import
const esmExternals = [
'fast-glob',
'fast-json-patch',
'yaml',
'commander',
'chalk',
'cli-progress',
'ora',
'express',
// @faker-js/faker is a devDep loaded lazily from test-data-generator.ts.
// Keeping it external means it is never bundled into the shipped MCP —
// callers that invoke test-data generation need to install it themselves.
'@faker-js/faker',
];
/**
* esbuild plugin: Lazy-load typescript via createRequire().
*
* typescript is a devDependency — it won't be available when users install
* agentic-qe globally. A top-level ESM `import` would crash the entire CLI
* (even `aqe --version`). This plugin replaces the static import with a
* lazy createRequire() call that only executes when the TypeScript parser
* is actually used, and throws a helpful error if typescript is missing.
*/
/**
* esbuild plugin: Lazy-load typescript via createRequire().
*
* typescript is a devDependency — it won't be available when users install
* agentic-qe globally. A top-level ESM `import` would crash the entire CLI
* (even `aqe --version`). This plugin replaces the static import with a
* lazy createRequire() call that only executes when the TypeScript parser
* is actually used, and throws a helpful error if typescript is missing.
*/
const typescriptLazyPlugin = {
name: 'typescript-lazy',
setup(build) {
build.onResolve({ filter: /^typescript$/ }, () => ({
path: 'typescript',
namespace: 'typescript-lazy',
}));
build.onLoad({ filter: /.*/, namespace: 'typescript-lazy' }, () => ({
contents: [
'import { createRequire } from "module";',
'let _ts;',
'function _load() {',
' if (!_ts) {',
' const req = createRequire(import.meta.url);',
' try { _ts = req("typescript"); }',
' catch {',
' try { _ts = req("typescript/lib/typescript.js"); }',
' catch {',
' _ts = new Proxy({}, { get(_, p) {',
' if (p === "__esModule" || typeof p === "symbol") return undefined;',
' throw new Error("TypeScript is required for code analysis. Install it: npm install -g typescript");',
' }});',
' }',
' }',
' }',
' return _ts;',
'}',
'export default new Proxy({}, {',
' get(_, p) { return _load()[p]; },',
' has(_, p) { return p in _load(); },',
' ownKeys() { return Object.keys(_load()); },',
' getOwnPropertyDescriptor(_, p) {',
' const v = _load()[p];',
' if (v !== undefined) return { configurable: true, enumerable: true, value: v };',
' },',
'});',
].join('\n'),
loader: 'js',
}));
},
};
/**
* esbuild plugin: Rewrite native/CJS module imports to use createRequire()
*/
const nativeRequirePlugin = {
name: 'native-require',
setup(build) {
const escapedNames = nativeModules.map(m =>
m.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
);
const filter = new RegExp(`^(${escapedNames.join('|')})(\/.*)?$`);
build.onResolve({ filter }, (args) => ({
path: args.path,
namespace: 'native-require',
}));
build.onLoad({ filter: /.*/, namespace: 'native-require' }, (args) => ({
contents: [
'import { createRequire } from "module";',
'const __require = createRequire(import.meta.url);',
`const __mod = __require(${JSON.stringify(args.path)});`,
'export default __mod;',
// Re-export all named exports used across the codebase
'export const {',
' RuvectorLayer, TensorCompress, differentiableSearch,',
' hierarchicalForward, getCompressionLevel, init,',
' FlashAttention, DotProductAttention, MultiHeadAttention,',
' HyperbolicAttention, LinearAttention, MoEAttention,',
' SonaEngine, pipeline,',
'} = __mod || {};',
].join('\n'),
loader: 'js',
}));
},
};
try {
await build({
entryPoints: [join(__dirname, '..', 'src/mcp/entry.ts')],
bundle: true,
minify: true,
platform: 'node',
format: 'esm',
external: esmExternals,
plugins: [typescriptLazyPlugin, nativeRequirePlugin],
outfile: join(__dirname, '..', 'dist/mcp/bundle.js'),
define: {
'__CLI_VERSION__': JSON.stringify(version),
},
banner: {
// Shebang + CJS require shim for ESM output (see build-cli.mjs for details)
js: '#!/usr/bin/env node\nimport{createRequire as __cr}from"module";const require=__cr(import.meta.url);',
},
});
console.log(`MCP bundle built successfully (v${version})`);
} catch (error) {
console.error('Build failed:', error.message);
process.exit(1);
}