@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/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,44 @@
|
|
|
1
1
|
# @iqai/adk-cli
|
|
2
2
|
|
|
3
|
+
## 0.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 17341fc: Refactor agent loading and resolution logic with enhanced flexibility and reliability
|
|
8
|
+
|
|
9
|
+
This major enhancement improves the ADK CLI server's agent loading capabilities and adds new features to the core framework:
|
|
10
|
+
|
|
11
|
+
**CLI Server Improvements:**
|
|
12
|
+
|
|
13
|
+
- **Modular Architecture**: Refactored monolithic server file into organized modules (`server/index.ts`, `server/routes.ts`, `server/services.ts`, `server/types.ts`)
|
|
14
|
+
- **Enhanced Agent Resolution**: New `resolveAgentExport` method supports multiple export patterns:
|
|
15
|
+
- Direct agent exports: `export const agent = new LlmAgent(...)`
|
|
16
|
+
- Function factories: `export function agent() { return new LlmAgent(...) }`
|
|
17
|
+
- Async factories: `export async function agent() { return new LlmAgent(...) }`
|
|
18
|
+
- Container objects: `export default { agent: ... }`
|
|
19
|
+
- Primitive exports with fallback scanning
|
|
20
|
+
- **Improved TypeScript Import Handling**: Better project root detection and module resolution for TypeScript files
|
|
21
|
+
|
|
22
|
+
**Core Framework Enhancements:**
|
|
23
|
+
|
|
24
|
+
- **New AgentBuilder Method**: Added `withAgent()` method to directly provide existing agent instances with definition locking to prevent accidental configuration overwrites
|
|
25
|
+
- **Two-Tier Tool Deduplication**: Implemented robust deduplication logic to prevent duplicate function declarations that cause errors with LLM providers (especially Google)
|
|
26
|
+
- **Better Type Safety**: Improved type definitions and replaced `any[]` usage with proper typed interfaces
|
|
27
|
+
|
|
28
|
+
**Testing & Reliability:**
|
|
29
|
+
|
|
30
|
+
- **Comprehensive Test Coverage**: New `agent-resolution.test.ts` with extensive fixtures testing various agent export patterns
|
|
31
|
+
- **Multiple Test Fixtures**: Added 6 different agent export pattern examples for validation
|
|
32
|
+
- **Edge Case Handling**: Improved error handling and logging throughout the agent loading pipeline
|
|
33
|
+
|
|
34
|
+
These changes provide a more flexible, reliable, and maintainable foundation for agent development and deployment while maintaining backward compatibility.
|
|
35
|
+
|
|
36
|
+
### Patch Changes
|
|
37
|
+
|
|
38
|
+
- Updated dependencies [17341fc]
|
|
39
|
+
- Updated dependencies [1564b7b]
|
|
40
|
+
- @iqai/adk@0.2.0
|
|
41
|
+
|
|
3
42
|
## 0.1.1
|
|
4
43
|
|
|
5
44
|
### Patch Changes
|
package/dist/index.js
CHANGED
|
@@ -31,7 +31,7 @@ var import_commander = require("commander");
|
|
|
31
31
|
// package.json
|
|
32
32
|
var package_default = {
|
|
33
33
|
name: "@iqai/adk-cli",
|
|
34
|
-
version: "0.
|
|
34
|
+
version: "0.2.0",
|
|
35
35
|
description: "CLI tool for creating, running, and testing ADK agents",
|
|
36
36
|
main: "dist/index.js",
|
|
37
37
|
types: "dist/index.d.ts",
|
|
@@ -328,95 +328,70 @@ var import_node_fs3 = require("fs");
|
|
|
328
328
|
var import_node_path3 = require("path");
|
|
329
329
|
var import_chalk2 = __toESM(require("chalk"));
|
|
330
330
|
|
|
331
|
-
// src/server.ts
|
|
331
|
+
// src/server/index.ts
|
|
332
|
+
var import_node_server = require("@hono/node-server");
|
|
333
|
+
var import_adk2 = require("@iqai/adk");
|
|
334
|
+
var import_hono = require("hono");
|
|
335
|
+
|
|
336
|
+
// src/server/routes.ts
|
|
337
|
+
var import_cors = require("hono/cors");
|
|
338
|
+
function setupRoutes(app, agentManager, sessionManager, agentsDir) {
|
|
339
|
+
app.use("/*", (0, import_cors.cors)());
|
|
340
|
+
app.get("/health", (c) => c.json({ status: "ok" }));
|
|
341
|
+
app.get("/api/agents", (c) => {
|
|
342
|
+
const agentsList = Array.from(
|
|
343
|
+
agentManager.getAgents().values()
|
|
344
|
+
).map((agent) => ({
|
|
345
|
+
path: agent.absolutePath,
|
|
346
|
+
name: agent.name,
|
|
347
|
+
directory: agent.absolutePath,
|
|
348
|
+
relativePath: agent.relativePath
|
|
349
|
+
}));
|
|
350
|
+
return c.json({ agents: agentsList });
|
|
351
|
+
});
|
|
352
|
+
app.post("/api/agents/refresh", (c) => {
|
|
353
|
+
agentManager.scanAgents(agentsDir);
|
|
354
|
+
const agentsList = Array.from(
|
|
355
|
+
agentManager.getAgents().values()
|
|
356
|
+
).map((agent) => ({
|
|
357
|
+
path: agent.absolutePath,
|
|
358
|
+
name: agent.name,
|
|
359
|
+
directory: agent.absolutePath,
|
|
360
|
+
relativePath: agent.relativePath
|
|
361
|
+
}));
|
|
362
|
+
return c.json({ agents: agentsList });
|
|
363
|
+
});
|
|
364
|
+
app.get("/api/agents/:id/messages", async (c) => {
|
|
365
|
+
const agentPath = decodeURIComponent(c.req.param("id"));
|
|
366
|
+
const loadedAgent = agentManager.getLoadedAgents().get(agentPath);
|
|
367
|
+
if (!loadedAgent) {
|
|
368
|
+
return c.json({ messages: [] });
|
|
369
|
+
}
|
|
370
|
+
const messages = await sessionManager.getSessionMessages(loadedAgent);
|
|
371
|
+
const response = { messages };
|
|
372
|
+
return c.json(response);
|
|
373
|
+
});
|
|
374
|
+
app.post("/api/agents/:id/message", async (c) => {
|
|
375
|
+
const agentPath = decodeURIComponent(c.req.param("id"));
|
|
376
|
+
const { message } = await c.req.json();
|
|
377
|
+
const response = await agentManager.sendMessageToAgent(agentPath, message);
|
|
378
|
+
const messageResponse = { response };
|
|
379
|
+
return c.json(messageResponse);
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// src/server/services.ts
|
|
332
384
|
var import_node_fs2 = require("fs");
|
|
333
385
|
var import_node_path2 = require("path");
|
|
334
386
|
var import_node_url = require("url");
|
|
335
|
-
var import_node_server = require("@hono/node-server");
|
|
336
387
|
var import_adk = require("@iqai/adk");
|
|
337
|
-
var
|
|
338
|
-
|
|
339
|
-
var ADKServer = class {
|
|
340
|
-
agents = /* @__PURE__ */ new Map();
|
|
341
|
-
loadedAgents = /* @__PURE__ */ new Map();
|
|
342
|
-
sessionService;
|
|
343
|
-
app;
|
|
344
|
-
server;
|
|
345
|
-
agentsDir;
|
|
346
|
-
port;
|
|
347
|
-
host;
|
|
348
|
-
quiet;
|
|
349
|
-
constructor(agentsDir, port = 8042, host = "localhost", quiet = false) {
|
|
350
|
-
this.agentsDir = agentsDir;
|
|
351
|
-
this.port = port;
|
|
352
|
-
this.host = host;
|
|
388
|
+
var AgentScanner = class {
|
|
389
|
+
constructor(quiet = false) {
|
|
353
390
|
this.quiet = quiet;
|
|
354
|
-
this.sessionService = new import_adk.InMemorySessionService();
|
|
355
|
-
this.app = new import_hono.Hono();
|
|
356
|
-
this.setupRoutes();
|
|
357
|
-
this.scanAgents();
|
|
358
391
|
}
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
this.app.get("/api/agents", (c) => {
|
|
363
|
-
const agentsList = Array.from(this.agents.values()).map((agent) => ({
|
|
364
|
-
path: agent.absolutePath,
|
|
365
|
-
name: agent.name,
|
|
366
|
-
directory: agent.absolutePath,
|
|
367
|
-
relativePath: agent.relativePath
|
|
368
|
-
}));
|
|
369
|
-
return c.json({ agents: agentsList });
|
|
370
|
-
});
|
|
371
|
-
this.app.post("/api/agents/refresh", (c) => {
|
|
372
|
-
this.scanAgents();
|
|
373
|
-
const agentsList = Array.from(this.agents.values()).map((agent) => ({
|
|
374
|
-
path: agent.absolutePath,
|
|
375
|
-
name: agent.name,
|
|
376
|
-
directory: agent.absolutePath,
|
|
377
|
-
relativePath: agent.relativePath
|
|
378
|
-
}));
|
|
379
|
-
return c.json({ agents: agentsList });
|
|
380
|
-
});
|
|
381
|
-
this.app.get("/api/agents/:id/messages", async (c) => {
|
|
382
|
-
const agentPath = decodeURIComponent(c.req.param("id"));
|
|
383
|
-
const loadedAgent = this.loadedAgents.get(agentPath);
|
|
384
|
-
if (!loadedAgent) {
|
|
385
|
-
return c.json({ messages: [] });
|
|
386
|
-
}
|
|
387
|
-
try {
|
|
388
|
-
const session = await this.sessionService.getSession(
|
|
389
|
-
loadedAgent.appName,
|
|
390
|
-
loadedAgent.userId,
|
|
391
|
-
loadedAgent.sessionId
|
|
392
|
-
);
|
|
393
|
-
if (!session || !session.events) {
|
|
394
|
-
return c.json({ messages: [] });
|
|
395
|
-
}
|
|
396
|
-
const messages = session.events.map((event, index) => ({
|
|
397
|
-
id: index + 1,
|
|
398
|
-
type: event.author === "user" ? "user" : "assistant",
|
|
399
|
-
content: event.content?.parts?.map(
|
|
400
|
-
(part) => typeof part === "object" && "text" in part ? part.text : ""
|
|
401
|
-
).join("") || "",
|
|
402
|
-
timestamp: new Date(event.timestamp || Date.now()).toISOString()
|
|
403
|
-
}));
|
|
404
|
-
return c.json({ messages });
|
|
405
|
-
} catch (error) {
|
|
406
|
-
console.error("Error fetching messages:", error);
|
|
407
|
-
return c.json({ messages: [] });
|
|
408
|
-
}
|
|
409
|
-
});
|
|
410
|
-
this.app.post("/api/agents/:id/message", async (c) => {
|
|
411
|
-
const agentPath = decodeURIComponent(c.req.param("id"));
|
|
412
|
-
const { message } = await c.req.json();
|
|
413
|
-
const response = await this.sendMessageToAgent(agentPath, message);
|
|
414
|
-
return c.json({ response });
|
|
415
|
-
});
|
|
416
|
-
}
|
|
417
|
-
scanAgents() {
|
|
418
|
-
this.agents.clear();
|
|
419
|
-
const scanDir = !this.agentsDir || !(0, import_node_fs2.existsSync)(this.agentsDir) ? process.cwd() : this.agentsDir;
|
|
392
|
+
scanAgents(agentsDir, loadedAgents) {
|
|
393
|
+
const agents = /* @__PURE__ */ new Map();
|
|
394
|
+
const scanDir = !agentsDir || !(0, import_node_fs2.existsSync)(agentsDir) ? process.cwd() : agentsDir;
|
|
420
395
|
const shouldSkipDirectory = (dirName) => {
|
|
421
396
|
const skipDirs = [
|
|
422
397
|
"node_modules",
|
|
@@ -443,7 +418,7 @@ var ADKServer = class {
|
|
|
443
418
|
}
|
|
444
419
|
} else if (item === "agent.ts" || item === "agent.js") {
|
|
445
420
|
const relativePath = (0, import_node_path2.relative)(scanDir, dir);
|
|
446
|
-
const loadedAgent =
|
|
421
|
+
const loadedAgent = loadedAgents.get(relativePath);
|
|
447
422
|
let agentName = relativePath.split("/").pop() || "unknown";
|
|
448
423
|
if (loadedAgent?.agent?.name) {
|
|
449
424
|
agentName = loadedAgent.agent.name;
|
|
@@ -454,7 +429,7 @@ var ADKServer = class {
|
|
|
454
429
|
} catch {
|
|
455
430
|
}
|
|
456
431
|
}
|
|
457
|
-
|
|
432
|
+
agents.set(relativePath, {
|
|
458
433
|
relativePath,
|
|
459
434
|
name: agentName,
|
|
460
435
|
absolutePath: dir,
|
|
@@ -465,9 +440,225 @@ var ADKServer = class {
|
|
|
465
440
|
};
|
|
466
441
|
scanDirectory(scanDir);
|
|
467
442
|
if (!this.quiet) {
|
|
468
|
-
console.log(`\u2705 Agent scan complete. Found ${
|
|
443
|
+
console.log(`\u2705 Agent scan complete. Found ${agents.size} agents.`);
|
|
444
|
+
}
|
|
445
|
+
return agents;
|
|
446
|
+
}
|
|
447
|
+
extractAgentNameFromFile(filePath) {
|
|
448
|
+
try {
|
|
449
|
+
const content = (0, import_node_fs2.readFileSync)(filePath, "utf-8");
|
|
450
|
+
const nameMatch = content.match(/name\s*:\s*["']([^"']+)["']/);
|
|
451
|
+
if (nameMatch?.[1]) {
|
|
452
|
+
return nameMatch[1];
|
|
453
|
+
}
|
|
454
|
+
return null;
|
|
455
|
+
} catch {
|
|
456
|
+
return null;
|
|
469
457
|
}
|
|
470
458
|
}
|
|
459
|
+
};
|
|
460
|
+
var AgentLoader = class {
|
|
461
|
+
constructor(quiet = false) {
|
|
462
|
+
this.quiet = quiet;
|
|
463
|
+
}
|
|
464
|
+
/**
|
|
465
|
+
* Import a TypeScript file by compiling it on-demand
|
|
466
|
+
*/
|
|
467
|
+
async importTypeScriptFile(filePath) {
|
|
468
|
+
const startDir = (0, import_node_path2.dirname)(filePath);
|
|
469
|
+
let projectRoot = startDir;
|
|
470
|
+
while (projectRoot !== "/" && projectRoot !== (0, import_node_path2.dirname)(projectRoot)) {
|
|
471
|
+
if ((0, import_node_fs2.existsSync)((0, import_node_path2.join)(projectRoot, "package.json")) || (0, import_node_fs2.existsSync)((0, import_node_path2.join)(projectRoot, ".env"))) {
|
|
472
|
+
break;
|
|
473
|
+
}
|
|
474
|
+
projectRoot = (0, import_node_path2.dirname)(projectRoot);
|
|
475
|
+
}
|
|
476
|
+
if (projectRoot === "/") {
|
|
477
|
+
projectRoot = startDir;
|
|
478
|
+
}
|
|
479
|
+
try {
|
|
480
|
+
const { build } = await import("esbuild");
|
|
481
|
+
const cacheDir = (0, import_node_path2.join)(projectRoot, ".adk-cache");
|
|
482
|
+
if (!(0, import_node_fs2.existsSync)(cacheDir)) {
|
|
483
|
+
(0, import_node_fs2.mkdirSync)(cacheDir, { recursive: true });
|
|
484
|
+
}
|
|
485
|
+
const outFile = (0, import_node_path2.join)(cacheDir, `agent-${Date.now()}.mjs`);
|
|
486
|
+
const plugin = {
|
|
487
|
+
name: "externalize-bare-imports",
|
|
488
|
+
setup(build2) {
|
|
489
|
+
build2.onResolve({ filter: /.*/ }, (args) => {
|
|
490
|
+
if (args.path.startsWith(".") || args.path.startsWith("/") || args.path.startsWith("..")) {
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
return { path: args.path, external: true };
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
};
|
|
497
|
+
const tsconfigPath = (0, import_node_path2.join)(projectRoot, "tsconfig.json");
|
|
498
|
+
await build({
|
|
499
|
+
entryPoints: [filePath],
|
|
500
|
+
outfile: outFile,
|
|
501
|
+
bundle: true,
|
|
502
|
+
format: "esm",
|
|
503
|
+
platform: "node",
|
|
504
|
+
target: ["node22"],
|
|
505
|
+
sourcemap: false,
|
|
506
|
+
logLevel: "silent",
|
|
507
|
+
plugins: [plugin],
|
|
508
|
+
absWorkingDir: projectRoot,
|
|
509
|
+
// Use tsconfig if present for path aliases
|
|
510
|
+
...(0, import_node_fs2.existsSync)(tsconfigPath) ? { tsconfig: tsconfigPath } : {}
|
|
511
|
+
});
|
|
512
|
+
const mod = await import(`${(0, import_node_url.pathToFileURL)(outFile).href}?t=${Date.now()}`);
|
|
513
|
+
let agentExport = mod?.agent;
|
|
514
|
+
if (!agentExport && mod?.default) {
|
|
515
|
+
agentExport = mod.default.agent ?? mod.default;
|
|
516
|
+
}
|
|
517
|
+
try {
|
|
518
|
+
(0, import_node_fs2.unlinkSync)(outFile);
|
|
519
|
+
} catch {
|
|
520
|
+
}
|
|
521
|
+
if (agentExport) {
|
|
522
|
+
const isPrimitive = (v) => v == null || ["string", "number", "boolean"].includes(typeof v);
|
|
523
|
+
if (isPrimitive(agentExport)) {
|
|
524
|
+
if (!this.quiet) {
|
|
525
|
+
console.log(
|
|
526
|
+
`\u2139\uFE0F Ignoring primitive 'agent' export in ${filePath}; scanning module for factory...`
|
|
527
|
+
);
|
|
528
|
+
}
|
|
529
|
+
} else {
|
|
530
|
+
if (!this.quiet) {
|
|
531
|
+
console.log(`\u2705 TS agent imported via esbuild: ${filePath}`);
|
|
532
|
+
}
|
|
533
|
+
return { agent: agentExport };
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
return mod;
|
|
537
|
+
} catch (e) {
|
|
538
|
+
throw new Error(
|
|
539
|
+
`Failed to import TS agent via esbuild: ${e instanceof Error ? e.message : String(e)}`
|
|
540
|
+
);
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
loadEnvironmentVariables(agentFilePath) {
|
|
544
|
+
let projectRoot = (0, import_node_path2.dirname)(agentFilePath);
|
|
545
|
+
while (projectRoot !== "/" && projectRoot !== (0, import_node_path2.dirname)(projectRoot)) {
|
|
546
|
+
if ((0, import_node_fs2.existsSync)((0, import_node_path2.join)(projectRoot, "package.json")) || (0, import_node_fs2.existsSync)((0, import_node_path2.join)(projectRoot, ".env"))) {
|
|
547
|
+
break;
|
|
548
|
+
}
|
|
549
|
+
projectRoot = (0, import_node_path2.dirname)(projectRoot);
|
|
550
|
+
}
|
|
551
|
+
const envPath = (0, import_node_path2.join)(projectRoot, ".env");
|
|
552
|
+
if ((0, import_node_fs2.existsSync)(envPath)) {
|
|
553
|
+
try {
|
|
554
|
+
const envContent = (0, import_node_fs2.readFileSync)(envPath, "utf8");
|
|
555
|
+
const envLines = envContent.split("\n");
|
|
556
|
+
for (const line of envLines) {
|
|
557
|
+
const trimmedLine = line.trim();
|
|
558
|
+
if (trimmedLine && !trimmedLine.startsWith("#")) {
|
|
559
|
+
const [key, ...valueParts] = trimmedLine.split("=");
|
|
560
|
+
if (key && valueParts.length > 0) {
|
|
561
|
+
const value = valueParts.join("=").replace(/^"(.*)"$/, "$1");
|
|
562
|
+
process.env[key.trim()] = value.trim();
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
} catch (error) {
|
|
567
|
+
console.warn(
|
|
568
|
+
`\u26A0\uFE0F Warning: Could not load .env file: ${error instanceof Error ? error.message : String(error)}`
|
|
569
|
+
);
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
// Minimal resolution logic for agent exports: supports
|
|
574
|
+
// 1) export const agent = new LlmAgent(...)
|
|
575
|
+
// 2) export function agent() { return new LlmAgent(...) }
|
|
576
|
+
// 3) export async function agent() { return new LlmAgent(...) }
|
|
577
|
+
// 4) default export (object or function) returning or containing .agent
|
|
578
|
+
async resolveAgentExport(mod) {
|
|
579
|
+
let candidate = mod?.agent ?? mod?.default?.agent ?? mod?.default ?? mod;
|
|
580
|
+
const isLikelyAgentInstance = (obj) => obj && typeof obj === "object" && typeof obj.name === "string";
|
|
581
|
+
const isPrimitive = (v) => v == null || ["string", "number", "boolean"].includes(typeof v);
|
|
582
|
+
const invokeMaybe = async (fn) => {
|
|
583
|
+
let out = fn();
|
|
584
|
+
if (out && typeof out === "object" && "then" in out) {
|
|
585
|
+
out = await out;
|
|
586
|
+
}
|
|
587
|
+
return out;
|
|
588
|
+
};
|
|
589
|
+
if (!isLikelyAgentInstance(candidate) && isPrimitive(candidate) || !isLikelyAgentInstance(candidate) && candidate && candidate === mod) {
|
|
590
|
+
candidate = mod;
|
|
591
|
+
for (const [key, value] of Object.entries(mod)) {
|
|
592
|
+
if (key === "default") continue;
|
|
593
|
+
const keyLower = key.toLowerCase();
|
|
594
|
+
if (isPrimitive(value)) continue;
|
|
595
|
+
if (isLikelyAgentInstance(value)) {
|
|
596
|
+
candidate = value;
|
|
597
|
+
break;
|
|
598
|
+
}
|
|
599
|
+
if (value && typeof value === "object" && value.agent && isLikelyAgentInstance(value.agent)) {
|
|
600
|
+
candidate = value.agent;
|
|
601
|
+
break;
|
|
602
|
+
}
|
|
603
|
+
if (typeof value === "function" && (/(agent|build|create)/i.test(keyLower) || value.name && /(agent|build|create)/i.test(value.name.toLowerCase()))) {
|
|
604
|
+
try {
|
|
605
|
+
const maybe = await invokeMaybe(value);
|
|
606
|
+
if (isLikelyAgentInstance(maybe)) {
|
|
607
|
+
candidate = maybe;
|
|
608
|
+
break;
|
|
609
|
+
}
|
|
610
|
+
if (maybe && typeof maybe === "object" && maybe.agent && isLikelyAgentInstance(maybe.agent)) {
|
|
611
|
+
candidate = maybe.agent;
|
|
612
|
+
break;
|
|
613
|
+
}
|
|
614
|
+
} catch (e) {
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
if (typeof candidate === "function") {
|
|
620
|
+
try {
|
|
621
|
+
candidate = await invokeMaybe(candidate);
|
|
622
|
+
} catch (e) {
|
|
623
|
+
throw new Error(
|
|
624
|
+
`Failed executing exported agent function: ${e instanceof Error ? e.message : String(e)}`
|
|
625
|
+
);
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
if (candidate && typeof candidate === "object" && candidate.agent && isLikelyAgentInstance(candidate.agent)) {
|
|
629
|
+
candidate = candidate.agent;
|
|
630
|
+
}
|
|
631
|
+
if (candidate?.agent && isLikelyAgentInstance(candidate.agent)) {
|
|
632
|
+
candidate = candidate.agent;
|
|
633
|
+
}
|
|
634
|
+
if (!candidate || !isLikelyAgentInstance(candidate)) {
|
|
635
|
+
throw new Error(
|
|
636
|
+
"No agent export resolved (expected variable, function, or function returning an agent)"
|
|
637
|
+
);
|
|
638
|
+
}
|
|
639
|
+
return { agent: candidate };
|
|
640
|
+
}
|
|
641
|
+
};
|
|
642
|
+
var AgentManager = class {
|
|
643
|
+
constructor(sessionService, quiet = false) {
|
|
644
|
+
this.sessionService = sessionService;
|
|
645
|
+
this.quiet = quiet;
|
|
646
|
+
this.scanner = new AgentScanner(quiet);
|
|
647
|
+
this.loader = new AgentLoader(quiet);
|
|
648
|
+
}
|
|
649
|
+
agents = /* @__PURE__ */ new Map();
|
|
650
|
+
loadedAgents = /* @__PURE__ */ new Map();
|
|
651
|
+
scanner;
|
|
652
|
+
loader;
|
|
653
|
+
getAgents() {
|
|
654
|
+
return this.agents;
|
|
655
|
+
}
|
|
656
|
+
getLoadedAgents() {
|
|
657
|
+
return this.loadedAgents;
|
|
658
|
+
}
|
|
659
|
+
scanAgents(agentsDir) {
|
|
660
|
+
this.agents = this.scanner.scanAgents(agentsDir, this.loadedAgents);
|
|
661
|
+
}
|
|
471
662
|
async startAgent(agentPath) {
|
|
472
663
|
const agent = this.agents.get(agentPath);
|
|
473
664
|
if (!agent) {
|
|
@@ -486,70 +677,31 @@ var ADKServer = class {
|
|
|
486
677
|
`No agent.js or agent.ts file found in ${agent.absolutePath}`
|
|
487
678
|
);
|
|
488
679
|
}
|
|
489
|
-
|
|
490
|
-
while (projectRoot !== "/" && projectRoot !== (0, import_node_path2.dirname)(projectRoot)) {
|
|
491
|
-
if ((0, import_node_fs2.existsSync)((0, import_node_path2.join)(projectRoot, "package.json")) || (0, import_node_fs2.existsSync)((0, import_node_path2.join)(projectRoot, ".env"))) {
|
|
492
|
-
break;
|
|
493
|
-
}
|
|
494
|
-
projectRoot = (0, import_node_path2.dirname)(projectRoot);
|
|
495
|
-
}
|
|
496
|
-
const envPath = (0, import_node_path2.join)(projectRoot, ".env");
|
|
497
|
-
if ((0, import_node_fs2.existsSync)(envPath)) {
|
|
498
|
-
try {
|
|
499
|
-
const envContent = (0, import_node_fs2.readFileSync)(envPath, "utf8");
|
|
500
|
-
const envLines = envContent.split("\n");
|
|
501
|
-
for (const line of envLines) {
|
|
502
|
-
const trimmedLine = line.trim();
|
|
503
|
-
if (trimmedLine && !trimmedLine.startsWith("#")) {
|
|
504
|
-
const [key, ...valueParts] = trimmedLine.split("=");
|
|
505
|
-
if (key && valueParts.length > 0) {
|
|
506
|
-
const value = valueParts.join("=").replace(/^"(.*)"$/, "$1");
|
|
507
|
-
process.env[key.trim()] = value.trim();
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
} catch (error) {
|
|
512
|
-
console.warn(
|
|
513
|
-
`\u26A0\uFE0F Warning: Could not load .env file: ${error instanceof Error ? error.message : String(error)}`
|
|
514
|
-
);
|
|
515
|
-
}
|
|
516
|
-
}
|
|
680
|
+
this.loader.loadEnvironmentVariables(agentFilePath);
|
|
517
681
|
const agentFileUrl = (0, import_node_url.pathToFileURL)(agentFilePath).href;
|
|
518
|
-
const agentModule = agentFilePath.endsWith(".ts") ? await this.importTypeScriptFile(agentFilePath) : await import(agentFileUrl);
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
if (!agentModule.agent || typeof agentModule.agent !== "object" || !agentModule.agent.name) {
|
|
682
|
+
const agentModule = agentFilePath.endsWith(".ts") ? await this.loader.importTypeScriptFile(agentFilePath) : await import(agentFileUrl);
|
|
683
|
+
const resolved = await this.loader.resolveAgentExport(agentModule);
|
|
684
|
+
const exportedAgent = resolved.agent;
|
|
685
|
+
if (!exportedAgent?.name) {
|
|
523
686
|
throw new Error(
|
|
524
687
|
`Invalid agent export in ${agentFilePath}. Expected an LlmAgent instance with a name property.`
|
|
525
688
|
);
|
|
526
689
|
}
|
|
527
|
-
|
|
528
|
-
}
|
|
529
|
-
const agentBuilder = import_adk.AgentBuilder.create(agentModule.agent.name).withModel(agentModule.agent.model).withDescription(agentModule.agent.description || "").withInstruction(agentModule.agent.instruction || "").withSessionService(this.sessionService, {
|
|
690
|
+
const agentBuilder = import_adk.AgentBuilder.create(exportedAgent.name).withAgent(exportedAgent).withSessionService(this.sessionService, {
|
|
530
691
|
userId: `user_${agentPath}`,
|
|
531
692
|
appName: "adk-server"
|
|
532
693
|
});
|
|
533
|
-
if (agentModule.agent.tools && Array.isArray(agentModule.agent.tools) && agentModule.agent.tools.length > 0) {
|
|
534
|
-
agentBuilder.withTools(...agentModule.agent.tools);
|
|
535
|
-
if (!this.quiet) {
|
|
536
|
-
const toolNames = agentModule.agent.tools.map((t) => t?.name || "<unnamed>").join(", ");
|
|
537
|
-
console.log(
|
|
538
|
-
`\u{1F9E9} Registered tools for agent "${agentModule.agent.name}": [${toolNames}]`
|
|
539
|
-
);
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
694
|
const { runner, session } = await agentBuilder.build();
|
|
543
695
|
const loadedAgent = {
|
|
544
|
-
agent:
|
|
696
|
+
agent: exportedAgent,
|
|
545
697
|
runner,
|
|
546
698
|
sessionId: session.id,
|
|
547
699
|
userId: `user_${agentPath}`,
|
|
548
700
|
appName: "adk-server"
|
|
549
701
|
};
|
|
550
702
|
this.loadedAgents.set(agentPath, loadedAgent);
|
|
551
|
-
agent.instance =
|
|
552
|
-
agent.name =
|
|
703
|
+
agent.instance = exportedAgent;
|
|
704
|
+
agent.name = exportedAgent.name;
|
|
553
705
|
} catch (error) {
|
|
554
706
|
console.error(`\u274C Failed to load agent "${agent.name}":`, error);
|
|
555
707
|
throw new Error(
|
|
@@ -584,78 +736,65 @@ var ADKServer = class {
|
|
|
584
736
|
throw new Error(`Failed to send message to agent: ${errorMessage}`);
|
|
585
737
|
}
|
|
586
738
|
}
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
async importTypeScriptFile(filePath) {
|
|
591
|
-
let projectRoot = (0, import_node_path2.dirname)(filePath);
|
|
592
|
-
while (projectRoot !== "/" && projectRoot !== (0, import_node_path2.dirname)(projectRoot)) {
|
|
593
|
-
if ((0, import_node_fs2.existsSync)((0, import_node_path2.join)(projectRoot, "package.json")) || (0, import_node_fs2.existsSync)((0, import_node_path2.join)(projectRoot, ".env"))) {
|
|
594
|
-
break;
|
|
595
|
-
}
|
|
596
|
-
projectRoot = (0, import_node_path2.dirname)(projectRoot);
|
|
739
|
+
stopAllAgents() {
|
|
740
|
+
for (const [agentPath] of this.loadedAgents.entries()) {
|
|
741
|
+
this.stopAgent(agentPath);
|
|
597
742
|
}
|
|
743
|
+
}
|
|
744
|
+
};
|
|
745
|
+
var SessionManager = class {
|
|
746
|
+
constructor(sessionService) {
|
|
747
|
+
this.sessionService = sessionService;
|
|
748
|
+
}
|
|
749
|
+
async getSessionMessages(loadedAgent) {
|
|
598
750
|
try {
|
|
599
|
-
const
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
}
|
|
604
|
-
const outFile = (0, import_node_path2.join)(cacheDir, `agent-${Date.now()}.mjs`);
|
|
605
|
-
const plugin = {
|
|
606
|
-
name: "externalize-bare-imports",
|
|
607
|
-
setup(build2) {
|
|
608
|
-
build2.onResolve({ filter: /.*/ }, (args) => {
|
|
609
|
-
if (args.path.startsWith(".") || args.path.startsWith("/") || args.path.startsWith("..")) {
|
|
610
|
-
return;
|
|
611
|
-
}
|
|
612
|
-
return { path: args.path, external: true };
|
|
613
|
-
});
|
|
614
|
-
}
|
|
615
|
-
};
|
|
616
|
-
const tsconfigPath = (0, import_node_path2.join)(projectRoot, "tsconfig.json");
|
|
617
|
-
await build({
|
|
618
|
-
entryPoints: [filePath],
|
|
619
|
-
outfile: outFile,
|
|
620
|
-
bundle: true,
|
|
621
|
-
format: "esm",
|
|
622
|
-
platform: "node",
|
|
623
|
-
target: ["node22"],
|
|
624
|
-
sourcemap: false,
|
|
625
|
-
logLevel: "silent",
|
|
626
|
-
plugins: [plugin],
|
|
627
|
-
absWorkingDir: projectRoot,
|
|
628
|
-
// Use tsconfig if present for path aliases
|
|
629
|
-
...(0, import_node_fs2.existsSync)(tsconfigPath) ? { tsconfig: tsconfigPath } : {}
|
|
630
|
-
});
|
|
631
|
-
const mod = await import(`${(0, import_node_url.pathToFileURL)(outFile).href}?t=${Date.now()}`);
|
|
632
|
-
let agentExport = mod?.agent;
|
|
633
|
-
if (!agentExport && mod?.default) {
|
|
634
|
-
agentExport = mod.default.agent ?? mod.default;
|
|
635
|
-
}
|
|
636
|
-
try {
|
|
637
|
-
(0, import_node_fs2.unlinkSync)(outFile);
|
|
638
|
-
} catch {
|
|
639
|
-
}
|
|
640
|
-
if (agentExport) {
|
|
641
|
-
if (!this.quiet) {
|
|
642
|
-
console.log(`\u2705 TS agent imported via esbuild: ${filePath}`);
|
|
643
|
-
}
|
|
644
|
-
return { agent: agentExport };
|
|
645
|
-
}
|
|
646
|
-
} catch (e) {
|
|
647
|
-
throw new Error(
|
|
648
|
-
`Failed to import TS agent via esbuild: ${e instanceof Error ? e.message : String(e)}`
|
|
751
|
+
const session = await this.sessionService.getSession(
|
|
752
|
+
loadedAgent.appName,
|
|
753
|
+
loadedAgent.userId,
|
|
754
|
+
loadedAgent.sessionId
|
|
649
755
|
);
|
|
756
|
+
if (!session || !session.events) {
|
|
757
|
+
return [];
|
|
758
|
+
}
|
|
759
|
+
const messages = session.events.map((event, index) => ({
|
|
760
|
+
id: index + 1,
|
|
761
|
+
type: event.author === "user" ? "user" : "assistant",
|
|
762
|
+
content: event.content?.parts?.map(
|
|
763
|
+
(part) => typeof part === "object" && "text" in part ? part.text : ""
|
|
764
|
+
).join("") || "",
|
|
765
|
+
timestamp: new Date(event.timestamp || Date.now()).toISOString()
|
|
766
|
+
}));
|
|
767
|
+
return messages;
|
|
768
|
+
} catch (error) {
|
|
769
|
+
console.error("Error fetching messages:", error);
|
|
770
|
+
return [];
|
|
650
771
|
}
|
|
651
|
-
|
|
772
|
+
}
|
|
773
|
+
};
|
|
774
|
+
|
|
775
|
+
// src/server/index.ts
|
|
776
|
+
var ADKServer = class {
|
|
777
|
+
agentManager;
|
|
778
|
+
sessionManager;
|
|
779
|
+
sessionService;
|
|
780
|
+
app;
|
|
781
|
+
server;
|
|
782
|
+
config;
|
|
783
|
+
constructor(agentsDir, port = 8042, host = "localhost", quiet = false) {
|
|
784
|
+
this.config = { agentsDir, port, host, quiet };
|
|
785
|
+
this.sessionService = new import_adk2.InMemorySessionService();
|
|
786
|
+
this.agentManager = new AgentManager(this.sessionService, quiet);
|
|
787
|
+
this.sessionManager = new SessionManager(this.sessionService);
|
|
788
|
+
this.app = new import_hono.Hono();
|
|
789
|
+
setupRoutes(this.app, this.agentManager, this.sessionManager, agentsDir);
|
|
790
|
+
this.agentManager.scanAgents(agentsDir);
|
|
652
791
|
}
|
|
653
792
|
async start() {
|
|
654
|
-
return new Promise((resolve2
|
|
793
|
+
return new Promise((resolve2) => {
|
|
655
794
|
this.server = (0, import_node_server.serve)({
|
|
656
795
|
fetch: this.app.fetch,
|
|
657
|
-
port: this.port,
|
|
658
|
-
hostname: this.host
|
|
796
|
+
port: this.config.port,
|
|
797
|
+
hostname: this.config.host
|
|
659
798
|
});
|
|
660
799
|
setTimeout(() => {
|
|
661
800
|
resolve2();
|
|
@@ -664,9 +803,7 @@ var ADKServer = class {
|
|
|
664
803
|
}
|
|
665
804
|
async stop() {
|
|
666
805
|
return new Promise((resolve2) => {
|
|
667
|
-
|
|
668
|
-
this.stopAgent(agentPath);
|
|
669
|
-
}
|
|
806
|
+
this.agentManager.stopAllAgents();
|
|
670
807
|
if (this.server) {
|
|
671
808
|
this.server.close();
|
|
672
809
|
}
|
|
@@ -674,19 +811,7 @@ var ADKServer = class {
|
|
|
674
811
|
});
|
|
675
812
|
}
|
|
676
813
|
getPort() {
|
|
677
|
-
return this.port;
|
|
678
|
-
}
|
|
679
|
-
extractAgentNameFromFile(filePath) {
|
|
680
|
-
try {
|
|
681
|
-
const content = (0, import_node_fs2.readFileSync)(filePath, "utf-8");
|
|
682
|
-
const nameMatch = content.match(/name\s*:\s*["']([^"']+)["']/);
|
|
683
|
-
if (nameMatch?.[1]) {
|
|
684
|
-
return nameMatch[1];
|
|
685
|
-
}
|
|
686
|
-
return null;
|
|
687
|
-
} catch {
|
|
688
|
-
return null;
|
|
689
|
-
}
|
|
814
|
+
return this.config.port;
|
|
690
815
|
}
|
|
691
816
|
};
|
|
692
817
|
|