n8n/packages/cli/scripts/bundle-agent-library.mjs
yehorkardash 64079ad98c
feat(core): Agents as first class entities support (no-changelog) (#28017)
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
Co-authored-by: Michael Drury <michael.drury@n8n.io>
Co-authored-by: Arvin A <51036481+DeveloperTheExplorer@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Arvin Ansari <arvin.ansari@n8n.io>
Co-authored-by: bjorger <50590409+bjorger@users.noreply.github.com>
Co-authored-by: Eugene <eugene@n8n.io>
Co-authored-by: Michael Drury <me@michaeldrury.co.uk>
Co-authored-by: Robin Braumann <robin.braumann@n8n.io>
Co-authored-by: Rob Hough <robhough180@gmail.com>
2026-05-06 15:44:44 +00:00

92 lines
3.2 KiB
JavaScript

#!/usr/bin/env node
/**
* Pre-bundles @n8n/agents (Tool SDK) + zod into a single CJS string consumed
* by the V8 isolate at runtime (see src/modules/agents/runtime/agent-secure-runtime.ts).
*
* Running this at build time means:
* - No esbuild on the hot path at first-request
* - No esbuild native binary needed in the Docker image at runtime
*
* Output: packages/cli/dist/agent-library-bundle.js
*/
import * as esbuild from 'esbuild';
import { createRequire } from 'module';
import path from 'path';
import { fileURLToPath } from 'url';
import { writeFileSync, mkdirSync } from 'fs';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const CLI_ROOT = path.resolve(__dirname, '..');
const OUTPUT_FILE = path.resolve(CLI_ROOT, 'dist', 'agent-library-bundle.js');
export async function buildAgentLibraryBundle({ silent = false } = {}) {
// Resolve @n8n/agents from the cli package so we get the workspace-linked
// copy regardless of where this script is invoked from.
const requireFromCli = createRequire(path.join(CLI_ROOT, 'package.json'));
const toSlash = (p) => p.replace(/\\/g, '/');
const agentsPath = toSlash(requireFromCli.resolve('@n8n/agents'));
const agentsSrcDir = agentsPath.replace(/dist\/index\.js$/, 'dist/');
// Import only the Tool builder (needed for describe() + handler execution)
// rather than the full barrel — the runtime pulls in MCP SDK, AI provider
// SDKs, database drivers, etc., none of which are usable inside the isolate.
const shim = `
const { Tool } = require('${agentsSrcDir}sdk/tool');
const zod = require('zod');
globalThis.__modules = {
'@n8n/agents': { Tool },
'zod': zod,
};
`;
const result = await esbuild.build({
stdin: { contents: shim, loader: 'js', resolveDir: CLI_ROOT },
bundle: true,
format: 'cjs',
target: 'es2022',
platform: 'node',
write: false,
treeShaking: true,
// The shim only pulls in `sdk/tool` (+ zod), which is pure JS with no
// Node built-in or native deps — esbuild currently externalises nothing.
// `node:*` stays as a safety net so that if some future transitive dep
// picks up a `node:<builtin>` import, esbuild marks it external rather
// than trying to bundle a built-in (which would blow up inside the
// isolate, which has no Node globals anyway).
external: ['node:*'],
define: {
'process.env.NODE_ENV': '"production"',
},
});
if (result.errors.length > 0) {
throw new Error(
`Failed to bundle agent library: ${result.errors.map((e) => e.text).join('\n')}`,
);
}
const bundle = result.outputFiles[0].text;
mkdirSync(path.dirname(OUTPUT_FILE), { recursive: true });
writeFileSync(OUTPUT_FILE, bundle, 'utf8');
if (!silent) {
const sizeKB = (bundle.length / 1024).toFixed(1);
const sizeMB = (bundle.length / (1024 * 1024)).toFixed(2);
console.log(
`[bundle-agent-library] Wrote ${path.relative(CLI_ROOT, OUTPUT_FILE)}${sizeKB} KB (${sizeMB} MB)`,
);
}
return OUTPUT_FILE;
}
// Allow running as a standalone script: `node scripts/bundle-agent-library.mjs`
const isMain = process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url);
if (isMain) {
buildAgentLibraryBundle().catch((e) => {
console.error(e);
process.exit(1);
});
}