UNPKG

@iqai/adk-cli

Version:

CLI tool for creating, running, and testing ADK agents

1,165 lines (1,149 loc) 36.5 kB
#!/usr/bin/env node // src/index.ts import chalk5 from "chalk"; import { program } from "commander"; // package.json var package_default = { name: "@iqai/adk-cli", version: "0.2.6", description: "CLI tool for creating, running, and testing ADK agents", main: "dist/index.js", types: "dist/index.d.ts", bin: { adk: "./dist/index.mjs" }, scripts: { build: "tsup", dev: "tsup --watch", test: "vitest run", "test:watch": "vitest" }, repository: { type: "git", url: "https://github.com/IQAIcom/adk-ts.git", directory: "packages/adk-cli" }, keywords: [ "ai", "llm", "agent", "cli", "adk", "typescript" ], author: "IQAI", license: "MIT", dependencies: { "@clack/prompts": "^0.11.0", "@hono/node-server": "^1.18.1", "@iqai/adk": "workspace:*", chalk: "^5.4.1", commander: "^12.1.0", dedent: "^1.6.0", giget: "^2.0.0", hono: "^4.6.13", marked: "^14.1.3", "marked-terminal": "^7.2.1", esbuild: "^0.23.0" }, devDependencies: { "@iqai/tsconfig": "workspace:*", "@types/marked-terminal": "^6.1.1", "@types/node": "^20.17.30", tsup: "^8.4.0", typescript: "^5.3.2", vitest: "^3.1.3" }, packageManager: "pnpm@9.0.0", engines: { node: ">=22.0" }, files: [ "dist", "!**/*.test.*", "!**/*.json", "CHANGELOG.md", "LICENSE", "README.md" ], publishConfig: { access: "public" } }; // src/commands/new.ts import { existsSync } from "fs"; import { join } from "path"; import { confirm, intro, outro, select, spinner, text } from "@clack/prompts"; import chalk from "chalk"; import dedent from "dedent"; import { downloadTemplate } from "giget"; var templates = [ { value: "simple-agent", label: "\u{1F916} Simple Agent", hint: "Basic agent with chat capabilities", source: "github:IQAIcom/adk-ts/apps/starter-templates/simple-agent" }, { value: "discord-bot", label: "\u{1F3AE} Discord Bot", hint: "Agent integrated with Discord", source: "github:IQAIcom/adk-ts/apps/starter-templates/discord-bot" }, { value: "telegram-bot", label: "\u{1F4F1} Telegram Bot", hint: "Agent integrated with Telegram", source: "github:IQAIcom/adk-ts/apps/starter-templates/telegram-bot" }, { value: "hono-server", label: "\u{1F680} Hono Server", hint: "Web server with agent endpoints", source: "github:IQAIcom/adk-ts/apps/starter-templates/hono-server" }, { value: "mcp-starter", label: "\u{1F50C} MCP Integration", hint: "Model Context Protocol server", source: "github:IQAIcom/adk-ts/apps/starter-templates/mcp-starter" } ]; var packageManagers = [ { name: "npm", command: "npm", args: ["install"], label: "\u{1F4E6} npm" }, { name: "pnpm", command: "pnpm", args: ["install"], label: "\u26A1 pnpm" }, { name: "yarn", command: "yarn", args: ["install"], label: "\u{1F9F6} yarn" }, { name: "bun", command: "bun", args: ["install"], label: "\u{1F35E} bun" } ]; async function detectAvailablePackageManagers() { const { spawn } = await import("child_process"); const available = []; for (const pm of packageManagers) { try { await new Promise((resolve2, reject) => { const child = spawn(pm.command, ["--version"], { stdio: "pipe" }); child.on("close", (code) => { if (code === 0) { available.push(pm); } resolve2(); }); child.on("error", () => resolve2()); }); } catch { } } return available.length > 0 ? available : [packageManagers[0]]; } async function createProject(projectName, options) { console.clear(); intro(chalk.magentaBright("\u{1F9E0} Create new ADK-TS project")); let finalProjectName = projectName; if (!finalProjectName) { const response = await text({ message: "What is your project name?", placeholder: "my-adk-project", validate: (value) => { if (!value) return "Project name is required"; if (value.includes(" ")) return "Project name cannot contain spaces"; if (existsSync(value)) return `Directory "${value}" already exists`; return void 0; } }); if (typeof response === "symbol") { outro("Operation cancelled"); process.exit(0); } finalProjectName = response; } let selectedTemplate = options?.template; if (!selectedTemplate || !templates.find((t) => t.value === selectedTemplate)) { const framework = await select({ message: "Which template would you like to use?", options: templates.map((t) => ({ value: t.value, label: t.label, hint: t.hint })) }); if (typeof framework === "symbol") { outro("Operation cancelled"); process.exit(0); } selectedTemplate = framework; } const template = templates.find((t) => t.value === selectedTemplate); if (!template) { outro("Invalid template selected"); process.exit(1); } if (existsSync(finalProjectName)) { outro(chalk.red(`Directory "${finalProjectName}" already exists`)); process.exit(1); } const s = spinner(); s.start("Downloading template..."); try { await downloadTemplate(template.source, { dir: finalProjectName, registry: "gh" }); s.stop("Template downloaded!"); } catch (error) { s.stop("Failed to download template"); outro(chalk.red(`Error: ${error}`)); process.exit(1); } const availablePackageManagers = await detectAvailablePackageManagers(); let selectedPackageManager; if (availablePackageManagers.length === 1) { selectedPackageManager = availablePackageManagers[0]; } else { const packageManagerChoice = await select({ message: "Which package manager would you like to use?", options: availablePackageManagers.map((pm) => ({ value: pm.name, label: pm.label })) }); if (typeof packageManagerChoice === "symbol") { outro("Operation cancelled"); process.exit(0); } selectedPackageManager = availablePackageManagers.find( (pm) => pm.name === packageManagerChoice ); } const shouldInstall = await confirm({ message: "Install dependencies?", initialValue: true }); if (typeof shouldInstall === "symbol") { outro("Operation cancelled"); process.exit(0); } if (shouldInstall) { const s2 = spinner(); s2.start(`Installing dependencies with ${selectedPackageManager.name}...`); const { spawn } = await import("child_process"); const projectPath = join(process.cwd(), finalProjectName); try { await new Promise((resolve2, reject) => { const child = spawn( selectedPackageManager.command, selectedPackageManager.args, { cwd: projectPath, stdio: "pipe" } ); child.on("close", (code) => { if (code === 0) { resolve2(); } else { reject(new Error(`Package installation failed with code ${code}`)); } }); child.on("error", reject); }); s2.stop("Dependencies installed!"); } catch (error) { s2.stop("Failed to install dependencies"); console.log( chalk.yellow("\nYou can install dependencies manually by running:") ); console.log( chalk.cyan( `cd ${finalProjectName} && ${selectedPackageManager.command} ${selectedPackageManager.args.join(" ")}` ) ); } } outro( chalk.green(dedent` 🎉 Project created successfully! Next steps: ${chalk.cyan(`cd ${finalProjectName}`)} ${shouldInstall ? "" : chalk.cyan(`${selectedPackageManager.command} ${selectedPackageManager.args.join(" ")}`)} ${chalk.cyan("npm run dev")} or ${chalk.cyan("yarn dev")} or ${chalk.cyan("pnpm dev")} Happy coding! 🚀 `) ); } // src/commands/run.ts import * as p from "@clack/prompts"; import { log, spinner as spinner2 } from "@clack/prompts"; import chalk3 from "chalk"; import { marked } from "marked"; import { markedTerminal } from "marked-terminal"; // src/commands/serve.ts import { existsSync as existsSync3 } from "fs"; import { resolve } from "path"; import chalk2 from "chalk"; // src/server/index.ts import { serve } from "@hono/node-server"; import { InMemorySessionService } from "@iqai/adk"; import { Hono } from "hono"; // src/server/routes.ts import { cors } from "hono/cors"; function setupRoutes(app, agentManager, sessionManager, agentsDir) { app.use("/*", cors()); app.get("/health", (c) => c.json({ status: "ok" })); app.get("/api/agents", (c) => { const agentsList = Array.from( agentManager.getAgents().values() ).map((agent) => ({ path: agent.absolutePath, name: agent.name, directory: agent.absolutePath, relativePath: agent.relativePath })); return c.json({ agents: agentsList }); }); app.post("/api/agents/refresh", (c) => { agentManager.scanAgents(agentsDir); const agentsList = Array.from( agentManager.getAgents().values() ).map((agent) => ({ path: agent.absolutePath, name: agent.name, directory: agent.absolutePath, relativePath: agent.relativePath })); return c.json({ agents: agentsList }); }); app.get("/api/agents/:id/messages", async (c) => { const agentPath = decodeURIComponent(c.req.param("id")); const loadedAgent = agentManager.getLoadedAgents().get(agentPath); if (!loadedAgent) { return c.json({ messages: [] }); } const messages = await sessionManager.getSessionMessages(loadedAgent); const response = { messages }; return c.json(response); }); app.post("/api/agents/:id/message", async (c) => { const agentPath = decodeURIComponent(c.req.param("id")); const { message, attachments } = await c.req.json(); const response = await agentManager.sendMessageToAgent( agentPath, message, attachments ); const messageResponse = { response }; return c.json(messageResponse); }); } // src/server/services.ts import { existsSync as existsSync2, mkdirSync, readFileSync, readdirSync, statSync, unlinkSync } from "fs"; import { dirname, join as join2, relative } from "path"; import { pathToFileURL } from "url"; import { AgentBuilder } from "@iqai/adk"; var AgentScanner = class { constructor(quiet = false) { this.quiet = quiet; } scanAgents(agentsDir, loadedAgents) { const agents = /* @__PURE__ */ new Map(); const scanDir = !agentsDir || !existsSync2(agentsDir) ? process.cwd() : agentsDir; const shouldSkipDirectory = (dirName) => { const skipDirs = [ "node_modules", ".git", ".next", "dist", "build", ".turbo", "coverage", ".vscode", ".idea" ]; return skipDirs.includes(dirName); }; const scanDirectory = (dir) => { if (!existsSync2(dir)) return; const items = readdirSync(dir); for (const item of items) { const fullPath = join2(dir, item); const stat = statSync(fullPath); if (stat.isDirectory()) { if (!shouldSkipDirectory(item)) { scanDirectory(fullPath); } } else if (item === "agent.ts" || item === "agent.js") { const relativePath = relative(scanDir, dir); const loadedAgent = loadedAgents.get(relativePath); let agentName = relativePath.split("/").pop() || "unknown"; if (loadedAgent?.agent?.name) { agentName = loadedAgent.agent.name; } else { try { const agentFilePath = join2(dir, item); agentName = this.extractAgentNameFromFile(agentFilePath) || agentName; } catch { } } agents.set(relativePath, { relativePath, name: agentName, absolutePath: dir, instance: loadedAgent?.agent }); } } }; scanDirectory(scanDir); if (!this.quiet) { console.log(`\u2705 Agent scan complete. Found ${agents.size} agents.`); } return agents; } extractAgentNameFromFile(filePath) { try { const content = readFileSync(filePath, "utf-8"); const nameMatch = content.match(/name\s*:\s*["']([^"']+)["']/); if (nameMatch?.[1]) { return nameMatch[1]; } return null; } catch { return null; } } }; var AgentLoader = class { constructor(quiet = false) { this.quiet = quiet; } /** * Import a TypeScript file by compiling it on-demand */ async importTypeScriptFile(filePath) { const startDir = dirname(filePath); let projectRoot = startDir; while (projectRoot !== "/" && projectRoot !== dirname(projectRoot)) { if (existsSync2(join2(projectRoot, "package.json")) || existsSync2(join2(projectRoot, ".env"))) { break; } projectRoot = dirname(projectRoot); } if (projectRoot === "/") { projectRoot = startDir; } try { const { build } = await import("esbuild"); const cacheDir = join2(projectRoot, ".adk-cache"); if (!existsSync2(cacheDir)) { mkdirSync(cacheDir, { recursive: true }); } const outFile = join2(cacheDir, `agent-${Date.now()}.mjs`); const plugin = { name: "externalize-bare-imports", setup(build2) { build2.onResolve({ filter: /.*/ }, (args) => { if (args.path.startsWith(".") || args.path.startsWith("/") || args.path.startsWith("..")) { return; } return { path: args.path, external: true }; }); } }; const tsconfigPath = join2(projectRoot, "tsconfig.json"); await build({ entryPoints: [filePath], outfile: outFile, bundle: true, format: "esm", platform: "node", target: ["node22"], sourcemap: false, logLevel: "silent", plugins: [plugin], absWorkingDir: projectRoot, // Use tsconfig if present for path aliases ...existsSync2(tsconfigPath) ? { tsconfig: tsconfigPath } : {} }); const mod = await import(`${pathToFileURL(outFile).href}?t=${Date.now()}`); let agentExport = mod?.agent; if (!agentExport && mod?.default) { agentExport = mod.default.agent ?? mod.default; } try { unlinkSync(outFile); } catch { } if (agentExport) { const isPrimitive = (v) => v == null || ["string", "number", "boolean"].includes(typeof v); if (isPrimitive(agentExport)) { if (!this.quiet) { console.log( `\u2139\uFE0F Ignoring primitive 'agent' export in ${filePath}; scanning module for factory...` ); } } else { if (!this.quiet) { console.log(`\u2705 TS agent imported via esbuild: ${filePath}`); } return { agent: agentExport }; } } return mod; } catch (e) { throw new Error( `Failed to import TS agent via esbuild: ${e instanceof Error ? e.message : String(e)}` ); } } loadEnvironmentVariables(agentFilePath) { let projectRoot = dirname(agentFilePath); while (projectRoot !== "/" && projectRoot !== dirname(projectRoot)) { if (existsSync2(join2(projectRoot, "package.json")) || existsSync2(join2(projectRoot, ".env"))) { break; } projectRoot = dirname(projectRoot); } const envFiles = [ ".env.local", ".env.development.local", ".env.production.local", ".env.development", ".env.production", ".env" ]; for (const envFile of envFiles) { const envPath = join2(projectRoot, envFile); if (existsSync2(envPath)) { try { const envContent = readFileSync(envPath, "utf8"); const envLines = envContent.split("\n"); for (const line of envLines) { const trimmedLine = line.trim(); if (trimmedLine && !trimmedLine.startsWith("#")) { const [key, ...valueParts] = trimmedLine.split("="); if (key && valueParts.length > 0) { const value = valueParts.join("=").replace(/^"(.*)"$/, "$1"); if (!process.env[key.trim()]) { process.env[key.trim()] = value.trim(); } } } } } catch (error) { console.warn( `\u26A0\uFE0F Warning: Could not load ${envFile} file: ${error instanceof Error ? error.message : String(error)}` ); } } } } // Minimal resolution logic for agent exports: supports // 1) export const agent = new LlmAgent(...) // 2) export function agent() { return new LlmAgent(...) } // 3) export async function agent() { return new LlmAgent(...) } // 4) default export (object or function) returning or containing .agent async resolveAgentExport(mod) { let candidate = mod?.agent ?? mod?.default?.agent ?? mod?.default ?? mod; const isLikelyAgentInstance = (obj) => obj && typeof obj === "object" && typeof obj.name === "string"; const isPrimitive = (v) => v == null || ["string", "number", "boolean"].includes(typeof v); const invokeMaybe = async (fn) => { let out = fn(); if (out && typeof out === "object" && "then" in out) { out = await out; } return out; }; if (!isLikelyAgentInstance(candidate) && isPrimitive(candidate) || !isLikelyAgentInstance(candidate) && candidate && candidate === mod) { candidate = mod; for (const [key, value] of Object.entries(mod)) { if (key === "default") continue; const keyLower = key.toLowerCase(); if (isPrimitive(value)) continue; if (isLikelyAgentInstance(value)) { candidate = value; break; } if (value && typeof value === "object" && value.agent && isLikelyAgentInstance(value.agent)) { candidate = value.agent; break; } if (typeof value === "function" && (/(agent|build|create)/i.test(keyLower) || value.name && /(agent|build|create)/i.test(value.name.toLowerCase()))) { try { const maybe = await invokeMaybe(value); if (isLikelyAgentInstance(maybe)) { candidate = maybe; break; } if (maybe && typeof maybe === "object" && maybe.agent && isLikelyAgentInstance(maybe.agent)) { candidate = maybe.agent; break; } } catch (e) { } } } } if (typeof candidate === "function") { try { candidate = await invokeMaybe(candidate); } catch (e) { throw new Error( `Failed executing exported agent function: ${e instanceof Error ? e.message : String(e)}` ); } } if (candidate && typeof candidate === "object" && candidate.agent && isLikelyAgentInstance(candidate.agent)) { candidate = candidate.agent; } if (candidate?.agent && isLikelyAgentInstance(candidate.agent)) { candidate = candidate.agent; } if (!candidate || !isLikelyAgentInstance(candidate)) { throw new Error( "No agent export resolved (expected variable, function, or function returning an agent)" ); } return { agent: candidate }; } }; var AgentManager = class { constructor(sessionService, quiet = false) { this.sessionService = sessionService; this.quiet = quiet; this.scanner = new AgentScanner(quiet); this.loader = new AgentLoader(quiet); } agents = /* @__PURE__ */ new Map(); loadedAgents = /* @__PURE__ */ new Map(); scanner; loader; getAgents() { return this.agents; } getLoadedAgents() { return this.loadedAgents; } scanAgents(agentsDir) { this.agents = this.scanner.scanAgents(agentsDir, this.loadedAgents); } async startAgent(agentPath) { const agent = this.agents.get(agentPath); if (!agent) { throw new Error(`Agent not found: ${agentPath}`); } if (this.loadedAgents.has(agentPath)) { return; } try { let agentFilePath = join2(agent.absolutePath, "agent.js"); if (!existsSync2(agentFilePath)) { agentFilePath = join2(agent.absolutePath, "agent.ts"); } if (!existsSync2(agentFilePath)) { throw new Error( `No agent.js or agent.ts file found in ${agent.absolutePath}` ); } this.loader.loadEnvironmentVariables(agentFilePath); const agentFileUrl = pathToFileURL(agentFilePath).href; const agentModule = agentFilePath.endsWith(".ts") ? await this.loader.importTypeScriptFile(agentFilePath) : await import(agentFileUrl); const resolved = await this.loader.resolveAgentExport(agentModule); const exportedAgent = resolved.agent; if (!exportedAgent?.name) { throw new Error( `Invalid agent export in ${agentFilePath}. Expected an LlmAgent instance with a name property.` ); } const agentBuilder = AgentBuilder.create(exportedAgent.name).withAgent(exportedAgent).withSessionService(this.sessionService, { userId: `user_${agentPath}`, appName: "adk-server" }); const { runner, session } = await agentBuilder.build(); const loadedAgent = { agent: exportedAgent, runner, sessionId: session.id, userId: `user_${agentPath}`, appName: "adk-server" }; this.loadedAgents.set(agentPath, loadedAgent); agent.instance = exportedAgent; agent.name = exportedAgent.name; } catch (error) { console.error(`\u274C Failed to load agent "${agent.name}":`, error); throw new Error( `Failed to load agent: ${error instanceof Error ? error.message : String(error)}` ); } } async stopAgent(agentPath) { this.loadedAgents.delete(agentPath); const agent = this.agents.get(agentPath); if (agent) { agent.instance = void 0; } } async sendMessageToAgent(agentPath, message, attachments) { if (!this.loadedAgents.has(agentPath)) { await this.startAgent(agentPath); } const loadedAgent = this.loadedAgents.get(agentPath); if (!loadedAgent) { throw new Error("Agent failed to start"); } try { if (attachments && attachments.length > 0) { const request = { parts: [ { text: message }, ...attachments.map((file) => ({ inlineData: { mimeType: file.mimeType, data: file.data } })) ] }; const response2 = await loadedAgent.runner.ask(request); return response2; } const response = await loadedAgent.runner.ask(message); return response; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); console.error( `Error sending message to agent ${agentPath}:`, errorMessage ); throw new Error(`Failed to send message to agent: ${errorMessage}`); } } stopAllAgents() { for (const [agentPath] of this.loadedAgents.entries()) { this.stopAgent(agentPath); } } }; var SessionManager = class { constructor(sessionService) { this.sessionService = sessionService; } async getSessionMessages(loadedAgent) { try { const session = await this.sessionService.getSession( loadedAgent.appName, loadedAgent.userId, loadedAgent.sessionId ); if (!session || !session.events) { return []; } const messages = session.events.map((event, index) => ({ id: index + 1, type: event.author === "user" ? "user" : "assistant", content: event.content?.parts?.map( (part) => typeof part === "object" && "text" in part ? part.text : "" ).join("") || "", timestamp: new Date(event.timestamp || Date.now()).toISOString() })); return messages; } catch (error) { console.error("Error fetching messages:", error); return []; } } }; // src/server/index.ts var ADKServer = class { agentManager; sessionManager; sessionService; app; server; config; constructor(agentsDir, port = 8042, host = "localhost", quiet = false) { this.config = { agentsDir, port, host, quiet }; this.sessionService = new InMemorySessionService(); this.agentManager = new AgentManager(this.sessionService, quiet); this.sessionManager = new SessionManager(this.sessionService); this.app = new Hono(); setupRoutes(this.app, this.agentManager, this.sessionManager, agentsDir); this.agentManager.scanAgents(agentsDir); } async start() { return new Promise((resolve2) => { this.server = serve({ fetch: this.app.fetch, port: this.config.port, hostname: this.config.host }); setTimeout(() => { resolve2(); }, 100); }); } async stop() { return new Promise((resolve2) => { this.agentManager.stopAllAgents(); if (this.server) { this.server.close(); } resolve2(); }); } getPort() { return this.config.port; } }; // src/commands/serve.ts async function serveCommand(options = {}) { const port = options.port || 8042; const host = options.host || "localhost"; const agentsDir = resolve(options.dir || "."); if (!existsSync3(agentsDir)) { console.error(chalk2.red(`\u274C Directory not found: ${agentsDir}`)); process.exit(1); } if (!options.quiet) { console.log(chalk2.blue(`\u{1F680} ADK Server starting on http://${host}:${port}`)); } const server = new ADKServer(agentsDir, port, host, options.quiet); try { await server.start(); if (!options.quiet) { console.log(chalk2.green("\u2705 Server ready")); } const cleanup = async () => { if (!options.quiet) { console.log(chalk2.yellow("\n\u{1F6D1} Stopping server...")); } await server.stop(); process.exit(0); }; process.on("SIGINT", cleanup); process.on("SIGTERM", cleanup); return server; } catch (error) { console.error(chalk2.red("\u274C Failed to start ADK server:"), error); process.exit(1); } } // src/commands/run.ts marked.use(markedTerminal()); async function renderMarkdown(text3) { try { const result = await marked(text3); return typeof result === "string" ? result : text3; } catch (error) { return text3; } } var AgentChatClient = class { apiUrl; selectedAgent = null; constructor(apiUrl) { this.apiUrl = apiUrl; } async connect() { try { const response = await fetch(`${this.apiUrl}/health`); if (!response.ok) { throw new Error("Connection failed"); } } catch (error) { throw new Error("\u274C Connection failed"); } } async fetchAgents() { try { const response = await fetch(`${this.apiUrl}/api/agents`); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const data = await response.json(); if (Array.isArray(data)) { return data; } if (data && Array.isArray(data.agents)) { return data.agents; } throw new Error(`Unexpected response format: ${JSON.stringify(data)}`); } catch (error) { throw new Error( `Failed to fetch agents: ${error instanceof Error ? error.message : String(error)}` ); } } async selectAgent() { const agents = await this.fetchAgents(); if (agents.length === 0) { throw new Error("No agents found in the current directory"); } if (agents.length === 1) { return agents[0]; } const selectedAgent = await p.select({ message: "Choose an agent to chat with:", options: agents.map((agent) => ({ label: agent.name, value: agent, hint: agent.relativePath })) }); if (p.isCancel(selectedAgent)) { p.cancel("Operation cancelled"); process.exit(0); } return selectedAgent; } // No-op: agents are auto-loaded on message; keeping method removed async sendMessage(message) { if (!this.selectedAgent) { throw new Error("No agent selected"); } const s = spinner2(); s.start("\u{1F916} Thinking..."); try { const response = await fetch( `${this.apiUrl}/api/agents/${encodeURIComponent(this.selectedAgent.relativePath)}/message`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ message }) } ); if (!response.ok) { const errorText = await response.text(); s.stop("\u274C Failed to send message"); throw new Error(`Failed to send message: ${errorText}`); } const result = await response.json(); s.stop("\u{1F916} Assistant:"); if (result.response) { const formattedResponse = await renderMarkdown(result.response); log.message(formattedResponse.trim()); } } catch (error) { log.error("Failed to send message"); } } async startChat() { if (!this.selectedAgent) { throw new Error("Agent not selected"); } while (true) { try { const message = await p.text({ message: "\u{1F4AC} Message:", placeholder: "Type your message here..." }); if (p.isCancel(message)) { break; } if (message.trim()) { await this.sendMessage(message.trim()); } } catch (error) { console.error(chalk3.red("Error in chat:"), error); break; } } } disconnect() { } setSelectedAgent(agent) { this.selectedAgent = agent; } }; async function runAgent(agentPath, options = {}) { const envVerbose = process.env.ADK_VERBOSE; const isVerbose = options.verbose ?? (envVerbose === "1" || envVerbose === "true"); if (options.server) { const apiPort = 8042; const host = options.host || "localhost"; console.log(chalk3.blue("\u{1F680} Starting ADK Server...")); const serveOptions = { port: apiPort, dir: process.cwd(), host, quiet: !isVerbose }; try { const server = await serveCommand(serveOptions); console.log(chalk3.cyan("Press Ctrl+C to stop the server")); process.on("SIGINT", async () => { console.log(chalk3.yellow("\n\u{1F6D1} Stopping server...")); await server.stop(); process.exit(0); }); return new Promise(() => { }); } catch (error) { console.error(chalk3.red("\u274C Failed to start server")); process.exit(1); } } const apiUrl = `http://${options.host || "localhost"}:8042`; p.intro("\u{1F916} ADK Agent Chat"); try { const healthResponse = await fetch(`${apiUrl}/health`).catch(() => null); if (!healthResponse || !healthResponse.ok) { const serverSpinner = spinner2(); serverSpinner.start("\u{1F680} Starting server..."); const serveOptions = { port: 8042, // Use new default port dir: process.cwd(), host: options.host || "localhost", quiet: !isVerbose }; await serveCommand(serveOptions); await new Promise((resolve2) => setTimeout(resolve2, 1e3)); serverSpinner.stop("\u2705 Server ready"); } const client = new AgentChatClient(apiUrl); await client.connect(); const agentSpinner = spinner2(); agentSpinner.start("\u{1F50D} Scanning for agents..."); const agents = await client.fetchAgents(); let selectedAgent; if (agents.length === 0) { agentSpinner.stop("\u274C No agents found"); p.cancel("No agents found in the current directory"); process.exit(1); } else if (agents.length === 1) { selectedAgent = agents[0]; agentSpinner.stop(`\u{1F916} Found agent: ${selectedAgent.name}`); } else { agentSpinner.stop(`\u{1F916} Found ${agents.length} agents`); const choice = await p.select({ message: "Choose an agent to chat with:", options: agents.map((agent) => ({ label: agent.name, value: agent, hint: agent.relativePath })) }); if (p.isCancel(choice)) { p.cancel("Operation cancelled"); process.exit(0); } selectedAgent = choice; } client.setSelectedAgent(selectedAgent); await client.startChat(); client.disconnect(); p.outro("Chat ended"); } catch (error) { p.cancel( `Error: ${error instanceof Error ? error.message : String(error)}` ); process.exit(1); } } // src/commands/web.ts import chalk4 from "chalk"; async function webCommand(options = {}) { const apiPort = options.port || 8042; const webPort = options.webPort || 3e3; const host = options.host || "localhost"; const useLocal = options.local || false; const webUrl = options.webUrl || "https://adk-web.iqai.com"; console.log(chalk4.blue("\u{1F310} Starting ADK Web Interface...")); const serveOptions = { port: apiPort, dir: options.dir, host, quiet: true }; await serveCommand(serveOptions); let webAppUrl; if (useLocal) { if (apiPort === 8042) { webAppUrl = `http://${host}:${webPort}`; } else { webAppUrl = `http://${host}:${webPort}?port=${apiPort}`; } } else { if (apiPort === 8042) { webAppUrl = webUrl; } else { webAppUrl = `${webUrl}?port=${apiPort}`; } } console.log(chalk4.cyan(`\u{1F517} Open this URL in your browser: ${webAppUrl}`)); console.log(chalk4.gray(` API Server: http://${host}:${apiPort}`)); console.log(chalk4.cyan("Press Ctrl+C to stop the API server")); } // src/index.ts program.name("adk").description(package_default.description).version(package_default.version); program.command("new").description("Create a new ADK project").argument("[project-name]", "Name of the project to create").option( "-t, --template <template>", "Template to use (simple-agent, discord-bot, telegram-bot, hono-server, mcp-starter)" ).action(async (projectName, options) => { try { await createProject(projectName, options); } catch (error) { console.error(chalk5.red("Error creating project:"), error); process.exit(1); } }); program.command("run").description("Start an interactive chat with an agent").argument( "[agent-path]", "Path to specific agent (optional - will show selector if multiple agents found)" ).option("-s, --server", "Start ADK server only (without chat interface)").option( "-h, --host <host>", "Host for server (when using --server)", "localhost" ).action(async (agentPath, options) => { try { await runAgent(agentPath, options); } catch (error) { console.error(chalk5.red("Error running agent:"), error); process.exit(1); } }); program.command("web").description("Start a web interface for testing agents").option("-p, --port <port>", "Port for API server", "8042").option("--web-port <port>", "Port for web app (when using --local)", "3000").option("-h, --host <host>", "Host for servers", "localhost").option( "-d, --dir <directory>", "Directory to scan for agents (default: current directory)", "." ).option( "--local", "Run local web app instead of opening production URL", false ).option( "--web-url <url>", "URL of the web application (used when not --local)", "https://adk-web.iqai.com" ).action(async (options) => { try { await webCommand(options); } catch (error) { console.error(chalk5.red("Error starting web UI:"), error); process.exit(1); } }); program.command("serve").description("Start an API server for agent management").option("-p, --port <port>", "Port for the server", "8042").option("-h, --host <host>", "Host for the server", "localhost").option( "-d, --dir <directory>", "Directory to scan for agents (default: current directory)", "." ).action(async (options) => { try { await serveCommand(options); } catch (error) { console.error(chalk5.red("Error starting server:"), error); process.exit(1); } }); program.parse(); //# sourceMappingURL=index.mjs.map