File size: 2,564 Bytes
20f108c | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | import { existsSync, writeFileSync, mkdirSync, readdirSync } from "fs";
import { homedir } from "os";
import { resolve, join } from "path";
import { pathToFileURL } from "url";
import { AgentCapability } from "../agents/types.js";
const TOOLS_DIR = resolve(homedir(), ".config", "phantom", "tools");
function ensureToolsDir(): void {
if (!existsSync(TOOLS_DIR)) {
mkdirSync(TOOLS_DIR, { recursive: true });
}
}
export function saveDynamicTool(toolName: string, code: string): string {
ensureToolsDir();
const fileName = `dynamic_${toolName.toLowerCase().replace(/[^a-z0-9_]/g, "_")}.js`;
const filePath = join(TOOLS_DIR, fileName);
// Wrap the code if it doesn't already export execute
let fileContent = code;
if (!code.includes("export async function execute") && !code.includes("export function execute") && !code.includes("export default")) {
fileContent = `
export async function execute(input) {
${code}
}
`;
}
writeFileSync(filePath, fileContent, "utf-8");
return filePath;
}
export async function loadDynamicTool(filePath: string, toolName: string, description: string): Promise<AgentCapability> {
const fileUrl = pathToFileURL(filePath).href;
// Use a query parameter cache buster so Node.js can re-import the file if it changed
const module = await import(`${fileUrl}?t=${Date.now()}`);
const executeFn = module.execute || module.default;
if (typeof executeFn !== "function") {
throw new Error(`Dynamic tool ${toolName} does not export an 'execute' function.`);
}
return {
name: toolName,
description,
execute: async (input: string, agentCtx?: any) => {
try {
const result = await executeFn(input, agentCtx);
return String(result);
} catch (err: any) {
return `[Tool Error in ${toolName}]: ${err.message}`;
}
}
};
}
export async function loadAllDynamicTools(): Promise<{ name: string; description: string; filePath: string }[]> {
ensureToolsDir();
if (!existsSync(TOOLS_DIR)) return [];
const files = readdirSync(TOOLS_DIR);
const tools: { name: string; description: string; filePath: string }[] = [];
for (const file of files) {
if (file.startsWith("dynamic_") && file.endsWith(".js")) {
const filePath = join(TOOLS_DIR, file);
const name = file.replace("dynamic_", "").replace(".js", "");
// We can infer description from file or just use a generic one
tools.push({
name,
description: `Dynamically created tool: ${name}`,
filePath
});
}
}
return tools;
}
|