@agentforce/adk
Version:
AgentForce Agent Development Kit - A powerful framework for building AI agents and servers
182 lines (158 loc) • 7.18 kB
text/typescript
import type { AgentForceServer } from "../../server";
import type { Context } from "hono";
import { validateHttpMethod, normalizePath } from "../handler/validationHandler";
/**
* Adds a workflow trigger that executes a workflow file when the route is called (chainable method)
*
* @param this - The AgentForceServer instance (bound context)
* @param method - HTTP method (typically "GET")
* @param path - The route path (e.g., "/trigger-workflow")
* @param workflowFilePath - Path to the workflow TypeScript file to execute
* @returns {AgentForceServer} The server instance for method chaining
*/
export function addWorkflowTrigger(
this: AgentForceServer,
method: string,
path: string,
workflowFilePath: string,
): AgentForceServer {
// Validate inputs
const normalizedMethod = validateHttpMethod(method);
const normalizedPath = normalizePath(path);
if (!workflowFilePath || typeof workflowFilePath !== "string") {
throw new Error("Workflow file path must be a non-empty string");
}
const log = this.getLogger();
const serverName = this.getName();
log.info({
serverName,
method: normalizedMethod,
path: normalizedPath,
workflowFilePath,
action: "workflow_trigger_adding",
}, `Adding workflow trigger: ${normalizedMethod} ${normalizedPath}`);
// Create workflow trigger handler
const workflowHandler = createWorkflowTriggerHandler(workflowFilePath, normalizedMethod, normalizedPath);
// Add as static route
this.addToStaticRoutes({
method: normalizedMethod,
path: normalizedPath,
responseData: workflowHandler,
});
log.info({
serverName,
method: normalizedMethod,
path: normalizedPath,
action: "workflow_trigger_added",
}, `Workflow trigger added: ${normalizedMethod} ${normalizedPath}`);
return this;
}
/**
* Creates a handler for executing workflow files
* @param workflowFilePath - Path to the workflow file to execute
* @param method - HTTP method for logging purposes
* @param path - Route path for logging purposes
* @returns Hono route handler function
*/
export function createWorkflowTriggerHandler(
workflowFilePath: string,
method: string,
path: string,
): (c: Context) => Promise<Response> {
return async (c: Context): Promise<Response> => {
try {
console.log(`🔄 Executing workflow: ${workflowFilePath}`);
// Resolve the absolute path to the workflow file
const { resolve } = await import("node:path");
const absolutePath = resolve(workflowFilePath);
console.log(`📁 Resolved workflow path: ${absolutePath}`);
// Execute the workflow file
// Note: This will execute the workflow file which should contain the workflow logic
// The workflow file is expected to export or execute a workflow
let workflowResult: any;
if (typeof globalThis.Bun !== "undefined") {
// Bun runtime - execute the file using Bun subprocess to avoid dynamic import issues
try {
const proc = Bun.spawn(["bun", "run", absolutePath], {
stdout: "pipe",
stderr: "pipe",
});
const output = await new Response(proc.stdout).text();
const error = await new Response(proc.stderr).text();
if (proc.exitCode !== 0) {
console.error(`❌ Workflow execution failed: ${error}`);
return c.json({
success: false,
error: "Workflow execution failed",
message: error,
method,
path,
workflowPath: absolutePath,
}, 500);
}
// Try to parse output as JSON, fallback to string
try {
workflowResult = JSON.parse(output);
} catch {
workflowResult = { output, executedAt: new Date().toISOString() };
}
} catch (bunError) {
console.error(`❌ Error executing workflow with Bun: ${bunError}`);
const errorMessage = bunError instanceof Error ? bunError.message : "Unknown error";
return c.json({
success: false,
error: "Workflow execution failed",
message: errorMessage,
method,
path,
workflowPath: absolutePath,
}, 500);
}
} else {
// Node.js runtime - use child_process
const { exec } = await import("node:child_process");
const { promisify } = await import("node:util");
const execAsync = promisify(exec);
try {
const { stdout, stderr } = await execAsync(`node ${absolutePath}`);
if (stderr && stderr.trim() !== "") {
console.warn(`⚠️ Workflow stderr: ${stderr}`);
}
workflowResult = {
output: stdout,
executedAt: new Date().toISOString(),
stderr: stderr || null,
};
} catch (execError: any) {
console.error(`❌ Error executing workflow with Node: ${execError.message}`);
return c.json({
success: false,
error: "Workflow execution failed",
message: execError.message,
method,
path,
workflowPath: absolutePath,
}, 500);
}
}
console.log("✅ Workflow executed successfully");
// Return success response
return c.json({
success: true,
message: "Workflow triggered successfully.",
workflowPath: workflowFilePath,
executedAt: new Date().toISOString(),
result: workflowResult,
});
} catch (error) {
console.error(`❌ Error executing workflow ${workflowFilePath}:`, error);
return c.json({
success: false,
message: "Workflow execution failed.",
workflowPath: workflowFilePath,
error: error instanceof Error ? error.message : "Unknown error occurred",
executedAt: new Date().toISOString(),
}, 500);
}
};
}