UNPKG

@acurast/cli

Version:

A cli to interact with the Acurast Cloud.

270 lines 13.6 kB
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; import { Option } from 'commander'; import { AcurastService } from '../acurast/env/acurastService.js'; import { getProjectEnvVars, RPC } from '../config.js'; import fs from 'fs'; import { readFilesInDeployFolder } from '../util/readFilesInDeployFolder.js'; import { toNumber } from '../util/jobToNumber.js'; import { getWallet } from '../util/getWallet.js'; import * as ora from '../util/ora.js'; import { getBalance } from '../util/getBalance.js'; import { ApiPromise, WsProvider } from '@polkadot/api'; import { setEnvVars } from '../util/setEnvVars.js'; import { ACURAST_DEPLOYMENTS_PATH } from '../constants.js'; import { getAcknowledgedProcessors } from '../acurast/jobAssignments.js'; import { MultiOrigin } from '../types.js'; import { editScript } from '../acurast/editScript.js'; import { transferEditor } from '../acurast/transferEditor.js'; export const addCommandDeployments = (program) => { const deploymentsCommand = program .command('deployments') .description('Manage deployments') .argument('[arg]', 'Deployment ID or command (ls/list)') .addOption(new Option('-e, --update-env-vars', 'Load the environment variables of a deployment and update them.')) .addOption(new Option('-c, --cleanup', 'Remove old, finished deployments. This will return any unused funds locked in the deployment back to the user.')) .action((arg, options) => __awaiter(void 0, void 0, void 0, function* () { var _a; const acurast = new AcurastService(); const wallet = yield getWallet(); if (arg === 'ls' || arg === 'list') { const spinner = ora.default('Loading deployments...'); spinner.start(); const jobs = yield acurast.getAllJobs(); const filteredJobs = jobs .filter((job) => job.id[0].acurast === wallet.address) .sort((a, b) => b.id[1] - a.id[1]); spinner.stop(); if (filteredJobs.length === 0) { console.log('No deployments found'); } else { console.log('You have the following deployments:'); const now = Date.now(); filteredJobs.forEach((job) => { const status = job.registration.schedule.startTime > now ? 'planned' : job.registration.schedule.endTime < now ? 'ended' : 'running'; console.log(`${job.id[1]} - ${status}`); }); } yield acurast.disconnect(); return; } const deploymentId = Number(arg); if (options.cleanup) { if (deploymentId) { const spinner = ora.default(`Cleaning up deployment ${deploymentId}...`); spinner.start(); yield acurast.deregisterJob(wallet, deploymentId); spinner.stop(); console.log(`Done`); } else { const spinner = ora.default(`Cleaning up old deployments...`); spinner.start(); const jobs = yield acurast.getAllJobs(); const now = Date.now(); const filteredJobs = jobs .filter((job) => job.id[0].acurast === wallet.address) .filter((job) => job.registration.schedule.endTime < now) .sort((a, b) => a.id[1] - b.id[1]); spinner.stop(); console.log(`Found ${filteredJobs.length} deployments to clean up`); const wsProvider = new WsProvider(RPC); const api = yield ApiPromise.create({ provider: wsProvider, noInitWarn: true, }); let balanceBefore = yield getBalance(wallet.address, api); for (const job of filteredJobs) { spinner.start(`Cleaning up deployment ${job.id[1]}...`); yield acurast.deregisterJob(wallet, job.id[1]); const balanceNew = yield getBalance(wallet.address, api); const diff = balanceNew - balanceBefore; spinner.succeed(`Deployment ${job.id[1]} cleaned up${diff > 0 ? `. cACU regained: ${diff}` : ``}`); balanceBefore = balanceNew; } yield api.disconnect(); spinner.stop(); } yield acurast.disconnect(); return; } if (!deploymentId || isNaN(deploymentId)) { console.log('Please provide a deployment ID'); yield acurast.disconnect(); return; } const deploymentFilename = yield readFilesInDeployFolder(`${toNumber(arg)}.json`); let job; if (deploymentFilename) { // File found, we can read details from file const deploymentFileData = JSON.parse(fs.readFileSync(`${ACURAST_DEPLOYMENTS_PATH}/${deploymentFilename}`, 'utf8')); const envVars = getProjectEnvVars(deploymentFileData.config); job = { id: deploymentFileData.deploymentId, registration: deploymentFileData.registration, // envInfo: deploymentFileData.envInfo, envVars, }; } else { console.log('Could not find deployment file.'); yield acurast.disconnect(); return; // const jobs = await acurast.getAllJobs() // job = jobs.find((job) => job.id[1] === Number(id)) } if (!job) { console.log('Deployment not found'); return; } if (options.updateEnvVars) { const spinner = ora.default(`Setting environment variables for deployment ${deploymentId}...`); spinner.start(); if (!job.envVars) { throw new Error('No environment variables found for deployment'); } const { hash } = yield setEnvVars(job); spinner.succeed(`${(_a = job.envVars) === null || _a === void 0 ? void 0 : _a.length} environment variables set`); spinner.stop(); console.log('Transaction ID:', hash); yield acurast.disconnect(); // If no file found, have user select the deployment config to be used // TODO: Introduce a flag in .env to store or not store encryption key. // TODO: Setting of .env var flag should be stored in deployment file } else { if (job.id) { console.log('Click here to open the deployment in your browser:'); console.log(`https://hub.acurast.com/job-detail/acurast-${job.id[0].acurast}-${deploymentId}`); } console.log('Deployment:', job); yield acurast.connect(); if (!acurast.api) { throw new Error('API not connected'); } const assignments = yield getAcknowledgedProcessors(acurast.api, job.id); console.log('Assignments:', JSON.stringify(assignments, null, 2)); acurast.disconnect(); return; } })); // Update subcommands const updateCommand = deploymentsCommand .command('update') .description('Update deployment properties'); // Update script subcommand updateCommand .command('script') .description('Update the script of a mutable deployment') .argument('<deployment-id>', 'The deployment/job ID in format "origin:address:number" (e.g., "Acurast:5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL:123456")') .argument('<script-ipfs>', 'IPFS hash of the new script (e.g., "ipfs://QmNewScriptHash")') .addOption(new Option('--dry-run', 'Preview the update without applying')) .addOption(new Option('--force', 'Skip confirmation prompts')) .action((deploymentId, scriptIpfs, options) => __awaiter(void 0, void 0, void 0, function* () { // Parse deploymentId in format "origin:address:number" const deploymentIdParts = deploymentId.split(':'); if (deploymentIdParts.length !== 3) { console.error('Invalid deployment ID format. Expected format: "origin:address:number" (e.g., "Acurast:5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL:123456")'); return; } const [origin, address, numberStr] = deploymentIdParts; const deploymentIdNumber = Number(numberStr); if (isNaN(deploymentIdNumber)) { console.error('Invalid deployment number. Please provide a valid number.'); return; } // Validate origin if (origin !== 'Acurast') { console.error('Invalid origin. Currently only "Acurast" is supported.'); return; } // Validate address format (AccountId32) if (!address.match(/^5[a-km-zA-HJ-NP-Z1-9]{47}$/)) { console.error('Invalid address format. Please provide a valid AccountId32 address.'); return; } // Validate IPFS hash format if (!scriptIpfs.startsWith('ipfs://')) { console.error('Invalid script format. Please provide an IPFS hash starting with "ipfs://"'); return; } if (options.dryRun) { console.log(`[DRY RUN] Would update script for deployment [${origin}, ${address}, ${deploymentIdNumber}] with: ${scriptIpfs}`); return; } const spinner = ora.default(`Updating script for deployment [${origin}, ${address}, ${deploymentIdNumber}]...`); spinner.start(); try { const txHash = yield editScript([MultiOrigin.Acurast, address, deploymentIdNumber], scriptIpfs); spinner.succeed(`Script updated successfully`); console.log('Transaction ID:', txHash); } catch (error) { spinner.fail(`Failed to update script: ${error}`); } })); // Update editor subcommand updateCommand .command('editor') .description('Transfer editor permissions for a mutable deployment') .argument('<deployment-id>', 'The deployment/job ID in format "origin:address:number" (e.g., "Acurast:5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL:123456")') .argument('<new-editor-address>', 'The AccountId32 address of the new editor') .addOption(new Option('--dry-run', 'Preview the transfer without executing')) .addOption(new Option('--force', 'Skip confirmation prompts')) .action((deploymentId, newEditorAddress, options) => __awaiter(void 0, void 0, void 0, function* () { // Parse deploymentId in format "origin:address:number" const deploymentIdParts = deploymentId.split(':'); if (deploymentIdParts.length !== 3) { console.error('Invalid deployment ID format. Expected format: "origin:address:number" (e.g., "Acurast:5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL:123456")'); return; } const [origin, address, numberStr] = deploymentIdParts; const deploymentIdNumber = Number(numberStr); if (isNaN(deploymentIdNumber)) { console.error('Invalid deployment number. Please provide a valid number.'); return; } // Validate origin if (origin !== 'Acurast') { console.error('Invalid origin. Currently only "Acurast" is supported.'); return; } // Validate address format (AccountId32) if (!address.match(/^5[a-km-zA-HJ-NP-Z1-9]{47}$/)) { console.error('Invalid address format. Please provide a valid AccountId32 address.'); return; } // Basic validation for AccountId32 format (starts with 5 and is 48 characters) if (!newEditorAddress.match(/^5[a-km-zA-HJ-NP-Z1-9]{47}$/)) { console.error('Invalid editor address. Please provide a valid AccountId32 address.'); return; } if (options.dryRun) { console.log(`[DRY RUN] Would transfer editor permissions for deployment [${origin}, ${address}, ${deploymentIdNumber}] to: ${newEditorAddress}`); return; } const spinner = ora.default(`Transferring editor permissions for deployment [${origin}, ${address}, ${deploymentIdNumber}]...`); spinner.start(); try { const txHash = yield transferEditor([MultiOrigin.Acurast, address, deploymentIdNumber], newEditorAddress); spinner.succeed(`Editor permissions transferred successfully`); console.log('Transaction ID:', txHash); } catch (error) { spinner.fail(`Failed to transfer editor permissions: ${error}`); } })); }; //# sourceMappingURL=deployments.js.map