@iqai/adk-cli 0.1.1 → 0.2.0
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 +39 -0
- package/dist/index.js +340 -215
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +341 -216
- 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.0",
|
|
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",
|
|
@@ -305,7 +305,59 @@ import { existsSync as existsSync3 } from "fs";
|
|
|
305
305
|
import { resolve } from "path";
|
|
306
306
|
import chalk2 from "chalk";
|
|
307
307
|
|
|
308
|
-
// src/server.ts
|
|
308
|
+
// src/server/index.ts
|
|
309
|
+
import { serve } from "@hono/node-server";
|
|
310
|
+
import { InMemorySessionService } from "@iqai/adk";
|
|
311
|
+
import { Hono } from "hono";
|
|
312
|
+
|
|
313
|
+
// src/server/routes.ts
|
|
314
|
+
import { cors } from "hono/cors";
|
|
315
|
+
function setupRoutes(app, agentManager, sessionManager, agentsDir) {
|
|
316
|
+
app.use("/*", cors());
|
|
317
|
+
app.get("/health", (c) => c.json({ status: "ok" }));
|
|
318
|
+
app.get("/api/agents", (c) => {
|
|
319
|
+
const agentsList = Array.from(
|
|
320
|
+
agentManager.getAgents().values()
|
|
321
|
+
).map((agent) => ({
|
|
322
|
+
path: agent.absolutePath,
|
|
323
|
+
name: agent.name,
|
|
324
|
+
directory: agent.absolutePath,
|
|
325
|
+
relativePath: agent.relativePath
|
|
326
|
+
}));
|
|
327
|
+
return c.json({ agents: agentsList });
|
|
328
|
+
});
|
|
329
|
+
app.post("/api/agents/refresh", (c) => {
|
|
330
|
+
agentManager.scanAgents(agentsDir);
|
|
331
|
+
const agentsList = Array.from(
|
|
332
|
+
agentManager.getAgents().values()
|
|
333
|
+
).map((agent) => ({
|
|
334
|
+
path: agent.absolutePath,
|
|
335
|
+
name: agent.name,
|
|
336
|
+
directory: agent.absolutePath,
|
|
337
|
+
relativePath: agent.relativePath
|
|
338
|
+
}));
|
|
339
|
+
return c.json({ agents: agentsList });
|
|
340
|
+
});
|
|
341
|
+
app.get("/api/agents/:id/messages", async (c) => {
|
|
342
|
+
const agentPath = decodeURIComponent(c.req.param("id"));
|
|
343
|
+
const loadedAgent = agentManager.getLoadedAgents().get(agentPath);
|
|
344
|
+
if (!loadedAgent) {
|
|
345
|
+
return c.json({ messages: [] });
|
|
346
|
+
}
|
|
347
|
+
const messages = await sessionManager.getSessionMessages(loadedAgent);
|
|
348
|
+
const response = { messages };
|
|
349
|
+
return c.json(response);
|
|
350
|
+
});
|
|
351
|
+
app.post("/api/agents/:id/message", async (c) => {
|
|
352
|
+
const agentPath = decodeURIComponent(c.req.param("id"));
|
|
353
|
+
const { message } = await c.req.json();
|
|
354
|
+
const response = await agentManager.sendMessageToAgent(agentPath, message);
|
|
355
|
+
const messageResponse = { response };
|
|
356
|
+
return c.json(messageResponse);
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// src/server/services.ts
|
|
309
361
|
import {
|
|
310
362
|
existsSync as existsSync2,
|
|
311
363
|
mkdirSync,
|
|
@@ -316,91 +368,14 @@ import {
|
|
|
316
368
|
} from "fs";
|
|
317
369
|
import { dirname, join as join2, relative } from "path";
|
|
318
370
|
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;
|
|
371
|
+
import { AgentBuilder } from "@iqai/adk";
|
|
372
|
+
var AgentScanner = class {
|
|
373
|
+
constructor(quiet = false) {
|
|
337
374
|
this.quiet = quiet;
|
|
338
|
-
this.sessionService = new InMemorySessionService();
|
|
339
|
-
this.app = new Hono();
|
|
340
|
-
this.setupRoutes();
|
|
341
|
-
this.scanAgents();
|
|
342
375
|
}
|
|
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;
|
|
376
|
+
scanAgents(agentsDir, loadedAgents) {
|
|
377
|
+
const agents = /* @__PURE__ */ new Map();
|
|
378
|
+
const scanDir = !agentsDir || !existsSync2(agentsDir) ? process.cwd() : agentsDir;
|
|
404
379
|
const shouldSkipDirectory = (dirName) => {
|
|
405
380
|
const skipDirs = [
|
|
406
381
|
"node_modules",
|
|
@@ -427,7 +402,7 @@ var ADKServer = class {
|
|
|
427
402
|
}
|
|
428
403
|
} else if (item === "agent.ts" || item === "agent.js") {
|
|
429
404
|
const relativePath = relative(scanDir, dir);
|
|
430
|
-
const loadedAgent =
|
|
405
|
+
const loadedAgent = loadedAgents.get(relativePath);
|
|
431
406
|
let agentName = relativePath.split("/").pop() || "unknown";
|
|
432
407
|
if (loadedAgent?.agent?.name) {
|
|
433
408
|
agentName = loadedAgent.agent.name;
|
|
@@ -438,7 +413,7 @@ var ADKServer = class {
|
|
|
438
413
|
} catch {
|
|
439
414
|
}
|
|
440
415
|
}
|
|
441
|
-
|
|
416
|
+
agents.set(relativePath, {
|
|
442
417
|
relativePath,
|
|
443
418
|
name: agentName,
|
|
444
419
|
absolutePath: dir,
|
|
@@ -449,9 +424,225 @@ var ADKServer = class {
|
|
|
449
424
|
};
|
|
450
425
|
scanDirectory(scanDir);
|
|
451
426
|
if (!this.quiet) {
|
|
452
|
-
console.log(`\u2705 Agent scan complete. Found ${
|
|
427
|
+
console.log(`\u2705 Agent scan complete. Found ${agents.size} agents.`);
|
|
428
|
+
}
|
|
429
|
+
return agents;
|
|
430
|
+
}
|
|
431
|
+
extractAgentNameFromFile(filePath) {
|
|
432
|
+
try {
|
|
433
|
+
const content = readFileSync(filePath, "utf-8");
|
|
434
|
+
const nameMatch = content.match(/name\s*:\s*["']([^"']+)["']/);
|
|
435
|
+
if (nameMatch?.[1]) {
|
|
436
|
+
return nameMatch[1];
|
|
437
|
+
}
|
|
438
|
+
return null;
|
|
439
|
+
} catch {
|
|
440
|
+
return null;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
};
|
|
444
|
+
var AgentLoader = class {
|
|
445
|
+
constructor(quiet = false) {
|
|
446
|
+
this.quiet = quiet;
|
|
447
|
+
}
|
|
448
|
+
/**
|
|
449
|
+
* Import a TypeScript file by compiling it on-demand
|
|
450
|
+
*/
|
|
451
|
+
async importTypeScriptFile(filePath) {
|
|
452
|
+
const startDir = dirname(filePath);
|
|
453
|
+
let projectRoot = startDir;
|
|
454
|
+
while (projectRoot !== "/" && projectRoot !== dirname(projectRoot)) {
|
|
455
|
+
if (existsSync2(join2(projectRoot, "package.json")) || existsSync2(join2(projectRoot, ".env"))) {
|
|
456
|
+
break;
|
|
457
|
+
}
|
|
458
|
+
projectRoot = dirname(projectRoot);
|
|
459
|
+
}
|
|
460
|
+
if (projectRoot === "/") {
|
|
461
|
+
projectRoot = startDir;
|
|
462
|
+
}
|
|
463
|
+
try {
|
|
464
|
+
const { build } = await import("esbuild");
|
|
465
|
+
const cacheDir = join2(projectRoot, ".adk-cache");
|
|
466
|
+
if (!existsSync2(cacheDir)) {
|
|
467
|
+
mkdirSync(cacheDir, { recursive: true });
|
|
468
|
+
}
|
|
469
|
+
const outFile = join2(cacheDir, `agent-${Date.now()}.mjs`);
|
|
470
|
+
const plugin = {
|
|
471
|
+
name: "externalize-bare-imports",
|
|
472
|
+
setup(build2) {
|
|
473
|
+
build2.onResolve({ filter: /.*/ }, (args) => {
|
|
474
|
+
if (args.path.startsWith(".") || args.path.startsWith("/") || args.path.startsWith("..")) {
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
return { path: args.path, external: true };
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
};
|
|
481
|
+
const tsconfigPath = join2(projectRoot, "tsconfig.json");
|
|
482
|
+
await build({
|
|
483
|
+
entryPoints: [filePath],
|
|
484
|
+
outfile: outFile,
|
|
485
|
+
bundle: true,
|
|
486
|
+
format: "esm",
|
|
487
|
+
platform: "node",
|
|
488
|
+
target: ["node22"],
|
|
489
|
+
sourcemap: false,
|
|
490
|
+
logLevel: "silent",
|
|
491
|
+
plugins: [plugin],
|
|
492
|
+
absWorkingDir: projectRoot,
|
|
493
|
+
// Use tsconfig if present for path aliases
|
|
494
|
+
...existsSync2(tsconfigPath) ? { tsconfig: tsconfigPath } : {}
|
|
495
|
+
});
|
|
496
|
+
const mod = await import(`${pathToFileURL(outFile).href}?t=${Date.now()}`);
|
|
497
|
+
let agentExport = mod?.agent;
|
|
498
|
+
if (!agentExport && mod?.default) {
|
|
499
|
+
agentExport = mod.default.agent ?? mod.default;
|
|
500
|
+
}
|
|
501
|
+
try {
|
|
502
|
+
unlinkSync(outFile);
|
|
503
|
+
} catch {
|
|
504
|
+
}
|
|
505
|
+
if (agentExport) {
|
|
506
|
+
const isPrimitive = (v) => v == null || ["string", "number", "boolean"].includes(typeof v);
|
|
507
|
+
if (isPrimitive(agentExport)) {
|
|
508
|
+
if (!this.quiet) {
|
|
509
|
+
console.log(
|
|
510
|
+
`\u2139\uFE0F Ignoring primitive 'agent' export in ${filePath}; scanning module for factory...`
|
|
511
|
+
);
|
|
512
|
+
}
|
|
513
|
+
} else {
|
|
514
|
+
if (!this.quiet) {
|
|
515
|
+
console.log(`\u2705 TS agent imported via esbuild: ${filePath}`);
|
|
516
|
+
}
|
|
517
|
+
return { agent: agentExport };
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
return mod;
|
|
521
|
+
} catch (e) {
|
|
522
|
+
throw new Error(
|
|
523
|
+
`Failed to import TS agent via esbuild: ${e instanceof Error ? e.message : String(e)}`
|
|
524
|
+
);
|
|
453
525
|
}
|
|
454
526
|
}
|
|
527
|
+
loadEnvironmentVariables(agentFilePath) {
|
|
528
|
+
let projectRoot = dirname(agentFilePath);
|
|
529
|
+
while (projectRoot !== "/" && projectRoot !== dirname(projectRoot)) {
|
|
530
|
+
if (existsSync2(join2(projectRoot, "package.json")) || existsSync2(join2(projectRoot, ".env"))) {
|
|
531
|
+
break;
|
|
532
|
+
}
|
|
533
|
+
projectRoot = dirname(projectRoot);
|
|
534
|
+
}
|
|
535
|
+
const envPath = join2(projectRoot, ".env");
|
|
536
|
+
if (existsSync2(envPath)) {
|
|
537
|
+
try {
|
|
538
|
+
const envContent = readFileSync(envPath, "utf8");
|
|
539
|
+
const envLines = envContent.split("\n");
|
|
540
|
+
for (const line of envLines) {
|
|
541
|
+
const trimmedLine = line.trim();
|
|
542
|
+
if (trimmedLine && !trimmedLine.startsWith("#")) {
|
|
543
|
+
const [key, ...valueParts] = trimmedLine.split("=");
|
|
544
|
+
if (key && valueParts.length > 0) {
|
|
545
|
+
const value = valueParts.join("=").replace(/^"(.*)"$/, "$1");
|
|
546
|
+
process.env[key.trim()] = value.trim();
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
} catch (error) {
|
|
551
|
+
console.warn(
|
|
552
|
+
`\u26A0\uFE0F Warning: Could not load .env file: ${error instanceof Error ? error.message : String(error)}`
|
|
553
|
+
);
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
// Minimal resolution logic for agent exports: supports
|
|
558
|
+
// 1) export const agent = new LlmAgent(...)
|
|
559
|
+
// 2) export function agent() { return new LlmAgent(...) }
|
|
560
|
+
// 3) export async function agent() { return new LlmAgent(...) }
|
|
561
|
+
// 4) default export (object or function) returning or containing .agent
|
|
562
|
+
async resolveAgentExport(mod) {
|
|
563
|
+
let candidate = mod?.agent ?? mod?.default?.agent ?? mod?.default ?? mod;
|
|
564
|
+
const isLikelyAgentInstance = (obj) => obj && typeof obj === "object" && typeof obj.name === "string";
|
|
565
|
+
const isPrimitive = (v) => v == null || ["string", "number", "boolean"].includes(typeof v);
|
|
566
|
+
const invokeMaybe = async (fn) => {
|
|
567
|
+
let out = fn();
|
|
568
|
+
if (out && typeof out === "object" && "then" in out) {
|
|
569
|
+
out = await out;
|
|
570
|
+
}
|
|
571
|
+
return out;
|
|
572
|
+
};
|
|
573
|
+
if (!isLikelyAgentInstance(candidate) && isPrimitive(candidate) || !isLikelyAgentInstance(candidate) && candidate && candidate === mod) {
|
|
574
|
+
candidate = mod;
|
|
575
|
+
for (const [key, value] of Object.entries(mod)) {
|
|
576
|
+
if (key === "default") continue;
|
|
577
|
+
const keyLower = key.toLowerCase();
|
|
578
|
+
if (isPrimitive(value)) continue;
|
|
579
|
+
if (isLikelyAgentInstance(value)) {
|
|
580
|
+
candidate = value;
|
|
581
|
+
break;
|
|
582
|
+
}
|
|
583
|
+
if (value && typeof value === "object" && value.agent && isLikelyAgentInstance(value.agent)) {
|
|
584
|
+
candidate = value.agent;
|
|
585
|
+
break;
|
|
586
|
+
}
|
|
587
|
+
if (typeof value === "function" && (/(agent|build|create)/i.test(keyLower) || value.name && /(agent|build|create)/i.test(value.name.toLowerCase()))) {
|
|
588
|
+
try {
|
|
589
|
+
const maybe = await invokeMaybe(value);
|
|
590
|
+
if (isLikelyAgentInstance(maybe)) {
|
|
591
|
+
candidate = maybe;
|
|
592
|
+
break;
|
|
593
|
+
}
|
|
594
|
+
if (maybe && typeof maybe === "object" && maybe.agent && isLikelyAgentInstance(maybe.agent)) {
|
|
595
|
+
candidate = maybe.agent;
|
|
596
|
+
break;
|
|
597
|
+
}
|
|
598
|
+
} catch (e) {
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
if (typeof candidate === "function") {
|
|
604
|
+
try {
|
|
605
|
+
candidate = await invokeMaybe(candidate);
|
|
606
|
+
} catch (e) {
|
|
607
|
+
throw new Error(
|
|
608
|
+
`Failed executing exported agent function: ${e instanceof Error ? e.message : String(e)}`
|
|
609
|
+
);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
if (candidate && typeof candidate === "object" && candidate.agent && isLikelyAgentInstance(candidate.agent)) {
|
|
613
|
+
candidate = candidate.agent;
|
|
614
|
+
}
|
|
615
|
+
if (candidate?.agent && isLikelyAgentInstance(candidate.agent)) {
|
|
616
|
+
candidate = candidate.agent;
|
|
617
|
+
}
|
|
618
|
+
if (!candidate || !isLikelyAgentInstance(candidate)) {
|
|
619
|
+
throw new Error(
|
|
620
|
+
"No agent export resolved (expected variable, function, or function returning an agent)"
|
|
621
|
+
);
|
|
622
|
+
}
|
|
623
|
+
return { agent: candidate };
|
|
624
|
+
}
|
|
625
|
+
};
|
|
626
|
+
var AgentManager = class {
|
|
627
|
+
constructor(sessionService, quiet = false) {
|
|
628
|
+
this.sessionService = sessionService;
|
|
629
|
+
this.quiet = quiet;
|
|
630
|
+
this.scanner = new AgentScanner(quiet);
|
|
631
|
+
this.loader = new AgentLoader(quiet);
|
|
632
|
+
}
|
|
633
|
+
agents = /* @__PURE__ */ new Map();
|
|
634
|
+
loadedAgents = /* @__PURE__ */ new Map();
|
|
635
|
+
scanner;
|
|
636
|
+
loader;
|
|
637
|
+
getAgents() {
|
|
638
|
+
return this.agents;
|
|
639
|
+
}
|
|
640
|
+
getLoadedAgents() {
|
|
641
|
+
return this.loadedAgents;
|
|
642
|
+
}
|
|
643
|
+
scanAgents(agentsDir) {
|
|
644
|
+
this.agents = this.scanner.scanAgents(agentsDir, this.loadedAgents);
|
|
645
|
+
}
|
|
455
646
|
async startAgent(agentPath) {
|
|
456
647
|
const agent = this.agents.get(agentPath);
|
|
457
648
|
if (!agent) {
|
|
@@ -470,70 +661,31 @@ var ADKServer = class {
|
|
|
470
661
|
`No agent.js or agent.ts file found in ${agent.absolutePath}`
|
|
471
662
|
);
|
|
472
663
|
}
|
|
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
|
-
}
|
|
664
|
+
this.loader.loadEnvironmentVariables(agentFilePath);
|
|
501
665
|
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) {
|
|
666
|
+
const agentModule = agentFilePath.endsWith(".ts") ? await this.loader.importTypeScriptFile(agentFilePath) : await import(agentFileUrl);
|
|
667
|
+
const resolved = await this.loader.resolveAgentExport(agentModule);
|
|
668
|
+
const exportedAgent = resolved.agent;
|
|
669
|
+
if (!exportedAgent?.name) {
|
|
507
670
|
throw new Error(
|
|
508
671
|
`Invalid agent export in ${agentFilePath}. Expected an LlmAgent instance with a name property.`
|
|
509
672
|
);
|
|
510
673
|
}
|
|
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, {
|
|
674
|
+
const agentBuilder = AgentBuilder.create(exportedAgent.name).withAgent(exportedAgent).withSessionService(this.sessionService, {
|
|
514
675
|
userId: `user_${agentPath}`,
|
|
515
676
|
appName: "adk-server"
|
|
516
677
|
});
|
|
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
678
|
const { runner, session } = await agentBuilder.build();
|
|
527
679
|
const loadedAgent = {
|
|
528
|
-
agent:
|
|
680
|
+
agent: exportedAgent,
|
|
529
681
|
runner,
|
|
530
682
|
sessionId: session.id,
|
|
531
683
|
userId: `user_${agentPath}`,
|
|
532
684
|
appName: "adk-server"
|
|
533
685
|
};
|
|
534
686
|
this.loadedAgents.set(agentPath, loadedAgent);
|
|
535
|
-
agent.instance =
|
|
536
|
-
agent.name =
|
|
687
|
+
agent.instance = exportedAgent;
|
|
688
|
+
agent.name = exportedAgent.name;
|
|
537
689
|
} catch (error) {
|
|
538
690
|
console.error(`\u274C Failed to load agent "${agent.name}":`, error);
|
|
539
691
|
throw new Error(
|
|
@@ -568,78 +720,65 @@ var ADKServer = class {
|
|
|
568
720
|
throw new Error(`Failed to send message to agent: ${errorMessage}`);
|
|
569
721
|
}
|
|
570
722
|
}
|
|
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);
|
|
723
|
+
stopAllAgents() {
|
|
724
|
+
for (const [agentPath] of this.loadedAgents.entries()) {
|
|
725
|
+
this.stopAgent(agentPath);
|
|
581
726
|
}
|
|
727
|
+
}
|
|
728
|
+
};
|
|
729
|
+
var SessionManager = class {
|
|
730
|
+
constructor(sessionService) {
|
|
731
|
+
this.sessionService = sessionService;
|
|
732
|
+
}
|
|
733
|
+
async getSessionMessages(loadedAgent) {
|
|
582
734
|
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)}`
|
|
735
|
+
const session = await this.sessionService.getSession(
|
|
736
|
+
loadedAgent.appName,
|
|
737
|
+
loadedAgent.userId,
|
|
738
|
+
loadedAgent.sessionId
|
|
633
739
|
);
|
|
740
|
+
if (!session || !session.events) {
|
|
741
|
+
return [];
|
|
742
|
+
}
|
|
743
|
+
const messages = session.events.map((event, index) => ({
|
|
744
|
+
id: index + 1,
|
|
745
|
+
type: event.author === "user" ? "user" : "assistant",
|
|
746
|
+
content: event.content?.parts?.map(
|
|
747
|
+
(part) => typeof part === "object" && "text" in part ? part.text : ""
|
|
748
|
+
).join("") || "",
|
|
749
|
+
timestamp: new Date(event.timestamp || Date.now()).toISOString()
|
|
750
|
+
}));
|
|
751
|
+
return messages;
|
|
752
|
+
} catch (error) {
|
|
753
|
+
console.error("Error fetching messages:", error);
|
|
754
|
+
return [];
|
|
634
755
|
}
|
|
635
|
-
|
|
756
|
+
}
|
|
757
|
+
};
|
|
758
|
+
|
|
759
|
+
// src/server/index.ts
|
|
760
|
+
var ADKServer = class {
|
|
761
|
+
agentManager;
|
|
762
|
+
sessionManager;
|
|
763
|
+
sessionService;
|
|
764
|
+
app;
|
|
765
|
+
server;
|
|
766
|
+
config;
|
|
767
|
+
constructor(agentsDir, port = 8042, host = "localhost", quiet = false) {
|
|
768
|
+
this.config = { agentsDir, port, host, quiet };
|
|
769
|
+
this.sessionService = new InMemorySessionService();
|
|
770
|
+
this.agentManager = new AgentManager(this.sessionService, quiet);
|
|
771
|
+
this.sessionManager = new SessionManager(this.sessionService);
|
|
772
|
+
this.app = new Hono();
|
|
773
|
+
setupRoutes(this.app, this.agentManager, this.sessionManager, agentsDir);
|
|
774
|
+
this.agentManager.scanAgents(agentsDir);
|
|
636
775
|
}
|
|
637
776
|
async start() {
|
|
638
|
-
return new Promise((resolve2
|
|
777
|
+
return new Promise((resolve2) => {
|
|
639
778
|
this.server = serve({
|
|
640
779
|
fetch: this.app.fetch,
|
|
641
|
-
port: this.port,
|
|
642
|
-
hostname: this.host
|
|
780
|
+
port: this.config.port,
|
|
781
|
+
hostname: this.config.host
|
|
643
782
|
});
|
|
644
783
|
setTimeout(() => {
|
|
645
784
|
resolve2();
|
|
@@ -648,9 +787,7 @@ var ADKServer = class {
|
|
|
648
787
|
}
|
|
649
788
|
async stop() {
|
|
650
789
|
return new Promise((resolve2) => {
|
|
651
|
-
|
|
652
|
-
this.stopAgent(agentPath);
|
|
653
|
-
}
|
|
790
|
+
this.agentManager.stopAllAgents();
|
|
654
791
|
if (this.server) {
|
|
655
792
|
this.server.close();
|
|
656
793
|
}
|
|
@@ -658,19 +795,7 @@ var ADKServer = class {
|
|
|
658
795
|
});
|
|
659
796
|
}
|
|
660
797
|
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
|
-
}
|
|
798
|
+
return this.config.port;
|
|
674
799
|
}
|
|
675
800
|
};
|
|
676
801
|
|