hardhat-forta
Version:
Forta Agent Hardhat Plugin
151 lines • 6.67 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.generateAgents = void 0;
const fs_1 = __importDefault(require("fs"));
const colors_1 = require("kleur/colors");
const node_fetch_1 = __importDefault(require("node-fetch"));
const path_1 = __importDefault(require("path"));
const prompts_1 = __importDefault(require("prompts"));
const shelljs_1 = __importDefault(require("shelljs"));
const forta_agent_1 = require("forta-agent");
const descriptions_1 = require("./descriptions");
const TEMPLATES_REPO_OWNER = "arbitraryexecution";
const TEMPLATES_REPO_NAME = "forta-bot-templates";
const TEMPLATES_REPO_COMMIT = "de5f1f272d906e3ceb8a1a2747c1600aa03a734e";
/**
* Retrieves the folders in the repository root.
* @returns The template directory subtree SHA.
*/
async function getTemplates() {
const treeUrl = `https://api.github.com/repos/${TEMPLATES_REPO_OWNER}/${TEMPLATES_REPO_NAME}/git/trees/${TEMPLATES_REPO_COMMIT}`;
const directories = (await (await (0, node_fetch_1.default)(treeUrl)).json());
const templateDirectories = directories.tree.filter((el) => {
return el.type === "tree";
});
if (!templateDirectories.length) {
throw new Error("Template not found");
}
return templateDirectories;
}
/**
* Retrieves data about the files inside the template directory.
* @param templateName - The name of the template, that should also be the
* name of one of the directories inside the `forta-bot-templates`
* repository root.
* @returns An array containing each of the files' relative path and download
* url.
*/
async function getTemplateFiles(template) {
const templateSubtreeUrl = `https://api.github.com/repos/${TEMPLATES_REPO_OWNER}/${TEMPLATES_REPO_NAME}/git/trees/${template.sha}?recursive=true`;
const templateSubtree = (await (await (0, node_fetch_1.default)(templateSubtreeUrl)).json());
return templateSubtree.tree
.filter((el) => el.type === "blob")
.map((el) => ({
path: el.path,
url: `https://raw.githubusercontent.com/${TEMPLATES_REPO_OWNER}/${TEMPLATES_REPO_NAME}/${TEMPLATES_REPO_COMMIT}/${template.path}/${el.path}`,
}));
}
/**
* Checks if the provided path has any files.
* @param path - Path to be checked.
*/
function pathHasFiles(pathStr) {
return fs_1.default.existsSync(pathStr) && shelljs_1.default.ls(pathStr).length;
}
/**
* Checks if destinationPath is empty or not and, if not, notifies the user
* and asks for confirmation.
* @param destinationPath - Path to be checked.
* @returns A boolean indicating whether the user agreed to write in the
* specified path.
*/
async function shouldWrite(destinationPath) {
if (pathHasFiles(destinationPath)) {
const response = await (0, prompts_1.default)({
type: "confirm",
name: "shouldContinue",
message: `The directory ${(0, colors_1.underline)(destinationPath)} is not empty and files may be overwritten. Continue?`,
});
if (!response.shouldContinue) {
return false;
}
}
return true;
}
/**
* Fetches an agents files based on its repository tree node.
* @param node - Repository tree node with information about the agent folder.
* @param destinationPath - Path in which the agent files should be written.
*/
async function fetchAgent(node, destinationPath) {
if (!(await shouldWrite(destinationPath))) {
console.log((0, colors_1.bold)(`Skipping agent download.`));
return;
}
console.log(`Initializing ${node.path} template...`);
const files = await getTemplateFiles(node);
await Promise.all(files.map(async (file) => {
const filePath = path_1.default.join(destinationPath, file.path);
shelljs_1.default.mkdir("-p", path_1.default.dirname(filePath));
const writeStream = fs_1.default.createWriteStream(filePath);
const res = await (0, node_fetch_1.default)(file.url);
return new Promise((resolve, reject) => {
var _a, _b;
(_a = res.body) === null || _a === void 0 ? void 0 : _a.pipe(writeStream);
(_b = res.body) === null || _b === void 0 ? void 0 : _b.on("error", reject);
writeStream.on("finish", resolve);
});
}));
console.log(`Running npm install...`);
shelljs_1.default.cd(destinationPath);
shelljs_1.default.exec("npm install");
console.log((0, colors_1.bold)((0, colors_1.blue)(`Agent successfully generated at ${(0, colors_1.underline)(destinationPath)}`)));
}
/**
* Generates an agent or more based on the available templates in the
* arbitraryexecution/forta-bot-templates github repository.
* @param destinationPath - The path in which the forta agent project(s) will
* be placed.
*/
async function generateAgents(destinationPath) {
const availableTemplates = await getTemplates();
const templatePrompt = await (0, prompts_1.default)({
type: "multiselect",
name: "agents",
message: "Templates:",
choices: availableTemplates.map((node) => ({
title: node.path,
value: node,
description: (0, descriptions_1.getDescription)(node.path),
})),
initial: 0,
});
if (templatePrompt.agents.length === 0) {
throw new Error("no templates selected");
}
const selectedMultipleAgents = templatePrompt.agents.length > 1;
if (!selectedMultipleAgents) {
// initialize the template in the root folder
await fetchAgent(templatePrompt.agents[0], destinationPath);
}
else {
// create separate folders for each template
for (const agent of templatePrompt.agents) {
await fetchAgent(agent, path_1.default.join(destinationPath, agent.path));
}
}
const diContainer = (0, forta_agent_1.configureContainer)();
const initKeystore = diContainer.resolve("initKeystore");
await initKeystore();
const initConfig = diContainer.resolve("initConfig");
await initConfig();
const initKeyfile = diContainer.resolve("initKeyfile");
await initKeyfile();
console.log(`You agree that your use is subject to the terms and conditions found at https://forta.org/legal`);
console.log((0, colors_1.bold)((0, colors_1.green)(`\n**Make sure to configure the template${selectedMultipleAgents ? 's' : ''} by following SETUP.md inside the agent folder${selectedMultipleAgents ? 's' : ''}!**`)));
}
exports.generateAgents = generateAgents;
//# sourceMappingURL=index.js.map