diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5647b80..ccc0817 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -160,4 +160,5 @@ jobs: INDEXING_RETRY_INTERVAL: 4000 INDEXING_MAX_RETRIES: 120 NODE_URL: 'http://127.0.0.1:8001' + AVOID_LOOP_RUN: true diff --git a/README.md b/README.md index 83e9a72..0cc9f36 100644 --- a/README.md +++ b/README.md @@ -63,36 +63,44 @@ export RPC='XXXX' export NODE_URL='XXXX' ``` -- Optional set metadataCache URL if you want to use a custom Aquarius version instead of the default one. This will not be used if you have set an Ocean Node url. +- Optional, set metadataCache URL if you want to use a custom Aquarius version instead of the default one. This will not be used if you have set an Ocean Node url. ``` export AQUARIUS_URL='XXXX' ``` -- Optional set Provider URL if you want to use a custom Provider version instead of the default one. This will not be used if you have set an Ocean Node url. +- Optional, set Provider URL if you want to use a custom Provider version instead of the default one. This will not be used if you have set an Ocean Node url. ``` export PROVIDER_URL='XXXX' ``` -- Optional set ADDRESS_FILE if you want to use a custom set of smart contract address +- Optional, set ADDRESS_FILE if you want to use a custom set of smart contract address ``` export ADDRESS_FILE='path-to-address-file' ``` -- Optional set INDEXING_MAX_RETRIES to the max number of retries when waiting for an asset to be indexed. Default is 100 retries max. +- Optional, set INDEXING_MAX_RETRIES to the max number of retries when waiting for an asset to be indexed. Default is 100 retries max. ``` export INDEXING_MAX_RETRIES='100' ``` -- Optional set INDEXING_RETRY_INTERVAL to the interval (in miliseconds) for each retry when waiting for an asset to be indexed. Default is 3 seconds. +- Optional, set INDEXING_RETRY_INTERVAL to the interval (in miliseconds) for each retry when waiting for an asset to be indexed. Default is 3 seconds. ``` export INDEXING_RETRY_INTERVAL='3000' ``` +- Optional, set AVOID_LOOP_RUN to 'true' to run each command and exit afterwards (usefull for CI test env and default behaviour). IF not set or set to 'false' the CLI will listen interactively for commands, until exit is manually forced + +``` +export AVOID_LOOP_RUN='true/false' +``` + + + ### Build the TypeScript code ``` diff --git a/src/cli.ts b/src/cli.ts index f7380b0..73ba0c1 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -56,7 +56,8 @@ export async function createCLI() { const assetDid = options.did || did; if (!assetDid) { console.error(chalk.red('DID is required')); - process.exit(1); + // process.exit(1); + return } const { signer, chainId } = await initializeSigner(); const commands = new Commands(signer, chainId); @@ -74,7 +75,8 @@ export async function createCLI() { const file = options.file || metadataFile; if (!file) { console.error(chalk.red('Metadata file is required')); - process.exit(1); + // process.exit(1); + return } const { signer, chainId } = await initializeSigner(); const commands = new Commands(signer, chainId); @@ -92,7 +94,8 @@ export async function createCLI() { const file = options.file || metadataFile; if (!file) { console.error(chalk.red('Metadata file is required')); - process.exit(1); + // process.exit(1); + return } const { signer, chainId } = await initializeSigner(); const commands = new Commands(signer, chainId); @@ -114,7 +117,8 @@ export async function createCLI() { const file = options.file || metadataFile; if (!dsDid || !file) { console.error(chalk.red('Dataset DID and metadata file are required')); - process.exit(1); + // process.exit(1); + return } const { signer, chainId } = await initializeSigner(); const commands = new Commands(signer, chainId); @@ -134,7 +138,8 @@ export async function createCLI() { const destFolder = options.folder || folder || '.'; if (!assetDid) { console.error(chalk.red('DID is required')); - process.exit(1); + // process.exit(1); + return } const { signer, chainId } = await initializeSigner(); const commands = new Commands(signer, chainId); @@ -155,7 +160,8 @@ export async function createCLI() { const aDid = options.algo || algoDid; if (!dsDid || !aDid) { console.error(chalk.red('Dataset DID and Algorithm DID are required')); - process.exit(1); + // process.exit(1); + return } const { signer, chainId } = await initializeSigner(); const commands = new Commands(signer, chainId); @@ -178,7 +184,8 @@ export async function createCLI() { const envId = options.env || computeEnvId; if (!dsDids || !aDid || !envId) { console.error(chalk.red('Missing required arguments')); - process.exit(1); + // process.exit(1); + return } const { signer, chainId } = await initializeSigner(); const commands = new Commands(signer, chainId); @@ -201,7 +208,8 @@ export async function createCLI() { const envId = options.env || computeEnvId; if (!dsDids || !aDid || !envId) { console.error(chalk.red('Missing required arguments')); - process.exit(1); + // process.exit(1); + return } const { signer, chainId } = await initializeSigner(); const commands = new Commands(signer, chainId); @@ -248,7 +256,8 @@ export async function createCLI() { const agrId = options.agreement || agreementId; if (!dsDid || !jId) { console.error(chalk.red('Dataset DID and Job ID are required')); - process.exit(1); + // process.exit(1); + return } const { signer, chainId } = await initializeSigner(); const commands = new Commands(signer, chainId); @@ -273,7 +282,8 @@ export async function createCLI() { const agrId = options.agreement || agreementId; if (!dsDid || !jId) { console.error(chalk.red('Dataset DID and Job ID are required')); - process.exit(1); + // process.exit(1); + return } const { signer, chainId } = await initializeSigner(); const commands = new Commands(signer, chainId); diff --git a/src/index.ts b/src/index.ts index 453760d..ff59a02 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,18 +1,110 @@ +import { sleep } from '@oceanprotocol/lib'; +import {createInterface} from "readline"; import { createCLI } from './cli.js'; +let program +let exit = false +const supportedCommands: string[] = [] +const initializeCommands: string[] = [] + +async function waitForCommands() { + const commandLine = await readLine("Enter command ('exit' | 'quit' or CTRL-C to terminate'):\n") + let command = null + if(commandLine === "quit" || commandLine === "exit" || commandLine === "\\q") { + exit = true + return + } + + const commandSplitted: string[] = commandLine.split(" ") + if(commandSplitted.length < 1) { + console.log("Invalid command, missing one or more arguments!") + return + } + + if(commandSplitted.length>=3) { + if(commandSplitted[0] === "npm" && commandSplitted[1] === "run" && commandSplitted[2] === "cli") { + commandSplitted.splice(0,3) + command = commandSplitted.join(" ") + } + } else if(commandSplitted.length >= 1) { + // just the command without npm run cli + command = commandSplitted[0] + } + + if(command && command.length > 0 && commandSplitted.length>0) { + const commandName = commandSplitted[0] + const args = initializeCommands.concat(commandSplitted) + + if(!supportedCommands.includes(commandName)) { + console.log("Invalid option: ", commandName) + return + } + + try { + await program.parseAsync(args); + }catch(error) { + console.log('Command error: ', error) + } + } + +} + +async function readLine(question: string): Promise { + + const readLine = createInterface({ + input: process.stdin, + output: process.stdout + }); + + let answer = "" + readLine.question(question, (it: string) => { + answer = it.trim() + readLine.close() + }) + while (answer == "") { await sleep(100) } + + return answer + +} async function main() { try { - const program = await createCLI(); + program = await createCLI(); + for(const command of program.commands) { + supportedCommands.push(command.name()) + supportedCommands.push(command.alias()) + } + console.log("Type 'exit' or 'quit' or 'CTRL-C' to terminate process") // Handle help command without initializing signer if (process.argv.includes('--help') || process.argv.includes('-h')) { program.outputHelp(); - process.exit(0); } - await program.parseAsync(process.argv); + const initialCommandLine:string [] = process.argv + if(process.env.AVOID_LOOP_RUN !== 'true') { + + do { + program.exitOverride(); + try { + if(initializeCommands.length === 0 && initialCommandLine.length > 2) { + initializeCommands.push(initialCommandLine[0]) // node + initializeCommands.push(initialCommandLine[1]) // file path + // just once + await program.parseAsync(initialCommandLine); + } + }catch(err) { + // silently ignore + } + await waitForCommands() + }while(!exit) + } else { + // one shot + await program.parseAsync(initialCommandLine); + } + } catch (error) { - console.error('Error:', error.message); + console.error('Program Error:', error.message); + exit = true process.exit(1); } }