UNPKG

@acurast/cli

Version:

A cli to interact with the Acurast Cloud.

212 lines 9.81 kB
import { Option } from 'commander'; import { readFileSync, statSync } from 'fs'; import { fileURLToPath } from 'url'; import { dirname, join } from 'path'; import { input } from '@inquirer/prompts'; import { red, green, yellowBright } from 'ansis'; import { loadAcurastConfig, deployProject } from '@acurast/sdk/deploy'; import { walletFromMnemonic, convertConfigToJob, jobToNumber, MIN_EXECUTION_DURATION_MS, } from '@acurast/sdk/chain'; import { AssignmentStrategyVariant, DeploymentStatus, } from '@acurast/sdk/types'; import { acurastColor } from '../util.js'; import * as ora from '../util/ora.js'; import { sendCode } from '../live-code.js'; import { addLiveCodeProcessor, readLiveCodeProcessors, } from '../services/storage.js'; import { parse } from '../util/parse-duration.js'; import { shortenString } from '../util/shortenString.js'; import { getEnv, getIpfsConfig, getRpcForNetwork } from '../config.js'; import { filelogger } from '../util/fileLogger.js'; import { LocalStorage } from '../util/LocalStorage.js'; export const addCommandLive = (program) => { program .command('live [project]') .description('Run the code in a live code environment on a remote processor') .addOption(new Option('-s, --setup', 'Set up a live code environment for the project.')) .action(async (project, options) => { if (options.setup) { const unparsedDuration = await input({ message: 'Enter the duration (eg. 1h, 2d):', validate: (input) => { const parsed = (parse(input) ?? 0).toString(); const value = Number(parsed); if (isNaN(value) || value <= 0) { return 'Please enter a valid number greater than 0'; } if (value < MIN_EXECUTION_DURATION_MS) { return 'Deployments must run for at least 1 minute'; } return true; }, }); const parsedDuration = (parse(unparsedDuration) ?? 0).toString(); const durationInMilliseconds = Number(parsedDuration); const numberOfProcessors = Number(await input({ message: 'On how many processors?:', validate: (input) => { const value = Number(input); if (isNaN(value) || value <= 0) { return 'Please enter a valid number greater than 0'; } if (isNaN(value) || value > 10) { return 'Please enter a number lower than 10'; } return true; }, })); const spinner = ora.default('Setting up live code environment'); spinner.start(); const jobId = await new Promise((resolve) => { const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const filePath = join(__dirname, '..', 'util', 'live-code-processor.js'); const config = { projectName: 'report', fileUrl: filePath, network: 'mainnet', onlyAttestedDevices: false, startAt: { msFromNow: 300000, }, assignmentStrategy: { type: AssignmentStrategyVariant.Single, }, execution: { type: 'onetime', maxExecutionTimeInMs: durationInMilliseconds, }, maxAllowedStartDelayInMs: 60000, usageLimit: { maxMemory: 0, maxNetworkRequests: 0, maxStorage: 0, }, numberOfReplicas: numberOfProcessors, requiredModules: [], minProcessorReputation: 0, maxCostPerExecution: 10000000000, includeEnvironmentVariables: [], processorWhitelist: [], }; const job = convertConfigToJob(config); const rpcEndpoint = getRpcForNetwork(config.network); filelogger.info(`Connecting to ${config.network} RPC: ${rpcEndpoint}`); (async () => { const wallet = await walletFromMnemonic(getEnv('ACURAST_MNEMONIC'), { name: 'AcurastCli', }); deployProject(config, job, { wallet, rpcEndpoint, ipfs: getIpfsConfig(), envVars: [], keyStore: new LocalStorage(), logger: filelogger, statusCallback: (status, data) => { if (status === DeploymentStatus.WaitingForMatch) { const jobId = data.jobIds[0]; resolve(jobId); spinner.succeed('Live code environment scheduled'); } }, }); })(); }); console.log('Please go to the follow link and copy the "Deployment Public Key" of the assigned processor, (it may take a few minutes until it\'s available)'); console.log(''); console.log(`https://hub.acurast.com/job-detail/acurast-${jobId[0].acurast}-${jobToNumber(jobId)}`); console.log(''); const processorPublicKey = await input({ message: 'Deployment Public Key', validate: (input) => { if (!input) { return 'Please enter a valid public key'; } if (!input.startsWith('0x')) { return 'Please enter a valid public key'; } return true; }, }); addLiveCodeProcessor({ publicKey: processorPublicKey, }); if (numberOfProcessors > 1) { console.log('To add more processors to the list, open ".acurast/live-code-processors.json" and add them manually.'); } console.log('Live code environment set up! Run `acurast live` to run your code on that processor.'); return process.exit(0); } let config; try { config = loadAcurastConfig({ project }); } catch (e) { console.log(e.message); return; } // console.log(config); if (!config) { throw new Error('No project found'); } // Get the file size in bytes const stats = statSync(config.fileUrl); // Check if file size is greater than 500KB const fileSizeInBytes = stats.size; const fileSizeInKB = fileSizeInBytes / 1024; if (fileSizeInKB > 500) { console.log('File is larger than 500kb. This is too big for the live-code environment.'); return; } const code = readFileSync(config.fileUrl, 'utf-8'); // TODO: Handle expired const liveCodeProcessors = readLiveCodeProcessors(); if (liveCodeProcessors.length === 0) { throw new Error('No processor found. Run `acurast live --setup`'); } const spinner = ora.default(`Running code on ${liveCodeProcessors.length} processor${liveCodeProcessors.length > 1 ? 's' : ''}`); spinner.start(); let terminations = 0; const registerTermination = () => { terminations++; if (terminations >= liveCodeProcessors.length) { spinner.stop(); process.exit(0); } }; liveCodeProcessors.forEach((processor) => { const timeout = setTimeout(() => { spinner.stop(); console.log(`${shortenString(processor.publicKey)} ${red('Error, no response from processor')}`); registerTermination(); spinner.start(); }, 5000); sendCode(processor.publicKey, code, (event) => { if (timeout) { clearTimeout(timeout); } spinner.stop(); // Sometimes messages arrive out of order, so we wait a little bit before exiting if (event.type === 'start') { console.log(`${shortenString(processor.publicKey)} ${yellowBright('Acknowledged, running code...')}`); } if (event.type === 'log') { if (Array.isArray(event.data)) { console.log(shortenString(processor.publicKey), acurastColor(`Log:`), ...event.data); } else { console.log(shortenString(processor.publicKey), acurastColor(`Log:`), event.data); } } if (event.type === 'success') { console.log(`${shortenString(processor.publicKey)} ${green('Success')}`); registerTermination(); } if (event.type === 'error') { console.log(`${shortenString(processor.publicKey)} ${red('Error')}: ${event.data}`); registerTermination(); } spinner.start(); }); }); }); }; //# sourceMappingURL=live.js.map