@iqai/adk-cli 0.1.1 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +45 -0
- package/dist/index.js +342 -232
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +343 -233
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -8,7 +8,7 @@ import { program } from "commander";
|
|
|
8
8
|
// package.json
|
|
9
9
|
var package_default = {
|
|
10
10
|
name: "@iqai/adk-cli",
|
|
11
|
-
version: "0.
|
|
11
|
+
version: "0.2.1",
|
|
12
12
|
description: "CLI tool for creating, running, and testing ADK agents",
|
|
13
13
|
main: "dist/index.js",
|
|
14
14
|
types: "dist/index.d.ts",
|
|
@@ -143,21 +143,7 @@ async function detectAvailablePackageManagers() {
|
|
|
143
143
|
}
|
|
144
144
|
async function createProject(projectName, options) {
|
|
145
145
|
console.clear();
|
|
146
|
-
|
|
147
|
-
chalk.magentaBright(dedent`
|
|
148
|
-
╔══════════════════════════════════════════════════════╗
|
|
149
|
-
║ ║
|
|
150
|
-
║ █████╗ ██████╗ ██╗ ██╗ ████████╗███████╗ ║
|
|
151
|
-
║ ██╔══██╗██╔══██╗██║ ██╔╝ ╚══██╔══╝██╔════╝ ║
|
|
152
|
-
║ ███████║██║ ██║█████╔╝ ██║ ███████╗ ║
|
|
153
|
-
║ ██╔══██║██║ ██║██╔═██╗ ██║ ╚════██║ ║
|
|
154
|
-
║ ██║ ██║██████╔╝██║ ██╗ ██║ ███████║ ║
|
|
155
|
-
║ ╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚══════╝ ║
|
|
156
|
-
║ ║
|
|
157
|
-
╚══════════════════════════════════════════════════════╝
|
|
158
|
-
`)
|
|
159
|
-
);
|
|
160
|
-
intro(chalk.bgMagenta.bold(" \u2728 Let's build something amazing! "));
|
|
146
|
+
intro(chalk.magentaBright("\u{1F9E0} Create new ADK-TS project"));
|
|
161
147
|
let finalProjectName = projectName;
|
|
162
148
|
if (!finalProjectName) {
|
|
163
149
|
const response = await text({
|
|
@@ -305,7 +291,59 @@ import { existsSync as existsSync3 } from "fs";
|
|
|
305
291
|
import { resolve } from "path";
|
|
306
292
|
import chalk2 from "chalk";
|
|
307
293
|
|
|
308
|
-
// src/server.ts
|
|
294
|
+
// src/server/index.ts
|
|
295
|
+
import { serve } from "@hono/node-server";
|
|
296
|
+
import { InMemorySessionService } from "@iqai/adk";
|
|
297
|
+
import { Hono } from "hono";
|
|
298
|
+
|
|
299
|
+
// src/server/routes.ts
|
|
300
|
+
import { cors } from "hono/cors";
|
|
301
|
+
function setupRoutes(app, agentManager, sessionManager, agentsDir) {
|
|
302
|
+
app.use("/*", cors());
|
|
303
|
+
app.get("/health", (c) => c.json({ status: "ok" }));
|
|
304
|
+
app.get("/api/agents", (c) => {
|
|
305
|
+
const agentsList = Array.from(
|
|
306
|
+
agentManager.getAgents().values()
|
|
307
|
+
).map((agent) => ({
|
|
308
|
+
path: agent.absolutePath,
|
|
309
|
+
name: agent.name,
|
|
310
|
+
directory: agent.absolutePath,
|
|
311
|
+
relativePath: agent.relativePath
|
|
312
|
+
}));
|
|
313
|
+
return c.json({ agents: agentsList });
|
|
314
|
+
});
|
|
315
|
+
app.post("/api/agents/refresh", (c) => {
|
|
316
|
+
agentManager.scanAgents(agentsDir);
|
|
317
|
+
const agentsList = Array.from(
|
|
318
|
+
agentManager.getAgents().values()
|
|
319
|
+
).map((agent) => ({
|
|
320
|
+
path: agent.absolutePath,
|
|
321
|
+
name: agent.name,
|
|
322
|
+
directory: agent.absolutePath,
|
|
323
|
+
relativePath: agent.relativePath
|
|
324
|
+
}));
|
|
325
|
+
return c.json({ agents: agentsList });
|
|
326
|
+
});
|
|
327
|
+
app.get("/api/agents/:id/messages", async (c) => {
|
|
328
|
+
const agentPath = decodeURIComponent(c.req.param("id"));
|
|
329
|
+
const loadedAgent = agentManager.getLoadedAgents().get(agentPath);
|
|
330
|
+
if (!loadedAgent) {
|
|
331
|
+
return c.json({ messages: [] });
|
|
332
|
+
}
|
|
333
|
+
const messages = await sessionManager.getSessionMessages(loadedAgent);
|
|
334
|
+
const response = { messages };
|
|
335
|
+
return c.json(response);
|
|
336
|
+
});
|
|
337
|
+
app.post("/api/agents/:id/message", async (c) => {
|
|
338
|
+
const agentPath = decodeURIComponent(c.req.param("id"));
|
|
339
|
+
const { message } = await c.req.json();
|
|
340
|
+
const response = await agentManager.sendMessageToAgent(agentPath, message);
|
|
341
|
+
const messageResponse = { response };
|
|
342
|
+
return c.json(messageResponse);
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// src/server/services.ts
|
|
309
347
|
import {
|
|
310
348
|
existsSync as existsSync2,
|
|
311
349
|
mkdirSync,
|
|
@@ -316,91 +354,14 @@ import {
|
|
|
316
354
|
} from "fs";
|
|
317
355
|
import { dirname, join as join2, relative } from "path";
|
|
318
356
|
import { pathToFileURL } from "url";
|
|
319
|
-
import {
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
import { cors } from "hono/cors";
|
|
323
|
-
var ADKServer = class {
|
|
324
|
-
agents = /* @__PURE__ */ new Map();
|
|
325
|
-
loadedAgents = /* @__PURE__ */ new Map();
|
|
326
|
-
sessionService;
|
|
327
|
-
app;
|
|
328
|
-
server;
|
|
329
|
-
agentsDir;
|
|
330
|
-
port;
|
|
331
|
-
host;
|
|
332
|
-
quiet;
|
|
333
|
-
constructor(agentsDir, port = 8042, host = "localhost", quiet = false) {
|
|
334
|
-
this.agentsDir = agentsDir;
|
|
335
|
-
this.port = port;
|
|
336
|
-
this.host = host;
|
|
357
|
+
import { AgentBuilder } from "@iqai/adk";
|
|
358
|
+
var AgentScanner = class {
|
|
359
|
+
constructor(quiet = false) {
|
|
337
360
|
this.quiet = quiet;
|
|
338
|
-
this.sessionService = new InMemorySessionService();
|
|
339
|
-
this.app = new Hono();
|
|
340
|
-
this.setupRoutes();
|
|
341
|
-
this.scanAgents();
|
|
342
361
|
}
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
this.app.get("/api/agents", (c) => {
|
|
347
|
-
const agentsList = Array.from(this.agents.values()).map((agent) => ({
|
|
348
|
-
path: agent.absolutePath,
|
|
349
|
-
name: agent.name,
|
|
350
|
-
directory: agent.absolutePath,
|
|
351
|
-
relativePath: agent.relativePath
|
|
352
|
-
}));
|
|
353
|
-
return c.json({ agents: agentsList });
|
|
354
|
-
});
|
|
355
|
-
this.app.post("/api/agents/refresh", (c) => {
|
|
356
|
-
this.scanAgents();
|
|
357
|
-
const agentsList = Array.from(this.agents.values()).map((agent) => ({
|
|
358
|
-
path: agent.absolutePath,
|
|
359
|
-
name: agent.name,
|
|
360
|
-
directory: agent.absolutePath,
|
|
361
|
-
relativePath: agent.relativePath
|
|
362
|
-
}));
|
|
363
|
-
return c.json({ agents: agentsList });
|
|
364
|
-
});
|
|
365
|
-
this.app.get("/api/agents/:id/messages", async (c) => {
|
|
366
|
-
const agentPath = decodeURIComponent(c.req.param("id"));
|
|
367
|
-
const loadedAgent = this.loadedAgents.get(agentPath);
|
|
368
|
-
if (!loadedAgent) {
|
|
369
|
-
return c.json({ messages: [] });
|
|
370
|
-
}
|
|
371
|
-
try {
|
|
372
|
-
const session = await this.sessionService.getSession(
|
|
373
|
-
loadedAgent.appName,
|
|
374
|
-
loadedAgent.userId,
|
|
375
|
-
loadedAgent.sessionId
|
|
376
|
-
);
|
|
377
|
-
if (!session || !session.events) {
|
|
378
|
-
return c.json({ messages: [] });
|
|
379
|
-
}
|
|
380
|
-
const messages = session.events.map((event, index) => ({
|
|
381
|
-
id: index + 1,
|
|
382
|
-
type: event.author === "user" ? "user" : "assistant",
|
|
383
|
-
content: event.content?.parts?.map(
|
|
384
|
-
(part) => typeof part === "object" && "text" in part ? part.text : ""
|
|
385
|
-
).join("") || "",
|
|
386
|
-
timestamp: new Date(event.timestamp || Date.now()).toISOString()
|
|
387
|
-
}));
|
|
388
|
-
return c.json({ messages });
|
|
389
|
-
} catch (error) {
|
|
390
|
-
console.error("Error fetching messages:", error);
|
|
391
|
-
return c.json({ messages: [] });
|
|
392
|
-
}
|
|
393
|
-
});
|
|
394
|
-
this.app.post("/api/agents/:id/message", async (c) => {
|
|
395
|
-
const agentPath = decodeURIComponent(c.req.param("id"));
|
|
396
|
-
const { message } = await c.req.json();
|
|
397
|
-
const response = await this.sendMessageToAgent(agentPath, message);
|
|
398
|
-
return c.json({ response });
|
|
399
|
-
});
|
|
400
|
-
}
|
|
401
|
-
scanAgents() {
|
|
402
|
-
this.agents.clear();
|
|
403
|
-
const scanDir = !this.agentsDir || !existsSync2(this.agentsDir) ? process.cwd() : this.agentsDir;
|
|
362
|
+
scanAgents(agentsDir, loadedAgents) {
|
|
363
|
+
const agents = /* @__PURE__ */ new Map();
|
|
364
|
+
const scanDir = !agentsDir || !existsSync2(agentsDir) ? process.cwd() : agentsDir;
|
|
404
365
|
const shouldSkipDirectory = (dirName) => {
|
|
405
366
|
const skipDirs = [
|
|
406
367
|
"node_modules",
|
|
@@ -427,7 +388,7 @@ var ADKServer = class {
|
|
|
427
388
|
}
|
|
428
389
|
} else if (item === "agent.ts" || item === "agent.js") {
|
|
429
390
|
const relativePath = relative(scanDir, dir);
|
|
430
|
-
const loadedAgent =
|
|
391
|
+
const loadedAgent = loadedAgents.get(relativePath);
|
|
431
392
|
let agentName = relativePath.split("/").pop() || "unknown";
|
|
432
393
|
if (loadedAgent?.agent?.name) {
|
|
433
394
|
agentName = loadedAgent.agent.name;
|
|
@@ -438,7 +399,7 @@ var ADKServer = class {
|
|
|
438
399
|
} catch {
|
|
439
400
|
}
|
|
440
401
|
}
|
|
441
|
-
|
|
402
|
+
agents.set(relativePath, {
|
|
442
403
|
relativePath,
|
|
443
404
|
name: agentName,
|
|
444
405
|
absolutePath: dir,
|
|
@@ -449,9 +410,225 @@ var ADKServer = class {
|
|
|
449
410
|
};
|
|
450
411
|
scanDirectory(scanDir);
|
|
451
412
|
if (!this.quiet) {
|
|
452
|
-
console.log(`\u2705 Agent scan complete. Found ${
|
|
413
|
+
console.log(`\u2705 Agent scan complete. Found ${agents.size} agents.`);
|
|
414
|
+
}
|
|
415
|
+
return agents;
|
|
416
|
+
}
|
|
417
|
+
extractAgentNameFromFile(filePath) {
|
|
418
|
+
try {
|
|
419
|
+
const content = readFileSync(filePath, "utf-8");
|
|
420
|
+
const nameMatch = content.match(/name\s*:\s*["']([^"']+)["']/);
|
|
421
|
+
if (nameMatch?.[1]) {
|
|
422
|
+
return nameMatch[1];
|
|
423
|
+
}
|
|
424
|
+
return null;
|
|
425
|
+
} catch {
|
|
426
|
+
return null;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
};
|
|
430
|
+
var AgentLoader = class {
|
|
431
|
+
constructor(quiet = false) {
|
|
432
|
+
this.quiet = quiet;
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* Import a TypeScript file by compiling it on-demand
|
|
436
|
+
*/
|
|
437
|
+
async importTypeScriptFile(filePath) {
|
|
438
|
+
const startDir = dirname(filePath);
|
|
439
|
+
let projectRoot = startDir;
|
|
440
|
+
while (projectRoot !== "/" && projectRoot !== dirname(projectRoot)) {
|
|
441
|
+
if (existsSync2(join2(projectRoot, "package.json")) || existsSync2(join2(projectRoot, ".env"))) {
|
|
442
|
+
break;
|
|
443
|
+
}
|
|
444
|
+
projectRoot = dirname(projectRoot);
|
|
445
|
+
}
|
|
446
|
+
if (projectRoot === "/") {
|
|
447
|
+
projectRoot = startDir;
|
|
448
|
+
}
|
|
449
|
+
try {
|
|
450
|
+
const { build } = await import("esbuild");
|
|
451
|
+
const cacheDir = join2(projectRoot, ".adk-cache");
|
|
452
|
+
if (!existsSync2(cacheDir)) {
|
|
453
|
+
mkdirSync(cacheDir, { recursive: true });
|
|
454
|
+
}
|
|
455
|
+
const outFile = join2(cacheDir, `agent-${Date.now()}.mjs`);
|
|
456
|
+
const plugin = {
|
|
457
|
+
name: "externalize-bare-imports",
|
|
458
|
+
setup(build2) {
|
|
459
|
+
build2.onResolve({ filter: /.*/ }, (args) => {
|
|
460
|
+
if (args.path.startsWith(".") || args.path.startsWith("/") || args.path.startsWith("..")) {
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
return { path: args.path, external: true };
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
};
|
|
467
|
+
const tsconfigPath = join2(projectRoot, "tsconfig.json");
|
|
468
|
+
await build({
|
|
469
|
+
entryPoints: [filePath],
|
|
470
|
+
outfile: outFile,
|
|
471
|
+
bundle: true,
|
|
472
|
+
format: "esm",
|
|
473
|
+
platform: "node",
|
|
474
|
+
target: ["node22"],
|
|
475
|
+
sourcemap: false,
|
|
476
|
+
logLevel: "silent",
|
|
477
|
+
plugins: [plugin],
|
|
478
|
+
absWorkingDir: projectRoot,
|
|
479
|
+
// Use tsconfig if present for path aliases
|
|
480
|
+
...existsSync2(tsconfigPath) ? { tsconfig: tsconfigPath } : {}
|
|
481
|
+
});
|
|
482
|
+
const mod = await import(`${pathToFileURL(outFile).href}?t=${Date.now()}`);
|
|
483
|
+
let agentExport = mod?.agent;
|
|
484
|
+
if (!agentExport && mod?.default) {
|
|
485
|
+
agentExport = mod.default.agent ?? mod.default;
|
|
486
|
+
}
|
|
487
|
+
try {
|
|
488
|
+
unlinkSync(outFile);
|
|
489
|
+
} catch {
|
|
490
|
+
}
|
|
491
|
+
if (agentExport) {
|
|
492
|
+
const isPrimitive = (v) => v == null || ["string", "number", "boolean"].includes(typeof v);
|
|
493
|
+
if (isPrimitive(agentExport)) {
|
|
494
|
+
if (!this.quiet) {
|
|
495
|
+
console.log(
|
|
496
|
+
`\u2139\uFE0F Ignoring primitive 'agent' export in ${filePath}; scanning module for factory...`
|
|
497
|
+
);
|
|
498
|
+
}
|
|
499
|
+
} else {
|
|
500
|
+
if (!this.quiet) {
|
|
501
|
+
console.log(`\u2705 TS agent imported via esbuild: ${filePath}`);
|
|
502
|
+
}
|
|
503
|
+
return { agent: agentExport };
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
return mod;
|
|
507
|
+
} catch (e) {
|
|
508
|
+
throw new Error(
|
|
509
|
+
`Failed to import TS agent via esbuild: ${e instanceof Error ? e.message : String(e)}`
|
|
510
|
+
);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
loadEnvironmentVariables(agentFilePath) {
|
|
514
|
+
let projectRoot = dirname(agentFilePath);
|
|
515
|
+
while (projectRoot !== "/" && projectRoot !== dirname(projectRoot)) {
|
|
516
|
+
if (existsSync2(join2(projectRoot, "package.json")) || existsSync2(join2(projectRoot, ".env"))) {
|
|
517
|
+
break;
|
|
518
|
+
}
|
|
519
|
+
projectRoot = dirname(projectRoot);
|
|
520
|
+
}
|
|
521
|
+
const envPath = join2(projectRoot, ".env");
|
|
522
|
+
if (existsSync2(envPath)) {
|
|
523
|
+
try {
|
|
524
|
+
const envContent = readFileSync(envPath, "utf8");
|
|
525
|
+
const envLines = envContent.split("\n");
|
|
526
|
+
for (const line of envLines) {
|
|
527
|
+
const trimmedLine = line.trim();
|
|
528
|
+
if (trimmedLine && !trimmedLine.startsWith("#")) {
|
|
529
|
+
const [key, ...valueParts] = trimmedLine.split("=");
|
|
530
|
+
if (key && valueParts.length > 0) {
|
|
531
|
+
const value = valueParts.join("=").replace(/^"(.*)"$/, "$1");
|
|
532
|
+
process.env[key.trim()] = value.trim();
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
} catch (error) {
|
|
537
|
+
console.warn(
|
|
538
|
+
`\u26A0\uFE0F Warning: Could not load .env file: ${error instanceof Error ? error.message : String(error)}`
|
|
539
|
+
);
|
|
540
|
+
}
|
|
453
541
|
}
|
|
454
542
|
}
|
|
543
|
+
// Minimal resolution logic for agent exports: supports
|
|
544
|
+
// 1) export const agent = new LlmAgent(...)
|
|
545
|
+
// 2) export function agent() { return new LlmAgent(...) }
|
|
546
|
+
// 3) export async function agent() { return new LlmAgent(...) }
|
|
547
|
+
// 4) default export (object or function) returning or containing .agent
|
|
548
|
+
async resolveAgentExport(mod) {
|
|
549
|
+
let candidate = mod?.agent ?? mod?.default?.agent ?? mod?.default ?? mod;
|
|
550
|
+
const isLikelyAgentInstance = (obj) => obj && typeof obj === "object" && typeof obj.name === "string";
|
|
551
|
+
const isPrimitive = (v) => v == null || ["string", "number", "boolean"].includes(typeof v);
|
|
552
|
+
const invokeMaybe = async (fn) => {
|
|
553
|
+
let out = fn();
|
|
554
|
+
if (out && typeof out === "object" && "then" in out) {
|
|
555
|
+
out = await out;
|
|
556
|
+
}
|
|
557
|
+
return out;
|
|
558
|
+
};
|
|
559
|
+
if (!isLikelyAgentInstance(candidate) && isPrimitive(candidate) || !isLikelyAgentInstance(candidate) && candidate && candidate === mod) {
|
|
560
|
+
candidate = mod;
|
|
561
|
+
for (const [key, value] of Object.entries(mod)) {
|
|
562
|
+
if (key === "default") continue;
|
|
563
|
+
const keyLower = key.toLowerCase();
|
|
564
|
+
if (isPrimitive(value)) continue;
|
|
565
|
+
if (isLikelyAgentInstance(value)) {
|
|
566
|
+
candidate = value;
|
|
567
|
+
break;
|
|
568
|
+
}
|
|
569
|
+
if (value && typeof value === "object" && value.agent && isLikelyAgentInstance(value.agent)) {
|
|
570
|
+
candidate = value.agent;
|
|
571
|
+
break;
|
|
572
|
+
}
|
|
573
|
+
if (typeof value === "function" && (/(agent|build|create)/i.test(keyLower) || value.name && /(agent|build|create)/i.test(value.name.toLowerCase()))) {
|
|
574
|
+
try {
|
|
575
|
+
const maybe = await invokeMaybe(value);
|
|
576
|
+
if (isLikelyAgentInstance(maybe)) {
|
|
577
|
+
candidate = maybe;
|
|
578
|
+
break;
|
|
579
|
+
}
|
|
580
|
+
if (maybe && typeof maybe === "object" && maybe.agent && isLikelyAgentInstance(maybe.agent)) {
|
|
581
|
+
candidate = maybe.agent;
|
|
582
|
+
break;
|
|
583
|
+
}
|
|
584
|
+
} catch (e) {
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
if (typeof candidate === "function") {
|
|
590
|
+
try {
|
|
591
|
+
candidate = await invokeMaybe(candidate);
|
|
592
|
+
} catch (e) {
|
|
593
|
+
throw new Error(
|
|
594
|
+
`Failed executing exported agent function: ${e instanceof Error ? e.message : String(e)}`
|
|
595
|
+
);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
if (candidate && typeof candidate === "object" && candidate.agent && isLikelyAgentInstance(candidate.agent)) {
|
|
599
|
+
candidate = candidate.agent;
|
|
600
|
+
}
|
|
601
|
+
if (candidate?.agent && isLikelyAgentInstance(candidate.agent)) {
|
|
602
|
+
candidate = candidate.agent;
|
|
603
|
+
}
|
|
604
|
+
if (!candidate || !isLikelyAgentInstance(candidate)) {
|
|
605
|
+
throw new Error(
|
|
606
|
+
"No agent export resolved (expected variable, function, or function returning an agent)"
|
|
607
|
+
);
|
|
608
|
+
}
|
|
609
|
+
return { agent: candidate };
|
|
610
|
+
}
|
|
611
|
+
};
|
|
612
|
+
var AgentManager = class {
|
|
613
|
+
constructor(sessionService, quiet = false) {
|
|
614
|
+
this.sessionService = sessionService;
|
|
615
|
+
this.quiet = quiet;
|
|
616
|
+
this.scanner = new AgentScanner(quiet);
|
|
617
|
+
this.loader = new AgentLoader(quiet);
|
|
618
|
+
}
|
|
619
|
+
agents = /* @__PURE__ */ new Map();
|
|
620
|
+
loadedAgents = /* @__PURE__ */ new Map();
|
|
621
|
+
scanner;
|
|
622
|
+
loader;
|
|
623
|
+
getAgents() {
|
|
624
|
+
return this.agents;
|
|
625
|
+
}
|
|
626
|
+
getLoadedAgents() {
|
|
627
|
+
return this.loadedAgents;
|
|
628
|
+
}
|
|
629
|
+
scanAgents(agentsDir) {
|
|
630
|
+
this.agents = this.scanner.scanAgents(agentsDir, this.loadedAgents);
|
|
631
|
+
}
|
|
455
632
|
async startAgent(agentPath) {
|
|
456
633
|
const agent = this.agents.get(agentPath);
|
|
457
634
|
if (!agent) {
|
|
@@ -470,70 +647,31 @@ var ADKServer = class {
|
|
|
470
647
|
`No agent.js or agent.ts file found in ${agent.absolutePath}`
|
|
471
648
|
);
|
|
472
649
|
}
|
|
473
|
-
|
|
474
|
-
while (projectRoot !== "/" && projectRoot !== dirname(projectRoot)) {
|
|
475
|
-
if (existsSync2(join2(projectRoot, "package.json")) || existsSync2(join2(projectRoot, ".env"))) {
|
|
476
|
-
break;
|
|
477
|
-
}
|
|
478
|
-
projectRoot = dirname(projectRoot);
|
|
479
|
-
}
|
|
480
|
-
const envPath = join2(projectRoot, ".env");
|
|
481
|
-
if (existsSync2(envPath)) {
|
|
482
|
-
try {
|
|
483
|
-
const envContent = readFileSync(envPath, "utf8");
|
|
484
|
-
const envLines = envContent.split("\n");
|
|
485
|
-
for (const line of envLines) {
|
|
486
|
-
const trimmedLine = line.trim();
|
|
487
|
-
if (trimmedLine && !trimmedLine.startsWith("#")) {
|
|
488
|
-
const [key, ...valueParts] = trimmedLine.split("=");
|
|
489
|
-
if (key && valueParts.length > 0) {
|
|
490
|
-
const value = valueParts.join("=").replace(/^"(.*)"$/, "$1");
|
|
491
|
-
process.env[key.trim()] = value.trim();
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
}
|
|
495
|
-
} catch (error) {
|
|
496
|
-
console.warn(
|
|
497
|
-
`\u26A0\uFE0F Warning: Could not load .env file: ${error instanceof Error ? error.message : String(error)}`
|
|
498
|
-
);
|
|
499
|
-
}
|
|
500
|
-
}
|
|
650
|
+
this.loader.loadEnvironmentVariables(agentFilePath);
|
|
501
651
|
const agentFileUrl = pathToFileURL(agentFilePath).href;
|
|
502
|
-
const agentModule = agentFilePath.endsWith(".ts") ? await this.importTypeScriptFile(agentFilePath) : await import(agentFileUrl);
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
if (!agentModule.agent || typeof agentModule.agent !== "object" || !agentModule.agent.name) {
|
|
652
|
+
const agentModule = agentFilePath.endsWith(".ts") ? await this.loader.importTypeScriptFile(agentFilePath) : await import(agentFileUrl);
|
|
653
|
+
const resolved = await this.loader.resolveAgentExport(agentModule);
|
|
654
|
+
const exportedAgent = resolved.agent;
|
|
655
|
+
if (!exportedAgent?.name) {
|
|
507
656
|
throw new Error(
|
|
508
657
|
`Invalid agent export in ${agentFilePath}. Expected an LlmAgent instance with a name property.`
|
|
509
658
|
);
|
|
510
659
|
}
|
|
511
|
-
|
|
512
|
-
}
|
|
513
|
-
const agentBuilder = AgentBuilder.create(agentModule.agent.name).withModel(agentModule.agent.model).withDescription(agentModule.agent.description || "").withInstruction(agentModule.agent.instruction || "").withSessionService(this.sessionService, {
|
|
660
|
+
const agentBuilder = AgentBuilder.create(exportedAgent.name).withAgent(exportedAgent).withSessionService(this.sessionService, {
|
|
514
661
|
userId: `user_${agentPath}`,
|
|
515
662
|
appName: "adk-server"
|
|
516
663
|
});
|
|
517
|
-
if (agentModule.agent.tools && Array.isArray(agentModule.agent.tools) && agentModule.agent.tools.length > 0) {
|
|
518
|
-
agentBuilder.withTools(...agentModule.agent.tools);
|
|
519
|
-
if (!this.quiet) {
|
|
520
|
-
const toolNames = agentModule.agent.tools.map((t) => t?.name || "<unnamed>").join(", ");
|
|
521
|
-
console.log(
|
|
522
|
-
`\u{1F9E9} Registered tools for agent "${agentModule.agent.name}": [${toolNames}]`
|
|
523
|
-
);
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
664
|
const { runner, session } = await agentBuilder.build();
|
|
527
665
|
const loadedAgent = {
|
|
528
|
-
agent:
|
|
666
|
+
agent: exportedAgent,
|
|
529
667
|
runner,
|
|
530
668
|
sessionId: session.id,
|
|
531
669
|
userId: `user_${agentPath}`,
|
|
532
670
|
appName: "adk-server"
|
|
533
671
|
};
|
|
534
672
|
this.loadedAgents.set(agentPath, loadedAgent);
|
|
535
|
-
agent.instance =
|
|
536
|
-
agent.name =
|
|
673
|
+
agent.instance = exportedAgent;
|
|
674
|
+
agent.name = exportedAgent.name;
|
|
537
675
|
} catch (error) {
|
|
538
676
|
console.error(`\u274C Failed to load agent "${agent.name}":`, error);
|
|
539
677
|
throw new Error(
|
|
@@ -568,78 +706,65 @@ var ADKServer = class {
|
|
|
568
706
|
throw new Error(`Failed to send message to agent: ${errorMessage}`);
|
|
569
707
|
}
|
|
570
708
|
}
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
async importTypeScriptFile(filePath) {
|
|
575
|
-
let projectRoot = dirname(filePath);
|
|
576
|
-
while (projectRoot !== "/" && projectRoot !== dirname(projectRoot)) {
|
|
577
|
-
if (existsSync2(join2(projectRoot, "package.json")) || existsSync2(join2(projectRoot, ".env"))) {
|
|
578
|
-
break;
|
|
579
|
-
}
|
|
580
|
-
projectRoot = dirname(projectRoot);
|
|
709
|
+
stopAllAgents() {
|
|
710
|
+
for (const [agentPath] of this.loadedAgents.entries()) {
|
|
711
|
+
this.stopAgent(agentPath);
|
|
581
712
|
}
|
|
713
|
+
}
|
|
714
|
+
};
|
|
715
|
+
var SessionManager = class {
|
|
716
|
+
constructor(sessionService) {
|
|
717
|
+
this.sessionService = sessionService;
|
|
718
|
+
}
|
|
719
|
+
async getSessionMessages(loadedAgent) {
|
|
582
720
|
try {
|
|
583
|
-
const
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
}
|
|
588
|
-
const outFile = join2(cacheDir, `agent-${Date.now()}.mjs`);
|
|
589
|
-
const plugin = {
|
|
590
|
-
name: "externalize-bare-imports",
|
|
591
|
-
setup(build2) {
|
|
592
|
-
build2.onResolve({ filter: /.*/ }, (args) => {
|
|
593
|
-
if (args.path.startsWith(".") || args.path.startsWith("/") || args.path.startsWith("..")) {
|
|
594
|
-
return;
|
|
595
|
-
}
|
|
596
|
-
return { path: args.path, external: true };
|
|
597
|
-
});
|
|
598
|
-
}
|
|
599
|
-
};
|
|
600
|
-
const tsconfigPath = join2(projectRoot, "tsconfig.json");
|
|
601
|
-
await build({
|
|
602
|
-
entryPoints: [filePath],
|
|
603
|
-
outfile: outFile,
|
|
604
|
-
bundle: true,
|
|
605
|
-
format: "esm",
|
|
606
|
-
platform: "node",
|
|
607
|
-
target: ["node22"],
|
|
608
|
-
sourcemap: false,
|
|
609
|
-
logLevel: "silent",
|
|
610
|
-
plugins: [plugin],
|
|
611
|
-
absWorkingDir: projectRoot,
|
|
612
|
-
// Use tsconfig if present for path aliases
|
|
613
|
-
...existsSync2(tsconfigPath) ? { tsconfig: tsconfigPath } : {}
|
|
614
|
-
});
|
|
615
|
-
const mod = await import(`${pathToFileURL(outFile).href}?t=${Date.now()}`);
|
|
616
|
-
let agentExport = mod?.agent;
|
|
617
|
-
if (!agentExport && mod?.default) {
|
|
618
|
-
agentExport = mod.default.agent ?? mod.default;
|
|
619
|
-
}
|
|
620
|
-
try {
|
|
621
|
-
unlinkSync(outFile);
|
|
622
|
-
} catch {
|
|
623
|
-
}
|
|
624
|
-
if (agentExport) {
|
|
625
|
-
if (!this.quiet) {
|
|
626
|
-
console.log(`\u2705 TS agent imported via esbuild: ${filePath}`);
|
|
627
|
-
}
|
|
628
|
-
return { agent: agentExport };
|
|
629
|
-
}
|
|
630
|
-
} catch (e) {
|
|
631
|
-
throw new Error(
|
|
632
|
-
`Failed to import TS agent via esbuild: ${e instanceof Error ? e.message : String(e)}`
|
|
721
|
+
const session = await this.sessionService.getSession(
|
|
722
|
+
loadedAgent.appName,
|
|
723
|
+
loadedAgent.userId,
|
|
724
|
+
loadedAgent.sessionId
|
|
633
725
|
);
|
|
726
|
+
if (!session || !session.events) {
|
|
727
|
+
return [];
|
|
728
|
+
}
|
|
729
|
+
const messages = session.events.map((event, index) => ({
|
|
730
|
+
id: index + 1,
|
|
731
|
+
type: event.author === "user" ? "user" : "assistant",
|
|
732
|
+
content: event.content?.parts?.map(
|
|
733
|
+
(part) => typeof part === "object" && "text" in part ? part.text : ""
|
|
734
|
+
).join("") || "",
|
|
735
|
+
timestamp: new Date(event.timestamp || Date.now()).toISOString()
|
|
736
|
+
}));
|
|
737
|
+
return messages;
|
|
738
|
+
} catch (error) {
|
|
739
|
+
console.error("Error fetching messages:", error);
|
|
740
|
+
return [];
|
|
634
741
|
}
|
|
635
|
-
|
|
742
|
+
}
|
|
743
|
+
};
|
|
744
|
+
|
|
745
|
+
// src/server/index.ts
|
|
746
|
+
var ADKServer = class {
|
|
747
|
+
agentManager;
|
|
748
|
+
sessionManager;
|
|
749
|
+
sessionService;
|
|
750
|
+
app;
|
|
751
|
+
server;
|
|
752
|
+
config;
|
|
753
|
+
constructor(agentsDir, port = 8042, host = "localhost", quiet = false) {
|
|
754
|
+
this.config = { agentsDir, port, host, quiet };
|
|
755
|
+
this.sessionService = new InMemorySessionService();
|
|
756
|
+
this.agentManager = new AgentManager(this.sessionService, quiet);
|
|
757
|
+
this.sessionManager = new SessionManager(this.sessionService);
|
|
758
|
+
this.app = new Hono();
|
|
759
|
+
setupRoutes(this.app, this.agentManager, this.sessionManager, agentsDir);
|
|
760
|
+
this.agentManager.scanAgents(agentsDir);
|
|
636
761
|
}
|
|
637
762
|
async start() {
|
|
638
|
-
return new Promise((resolve2
|
|
763
|
+
return new Promise((resolve2) => {
|
|
639
764
|
this.server = serve({
|
|
640
765
|
fetch: this.app.fetch,
|
|
641
|
-
port: this.port,
|
|
642
|
-
hostname: this.host
|
|
766
|
+
port: this.config.port,
|
|
767
|
+
hostname: this.config.host
|
|
643
768
|
});
|
|
644
769
|
setTimeout(() => {
|
|
645
770
|
resolve2();
|
|
@@ -648,9 +773,7 @@ var ADKServer = class {
|
|
|
648
773
|
}
|
|
649
774
|
async stop() {
|
|
650
775
|
return new Promise((resolve2) => {
|
|
651
|
-
|
|
652
|
-
this.stopAgent(agentPath);
|
|
653
|
-
}
|
|
776
|
+
this.agentManager.stopAllAgents();
|
|
654
777
|
if (this.server) {
|
|
655
778
|
this.server.close();
|
|
656
779
|
}
|
|
@@ -658,19 +781,7 @@ var ADKServer = class {
|
|
|
658
781
|
});
|
|
659
782
|
}
|
|
660
783
|
getPort() {
|
|
661
|
-
return this.port;
|
|
662
|
-
}
|
|
663
|
-
extractAgentNameFromFile(filePath) {
|
|
664
|
-
try {
|
|
665
|
-
const content = readFileSync(filePath, "utf-8");
|
|
666
|
-
const nameMatch = content.match(/name\s*:\s*["']([^"']+)["']/);
|
|
667
|
-
if (nameMatch?.[1]) {
|
|
668
|
-
return nameMatch[1];
|
|
669
|
-
}
|
|
670
|
-
return null;
|
|
671
|
-
} catch {
|
|
672
|
-
return null;
|
|
673
|
-
}
|
|
784
|
+
return this.config.port;
|
|
674
785
|
}
|
|
675
786
|
};
|
|
676
787
|
|
|
@@ -963,8 +1074,7 @@ async function webCommand(options = {}) {
|
|
|
963
1074
|
program.name("adk").description(package_default.description).version(package_default.version);
|
|
964
1075
|
program.command("new").description("Create a new ADK project").argument("[project-name]", "Name of the project to create").option(
|
|
965
1076
|
"-t, --template <template>",
|
|
966
|
-
"Template to use (simple-agent, discord-bot, telegram-bot, hono-server, mcp-starter)"
|
|
967
|
-
"simple-agent"
|
|
1077
|
+
"Template to use (simple-agent, discord-bot, telegram-bot, hono-server, mcp-starter)"
|
|
968
1078
|
).action(async (projectName, options) => {
|
|
969
1079
|
try {
|
|
970
1080
|
await createProject(projectName, options);
|