@iqai/adk-cli
Version:
CLI tool for creating, running, and testing ADK-TS agents
272 lines • 12.7 kB
JavaScript
;
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