UNPKG

@iqai/adk-cli

Version:

CLI tool for creating, running, and testing ADK-TS agents

272 lines 12.7 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var AgentLoader_1; Object.defineProperty(exports, "__esModule", { value: true }); exports.AgentLoader = void 0; const node_crypto_1 = require("node:crypto"); const node_fs_1 = require("node:fs"); const node_module_1 = require("node:module"); const node_path_1 = require("node:path"); const node_url_1 = require("node:url"); const common_1 = require("@nestjs/common"); const find_project_root_1 = require("../../common/find-project-root"); const agent_resolver_1 = require("./agent-loader/agent-resolver"); const cache_utils_1 = require("./agent-loader/cache-utils"); const env_utils_1 = require("./agent-loader/env-utils"); const error_handling_utils_1 = require("./agent-loader/error-handling-utils"); const path_utils_1 = require("./agent-loader/path-utils"); const type_guards_1 = require("./agent-loader/type-guards"); let AgentLoader = class AgentLoader { static { AgentLoader_1 = this; } quiet; logger; static cacheCleanupRegistered = false; cacheUtils; envUtils; pathUtils; errorUtils; guards; resolver; static activeCacheFiles = new Set(); static projectRoots = new Set(); constructor(quiet = false) { this.quiet = quiet; this.logger = new common_1.Logger("agent-loader"); this.registerCleanupHandlers(); this.cacheUtils = new cache_utils_1.CacheUtils(this.logger, this.quiet); this.envUtils = new env_utils_1.EnvUtils(this.logger, this.quiet); this.pathUtils = new path_utils_1.PathUtils(this.logger, this.quiet); this.errorUtils = new error_handling_utils_1.ErrorHandlingUtils(this.logger); this.guards = new type_guards_1.TypeGuards(); this.resolver = new agent_resolver_1.AgentResolver(this.logger, this.quiet, this.guards); } registerCleanupHandlers() { if (AgentLoader_1.cacheCleanupRegistered) return; AgentLoader_1.cacheCleanupRegistered = true; process.on("uncaughtException", (error) => { this.logger.error("Uncaught exception:", error); process.exit(1); }); process.on("unhandledRejection", (reason, promise) => { this.logger.error("Unhandled rejection at:", promise, "reason:", reason); process.exit(1); }); } static cleanupAllCacheFiles(logger, quiet = false) { cache_utils_1.CacheUtils.cleanupAllCacheFiles(logger, quiet); } loadEnvironmentVariables(agentFilePath) { this.envUtils.loadEnvironmentVariables(agentFilePath); } /** * Check if a rebuild is needed based on file modification times */ isRebuildNeeded(outFile, sourceFile, tsconfigPath) { if (!(0, node_fs_1.existsSync)(outFile)) { return true; } try { const outStat = (0, node_fs_1.statSync)(outFile); const srcStat = (0, node_fs_1.statSync)(sourceFile); const tsconfigMtime = (0, node_fs_1.existsSync)(tsconfigPath) ? (0, node_fs_1.statSync)(tsconfigPath).mtimeMs : 0; const needRebuild = !(outStat.mtimeMs >= srcStat.mtimeMs && outStat.mtimeMs >= tsconfigMtime); if (!needRebuild && !this.quiet) { this.logger.debug(`Reusing cached build: ${outFile}`); } return needRebuild; } catch (error) { if (!this.quiet) { this.logger.warn(`Failed to check cache freshness for ${outFile}: ${error instanceof Error ? error.message : String(error)}. Forcing rebuild.`); } return true; } } /** * Track a cache file for cleanup */ trackCacheFile(filePath, projectRoot) { AgentLoader_1.activeCacheFiles.add(filePath); AgentLoader_1.projectRoots.add(projectRoot); } /** * Normalize path to use forward slashes (cross-platform) */ normalizePath(path) { return path.replace(/\\/g, "/"); } /** * Import a TypeScript file by compiling it on-demand * @param filePath - Path to the TypeScript file * @param providedProjectRoot - Optional project root path * @param forceInvalidateCache - Force cache invalidation (full reload) */ async importTypeScriptFile(filePath, providedProjectRoot, forceInvalidateCache) { const normalizedFilePath = (0, node_path_1.normalize)((0, node_path_1.resolve)(filePath)); const projectRoot = providedProjectRoot ?? (0, find_project_root_1.findProjectRoot)((0, node_path_1.dirname)(normalizedFilePath)); if (!this.quiet) { this.logger.log(`Using project root: ${projectRoot} for agent: ${normalizedFilePath}`); } try { const { build } = await Promise.resolve().then(() => __importStar(require("esbuild"))); const cacheDir = (0, node_path_1.join)(projectRoot, cache_utils_1.CACHE_DIR); if (!(0, node_fs_1.existsSync)(cacheDir)) { (0, node_fs_1.mkdirSync)(cacheDir, { recursive: true }); } // Deterministic cache file path per source file const cacheKey = (0, node_crypto_1.createHash)("sha1") .update(this.pathUtils.normalizePath(normalizedFilePath)) .digest("hex"); const outFile = (0, node_path_1.normalize)((0, node_path_1.join)(cacheDir, `agent-${cacheKey}.cjs`)); this.cacheUtils.trackCacheFile(outFile, projectRoot); const tsconfigPath = (0, node_path_1.join)(projectRoot, "tsconfig.json"); // Check if we need to rebuild // Force rebuild if explicitly requested (e.g., initial state changed) const needRebuild = forceInvalidateCache || this.isRebuildNeeded(outFile, normalizedFilePath, tsconfigPath); if (forceInvalidateCache && !this.quiet) { this.logger.log(`Forcing cache invalidation for ${normalizedFilePath}`); } const plugins = [ this.pathUtils.createPathMappingPlugin(projectRoot), this.pathUtils.createExternalizePlugin(), ]; if (needRebuild) { // Delete old cache file before rebuilding try { if ((0, node_fs_1.existsSync)(outFile)) { (0, node_fs_1.unlinkSync)(outFile); if (!this.quiet) { this.logger.debug(`Deleted old cache file: ${outFile}`); } } } catch (error) { if (!this.quiet) { this.logger.warn(`Failed to delete old cache file ${outFile}: ${error instanceof Error ? error.message : String(error)}`); } } await build({ entryPoints: [this.pathUtils.normalizePath(normalizedFilePath)], outfile: outFile, bundle: true, format: "cjs", platform: "node", target: ["node22"], sourcemap: false, logLevel: "silent", plugins, absWorkingDir: projectRoot, external: ["@iqai/adk"], ...((0, node_fs_1.existsSync)(tsconfigPath) ? { tsconfig: tsconfigPath } : {}), }); } const dynamicRequire = (0, node_module_1.createRequire)(outFile); // Bust require cache if we rebuilt try { if (needRebuild) { const resolved = dynamicRequire.resolve ? dynamicRequire.resolve(outFile) : outFile; if (dynamicRequire.cache?.[resolved]) { delete dynamicRequire.cache[resolved]; } } } catch (error) { if (!this.quiet) { this.logger.warn(`Failed to invalidate require cache for ${outFile}: ${error instanceof Error ? error.message : String(error)}. Stale code may be executed.`); } } let mod; try { mod = dynamicRequire(outFile); } catch (loadErr) { this.logger.warn(`Primary require failed for built agent '${outFile}': ${loadErr instanceof Error ? loadErr.message : String(loadErr)}. Falling back to dynamic import...`); try { mod = (await Promise.resolve(`${(0, node_url_1.pathToFileURL)(outFile).href}`).then(s => __importStar(require(s)))); } catch (fallbackErr) { // Handle env-related import errors mod = await this.errorUtils.handleImportError(fallbackErr, outFile, projectRoot); } } this.logger.log(`TS agent imported via esbuild: ${normalizedFilePath} ✅`); return mod; } catch (e) { const msg = e instanceof Error ? e.message : String(e); const envCheck = this.errorUtils.isMissingEnvError(e); if (!envCheck.isMissing) { if ("formatUserError" in this.errorUtils) { this.logger.error(this.errorUtils.formatUserError(e)); } else { this.logger.error(`❌ Error loading TypeScript agent: ${msg}`); } } if (/Cannot find module/.test(msg)) { this.logger.error(`Module resolution failed while loading agent file '${filePath}'.\n> ${msg}\n` + "This usually means the dependency is declared in a parent workspace package and got externalized,\n" + "but is not installed in the agent project's own node_modules.\n" + "Fix: add it to the agent project's package.json or run: pnpm add <missing-pkg> -F <agent-workspace>."); } throw new Error(`Failed to import TS agent via esbuild: ${msg}`); } } async resolveAgentExport(mod) { const agent = await this.resolver.resolveAgentExport(mod); return { agent }; } }; exports.AgentLoader = AgentLoader; exports.AgentLoader = AgentLoader = AgentLoader_1 = __decorate([ (0, common_1.Injectable)(), __metadata("design:paramtypes", [Object]) ], AgentLoader); //# sourceMappingURL=agent-loader.service.js.map