From 2c564981c9db4809d8f6efe598e9127340627a5f Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Tue, 1 Oct 2024 08:26:20 +0300 Subject: [PATCH 01/30] start work on docker c2d --- src/@types/C2D.ts | 199 -------------------- src/@types/C2D/C2D.ts | 107 +++++++++++ src/@types/C2D/C2D_OPFK8.ts | 94 +++++++++ src/@types/OceanNode.ts | 12 +- src/@types/commands.ts | 2 +- src/@types/index.ts | 2 +- src/components/c2d/compute_engine_base.ts | 4 +- src/components/c2d/compute_engine_docker.ts | 135 +++++++++++++ src/components/c2d/compute_engine_opf_k8.ts | 8 +- src/components/c2d/compute_engines.ts | 12 +- src/components/core/compute/environments.ts | 2 +- src/components/core/compute/getStatus.ts | 2 +- src/components/core/compute/startCompute.ts | 12 +- src/components/core/compute/utils.ts | 2 +- src/components/core/utils/feesHandler.ts | 2 +- src/components/database/index.ts | 57 ++++++ src/components/database/schemas.ts | 8 + src/components/database/sqlite.ts | 17 ++ src/components/httpRoutes/compute.ts | 6 +- src/test/integration/compute.test.ts | 2 +- src/utils/config.ts | 42 ++--- 21 files changed, 482 insertions(+), 245 deletions(-) delete mode 100644 src/@types/C2D.ts create mode 100644 src/@types/C2D/C2D.ts create mode 100644 src/@types/C2D/C2D_OPFK8.ts create mode 100644 src/components/c2d/compute_engine_docker.ts diff --git a/src/@types/C2D.ts b/src/@types/C2D.ts deleted file mode 100644 index f1297ca22..000000000 --- a/src/@types/C2D.ts +++ /dev/null @@ -1,199 +0,0 @@ -import type { MetadataAlgorithm } from './DDO/Metadata.js' - -export enum C2DClusterType { - // eslint-disable-next-line no-unused-vars - OPF_K8 = 0, - // eslint-disable-next-line no-unused-vars - NODE_LOCAL = 1 -} - -export interface C2DClusterInfo { - /** Type of cluster: K8, Node local, etc */ - type: C2DClusterType - /** Hash of cluster. hash(url) for remote, hash(nodeId) for local */ - hash: string - /** Connection URI */ - connection?: string -} - -export interface ComputeEnvironment { - id: string - cpuNumber: number - cpuType: string - gpuNumber: number - gpuType: string - ramGB: number - diskGB: number - priceMin: number - desc: string - currentJobs: number - maxJobs: number - consumerAddress: string - storageExpiry: number - maxJobDuration: number - lastSeen: number - chainId?: number - feeToken: string -} - -export interface ComputeEnvByChain { - [chainId: number]: ComputeEnvironment[] -} - -export type ComputeResultType = - | 'algorithmLog' - | 'output' - | 'configrationLog' - | 'publishLog' - -export interface ComputeResult { - filename: string - filesize: number - type: ComputeResultType - index?: number -} - -export interface ComputeJob { - owner: string - did?: string - jobId: string - dateCreated: string - dateFinished: string - status: number - statusText: string - results: ComputeResult[] - inputDID?: string[] - algoDID?: string - agreementId?: string - expireTimestamp: number -} - -export interface ComputeOutput { - publishAlgorithmLog?: boolean - publishOutput?: boolean - providerAddress?: string - providerUri?: string - metadataUri?: string - nodeUri?: string - owner?: string - secretStoreUri?: string - whitelist?: string[] -} - -export interface ComputeAsset { - url?: string - documentId: string - serviceId: string - transferTxId?: string - userdata?: { [key: string]: any } -} - -export interface ComputeAlgorithm { - documentId?: string - serviceId?: string - url?: string - meta?: MetadataAlgorithm - transferTxId?: string - algocustomdata?: { [key: string]: any } - userdata?: { [key: string]: any } -} - -/* The following are specific to OPF_k8 compute engine */ -export interface OPFK8ComputeStageInput { - index: number - id?: string - remote?: any - url?: string[] -} -export interface OPFK8ComputeStageAlgorithm { - id?: string - url?: string - remote?: any - rawcode?: string - container?: { - /** - * The command to execute, or script to run inside the Docker image. - * @type {string} - */ - entrypoint: string - - /** - * Name of the Docker image. - * @type {string} - */ - image: string - - /** - * Tag of the Docker image. - * @type {string} - */ - tag: string - } -} - -export interface OPFK8ComputeOutput { - // this is a copy of ComputeOutput, but they could diverge in the future - publishAlgorithmLog?: boolean - publishOutput?: boolean - providerAddress?: string - providerUri?: string - metadataUri?: string - nodeUri?: string - owner?: string - secretStoreUri?: string - whitelist?: string[] -} -export interface OPFK8ComputeStage { - index: number - input: OPFK8ComputeStageInput[] - algorithm: OPFK8ComputeStageAlgorithm - compute?: {} - output: OPFK8ComputeOutput -} - -export interface OPFK8ComputeWorkflow { - stages: OPFK8ComputeStage[] -} -export interface OPFK8ComputeStart { - workflow: OPFK8ComputeWorkflow - owner: string - agreementId: string - providerSignature: string - providerAddress: string - environment: string - validUntil: number - nonce: number - chainId: number -} - -export interface OPFK8ComputeStop { - jobId: string - owner: string - agreementId?: string - providerSignature: string // message=owner+jobId - providerAddress: string - nonce: number -} - -export interface OPFK8ComputeGetStatus { - agreementId?: string - jobId?: string - owner?: string - providerSignature: string // message=owner+jobId(if any) - providerAddress: string - nonce: number -} - -export interface OPFK8ComputeGetResult { - jobId: string - owner: string - index: number - providerSignature: string // message=owner+jobId - providerAddress: string - nonce: number -} - -export interface AlgoChecksums { - files: string - container: string -} diff --git a/src/@types/C2D/C2D.ts b/src/@types/C2D/C2D.ts new file mode 100644 index 000000000..128dc6900 --- /dev/null +++ b/src/@types/C2D/C2D.ts @@ -0,0 +1,107 @@ +import type { MetadataAlgorithm } from '../DDO/Metadata.js' + +export enum C2DClusterType { + // eslint-disable-next-line no-unused-vars + OPF_K8 = 0, + // eslint-disable-next-line no-unused-vars + NODE_LOCAL = 1, + // eslint-disable-next-line no-unused-vars + DOCKER = 2 +} + +export interface C2DClusterInfo { + /** Type of cluster: K8, Node local, etc */ + type: C2DClusterType + /** Hash of cluster. hash(url) for remote, hash(nodeId) for local */ + hash: string + /** Connection URI */ + connection?: any +} + +export interface ComputeEnvironment { + id: string + cpuNumber: number + cpuType?: string + gpuNumber?: number + gpuType?: string + ramGB: number + diskGB: number + priceMin: number + desc: string + currentJobs: number + maxJobs: number + consumerAddress: string + storageExpiry: number + maxJobDuration: number + lastSeen?: number + chainId?: number + feeToken: string + free: boolean +} + +export interface ComputeEnvByChain { + [chainId: number]: ComputeEnvironment[] +} + +export type ComputeResultType = + | 'algorithmLog' + | 'output' + | 'configrationLog' + | 'publishLog' + +export interface ComputeResult { + filename: string + filesize: number + type: ComputeResultType + index?: number +} + +export interface ComputeJob { + owner: string + did?: string + jobId: string + dateCreated: string + dateFinished: string + status: number + statusText: string + results: ComputeResult[] + inputDID?: string[] + algoDID?: string + agreementId?: string + expireTimestamp: number +} + +export interface ComputeOutput { + publishAlgorithmLog?: boolean + publishOutput?: boolean + providerAddress?: string + providerUri?: string + metadataUri?: string + nodeUri?: string + owner?: string + secretStoreUri?: string + whitelist?: string[] +} + +export interface ComputeAsset { + url?: string + documentId: string + serviceId: string + transferTxId?: string + userdata?: { [key: string]: any } +} + +export interface ComputeAlgorithm { + documentId?: string + serviceId?: string + url?: string + meta?: MetadataAlgorithm + transferTxId?: string + algocustomdata?: { [key: string]: any } + userdata?: { [key: string]: any } +} + +export interface AlgoChecksums { + files: string + container: string +} diff --git a/src/@types/C2D/C2D_OPFK8.ts b/src/@types/C2D/C2D_OPFK8.ts new file mode 100644 index 000000000..9c894bd38 --- /dev/null +++ b/src/@types/C2D/C2D_OPFK8.ts @@ -0,0 +1,94 @@ +/* The following are specific to OPF_k8 compute engine */ +export interface OPFK8ComputeStageInput { + index: number + id?: string + remote?: any + url?: string[] +} +export interface OPFK8ComputeStageAlgorithm { + id?: string + url?: string + remote?: any + rawcode?: string + container?: { + /** + * The command to execute, or script to run inside the Docker image. + * @type {string} + */ + entrypoint: string + + /** + * Name of the Docker image. + * @type {string} + */ + image: string + + /** + * Tag of the Docker image. + * @type {string} + */ + tag: string + } +} + +export interface OPFK8ComputeOutput { + // this is a copy of ComputeOutput, but they could diverge in the future + publishAlgorithmLog?: boolean + publishOutput?: boolean + providerAddress?: string + providerUri?: string + metadataUri?: string + nodeUri?: string + owner?: string + secretStoreUri?: string + whitelist?: string[] +} +export interface OPFK8ComputeStage { + index: number + input: OPFK8ComputeStageInput[] + algorithm: OPFK8ComputeStageAlgorithm + compute?: {} + output: OPFK8ComputeOutput +} + +export interface OPFK8ComputeWorkflow { + stages: OPFK8ComputeStage[] +} +export interface OPFK8ComputeStart { + workflow: OPFK8ComputeWorkflow + owner: string + agreementId: string + providerSignature: string + providerAddress: string + environment: string + validUntil: number + nonce: number + chainId: number +} + +export interface OPFK8ComputeStop { + jobId: string + owner: string + agreementId?: string + providerSignature: string // message=owner+jobId + providerAddress: string + nonce: number +} + +export interface OPFK8ComputeGetStatus { + agreementId?: string + jobId?: string + owner?: string + providerSignature: string // message=owner+jobId(if any) + providerAddress: string + nonce: number +} + +export interface OPFK8ComputeGetResult { + jobId: string + owner: string + index: number + providerSignature: string // message=owner+jobId + providerAddress: string + nonce: number +} diff --git a/src/@types/OceanNode.ts b/src/@types/OceanNode.ts index a5aa4106a..b9ea96bba 100644 --- a/src/@types/OceanNode.ts +++ b/src/@types/OceanNode.ts @@ -1,6 +1,6 @@ import { Stream } from 'stream' import { RPCS } from './blockchain' -import { C2DClusterInfo } from './C2D' +import { C2DClusterInfo } from './C2D/C2D' import { FeeStrategy } from './Fees' import { Schema } from '../components/database/schemas' @@ -58,15 +58,6 @@ export interface OceanNodeP2PConfig { autoDialInterval: number } -export interface OceanNodeDockerConfig { - socketPath?: string - protocol?: string - host?: string - port?: number - caPath?: string - certPath?: string - keyPath?: string -} export interface OceanNodeConfig { authorizedDecrypters: string[] allowedValidators: string[] @@ -83,7 +74,6 @@ export interface OceanNodeConfig { indexingNetworks?: RPCS c2dClusters: C2DClusterInfo[] c2dNodeUri: string - dockerConfig?: OceanNodeDockerConfig accountPurgatoryUrl: string assetPurgatoryUrl: string allowedAdmins?: string[] diff --git a/src/@types/commands.ts b/src/@types/commands.ts index 32117af5f..aac2c6dfe 100644 --- a/src/@types/commands.ts +++ b/src/@types/commands.ts @@ -1,7 +1,7 @@ import { ValidateParams } from '../components/httpRoutes/validateCommands.js' import { DDO } from './DDO/DDO' import { P2PCommandResponse } from './OceanNode' -import type { ComputeAsset, ComputeAlgorithm, ComputeOutput } from './C2D' +import type { ComputeAsset, ComputeAlgorithm, ComputeOutput } from './C2D/C2D.js' import { ArweaveFileObject, FileObjectType, diff --git a/src/@types/index.ts b/src/@types/index.ts index 8a5d367bc..8ac35eeb1 100644 --- a/src/@types/index.ts +++ b/src/@types/index.ts @@ -1,3 +1,3 @@ export * from './OceanNode' -export * from './C2D' +export * from './C2D/C2D' export * from './Typesense' diff --git a/src/components/c2d/compute_engine_base.ts b/src/components/c2d/compute_engine_base.ts index ef147eea5..6b8569b80 100644 --- a/src/components/c2d/compute_engine_base.ts +++ b/src/components/c2d/compute_engine_base.ts @@ -6,8 +6,8 @@ import type { ComputeAsset, ComputeJob, ComputeOutput -} from '../../@types/C2D.js' -import { C2DClusterType } from '../../@types/C2D.js' +} from '../../@types/C2D/C2D.js' +import { C2DClusterType } from '../../@types/C2D/C2D.js' export class C2DEngine { private clusterConfig: C2DClusterInfo diff --git a/src/components/c2d/compute_engine_docker.ts b/src/components/c2d/compute_engine_docker.ts new file mode 100644 index 000000000..d04bb8c9d --- /dev/null +++ b/src/components/c2d/compute_engine_docker.ts @@ -0,0 +1,135 @@ +import { Readable } from 'stream' +import { C2DClusterType } from '../../@types/C2D/C2D.js' +import type { + C2DClusterInfo, + ComputeEnvironment, + ComputeAlgorithm, + ComputeAsset, + ComputeJob, + ComputeOutput +} from '../../@types/C2D/C2D.js' +import { ZeroAddress } from 'ethers' +// import { getProviderFeeToken } from '../../components/core/utils/feesHandler.js' +import { C2DEngine } from './compute_engine_base.js' +import { create256Hash } from '../../utils/crypt.js' +export class C2DEngineDocker extends C2DEngine { + // eslint-disable-next-line no-useless-constructor + private envs: ComputeEnvironment[] = [] + public constructor(clusterConfig: C2DClusterInfo) { + super(clusterConfig) + // TO DO C2D - create envs + } + + // eslint-disable-next-line require-await + public override async getComputeEnvironments( + chainId: number + ): Promise { + /** + * Returns all cluster's compute environments for a specific chainId. Env's id already contains the cluster hash + */ + + return this.envs + } + + // eslint-disable-next-line require-await + public override async startComputeJob( + assets: ComputeAsset[], + algorithm: ComputeAlgorithm, + output: ComputeOutput, + owner: string, + environment: string, + validUntil: number, + chainId: number, + agreementId: string + ): Promise { + return null + } + + // eslint-disable-next-line require-await + public override async stopComputeJob( + jobId: string, + owner: string, + agreementId?: string + ): Promise { + return null + } + + // eslint-disable-next-line require-await + public override async getComputeJobStatus( + consumerAddress?: string, + agreementId?: string, + jobId?: string + ): Promise { + return null + } + + // eslint-disable-next-line require-await + public override async getComputeJobResult( + consumerAddress: string, + jobId: string, + index: number + ): Promise { + return null + } + + private async InternalLoop() { + // this is the internal loop of docker engine + // has to : + // - monitor running containers and stop them if over limits + // - monitor disc space and clean up + } +} + +// this uses the docker engine, but exposes only one env, the free one +export class C2DEngineDockerFree extends C2DEngineDocker { + public constructor(clusterConfig: C2DClusterInfo) { + // we remove envs, cause we have our own + const owerwrite = { + type: C2DClusterType.DOCKER, + hash: create256Hash('free' + clusterConfig.hash), + connection: { + socketPath: clusterConfig.connection.socketPath, + protocol: clusterConfig.connection.protocol, + host: clusterConfig.connection.host, + port: clusterConfig.connection.port, + caPath: clusterConfig.connection.caPath, + certPath: clusterConfig.connection.certPath, + keyPath: clusterConfig.connection.keyPath + } + } + super(owerwrite) + } + + // eslint-disable-next-line require-await + public override async getComputeEnvironments( + chainId: number + ): Promise { + /** + * Returns all cluster's compute environments for a specific chainId. Env's id already contains the cluster hash + */ + // TO DO C2D - fill consts below + const cpuType = '' + const currentJobs = 0 + const consumerAddress = '' + const envs: ComputeEnvironment[] = [ + { + id: `${this.getC2DConfig().hash}-free`, + cpuNumber: 1, + cpuType, + gpuNumber: 0, + ramGB: 1, + diskGB: 1, + priceMin: 0, + desc: 'Free', + currentJobs, + maxJobs: 1, + consumerAddress, + storageExpiry: 600, + maxJobDuration: 60, + feeToken: ZeroAddress, + free: true + } + ] + return envs + } +} diff --git a/src/components/c2d/compute_engine_opf_k8.ts b/src/components/c2d/compute_engine_opf_k8.ts index 9956cdcf2..b4cac240d 100644 --- a/src/components/c2d/compute_engine_opf_k8.ts +++ b/src/components/c2d/compute_engine_opf_k8.ts @@ -5,7 +5,9 @@ import type { ComputeAlgorithm, ComputeAsset, ComputeJob, - ComputeOutput, + ComputeOutput +} from '../../@types/C2D/C2D.js' +import type { OPFK8ComputeStage, OPFK8ComputeStageAlgorithm, OPFK8ComputeStageInput, @@ -14,7 +16,7 @@ import type { OPFK8ComputeStop, OPFK8ComputeGetStatus, OPFK8ComputeGetResult -} from '../../@types/C2D.js' +} from '../../@types/C2D/C2D_OPFK8.js' import { sign } from '../core/utils/nonceHandler.js' import axios from 'axios' import { getConfiguration } from '../../utils/config.js' @@ -45,6 +47,8 @@ export class C2DEngineOPFK8 extends C2DEngine { // we need to add hash to each env id for (const [index, val] of data.entries()) { data[index].id = `${clusterHash}-${val.id}` + // k8 envs are not free envs + data[index].free = false if (!data[index].feeToken || data[index].feeToken?.toLowerCase() === ZeroAddress) data[index].feeToken = await getProviderFeeToken(chainId) } diff --git a/src/components/c2d/compute_engines.ts b/src/components/c2d/compute_engines.ts index 9991ac9e4..e420abff4 100644 --- a/src/components/c2d/compute_engines.ts +++ b/src/components/c2d/compute_engines.ts @@ -1,18 +1,28 @@ -import { C2DClusterType, ComputeEnvironment } from '../../@types/C2D.js' +import { C2DClusterType, ComputeEnvironment } from '../../@types/C2D/C2D.js' import { C2DEngine } from './compute_engine_base.js' import { C2DEngineOPFK8 } from './compute_engine_opf_k8.js' +import { C2DEngineDocker, C2DEngineDockerFree } from './compute_engine_docker.js' import { OceanNodeConfig } from '../../@types/OceanNode.js' export class C2DEngines { public engines: C2DEngine[] public constructor(config: OceanNodeConfig) { // let's see what engines do we have and initialize them one by one + // for docker, we need to add the "free" + let haveFree = false if (config && config.c2dClusters) { this.engines = [] for (const cluster of config.c2dClusters) { if (cluster.type === C2DClusterType.OPF_K8) { this.engines.push(new C2DEngineOPFK8(cluster)) } + if (cluster.type === C2DClusterType.DOCKER) { + this.engines.push(new C2DEngineDocker(cluster)) + if (!haveFree) { + this.engines.push(new C2DEngineDockerFree(cluster)) + haveFree = true + } + } } } } diff --git a/src/components/core/compute/environments.ts b/src/components/core/compute/environments.ts index 8561d66c4..a07047e45 100644 --- a/src/components/core/compute/environments.ts +++ b/src/components/core/compute/environments.ts @@ -1,6 +1,6 @@ import { Readable } from 'stream' import { P2PCommandResponse } from '../../../@types/index.js' -import { ComputeEnvByChain } from '../../../@types/C2D.js' +import { ComputeEnvByChain } from '../../../@types/C2D/C2D.js' import { CORE_LOGGER } from '../../../utils/logging/common.js' import { Handler } from '../handler/handler.js' import { ComputeGetEnvironmentsCommand } from '../../../@types/commands.js' diff --git a/src/components/core/compute/getStatus.ts b/src/components/core/compute/getStatus.ts index 23b330298..2bd96bd33 100644 --- a/src/components/core/compute/getStatus.ts +++ b/src/components/core/compute/getStatus.ts @@ -1,6 +1,6 @@ import { Readable } from 'stream' import { P2PCommandResponse } from '../../../@types/index.js' -import { ComputeJob } from '../../../@types/C2D.js' +import { ComputeJob } from '../../../@types/C2D/C2D.js' import { CORE_LOGGER } from '../../../utils/logging/common.js' import { Handler } from '../handler/handler.js' import { ComputeGetStatusCommand } from '../../../@types/commands.js' diff --git a/src/components/core/compute/startCompute.ts b/src/components/core/compute/startCompute.ts index 7d10ba894..b0c822449 100644 --- a/src/components/core/compute/startCompute.ts +++ b/src/components/core/compute/startCompute.ts @@ -1,6 +1,6 @@ import { Readable } from 'stream' import { P2PCommandResponse } from '../../../@types/index.js' -import { ComputeAsset } from '../../../@types/C2D.js' +import { ComputeAsset } from '../../../@types/C2D/C2D.js' import { CORE_LOGGER } from '../../../utils/logging/common.js' import { Handler } from '../handler/handler.js' import { ComputeStartCommand } from '../../../@types/commands.js' @@ -219,6 +219,16 @@ export class ComputeStartHandler extends Handler { result.chainId = ddo.chainId const env = await engine.getComputeEnvironment(ddo.chainId, task.environment) + if (env.free) { + const error = `Free Jobs cannot be started here, use startFreeCompute` + return { + stream: null, + status: { + httpStatus: 500, + error + } + } + } if (!('transferTxId' in elem) || !elem.transferTxId) { const error = `Missing transferTxId for DDO ${elem.documentId}` return { diff --git a/src/components/core/compute/utils.ts b/src/components/core/compute/utils.ts index f4e60b95b..b5165046c 100644 --- a/src/components/core/compute/utils.ts +++ b/src/components/core/compute/utils.ts @@ -1,5 +1,5 @@ import { OceanNode } from '../../../OceanNode.js' -import { AlgoChecksums } from '../../../@types/C2D.js' +import { AlgoChecksums } from '../../../@types/C2D/C2D.js' import { ArweaveFileObject, IpfsFileObject, diff --git a/src/components/core/utils/feesHandler.ts b/src/components/core/utils/feesHandler.ts index 39ffa4378..ba5a4f188 100644 --- a/src/components/core/utils/feesHandler.ts +++ b/src/components/core/utils/feesHandler.ts @@ -1,4 +1,4 @@ -import type { ComputeEnvironment } from '../../../@types/C2D.js' +import type { ComputeEnvironment } from '../../../@types/C2D/C2D.js' import { JsonRpcApiProvider, ethers, diff --git a/src/components/database/index.ts b/src/components/database/index.ts index 65aef7bc9..c7b4694f6 100644 --- a/src/components/database/index.ts +++ b/src/components/database/index.ts @@ -1057,6 +1057,61 @@ export class LogDatabase { } } +export class C2DDatabase { + private provider: Typesense | SQLiteProvider + + constructor( + private config: OceanNodeDBConfig, + private schema: Schema + ) { + return (async (): Promise => { + if (this.config.url && URLUtils.isValidUrl(this.config.url)) { + try { + this.provider = new Typesense({ + ...convertTypesenseConfig(this.config.url), + logger: DATABASE_LOGGER + }) + await this.provider.collections(this.schema.name).retrieve() + } catch (error) { + if (error instanceof TypesenseError && error.httpStatus === 404) { + await (this.provider as Typesense).collections().create(this.schema) + } + } + } else { + // Fall back to SQLite + DATABASE_LOGGER.logMessageWithEmoji( + 'C2DDatabase: Typesense not available, falling back to SQLite', + true, + GENERIC_EMOJIS.EMOJI_CROSS_MARK, + LOG_LEVELS_STR.LEVEL_WARN + ) + + // Ensure the directory exists before instantiating SQLiteProvider + const dbDir = path.dirname('databases/c2dDatabase.sqlite') + if (!fs.existsSync(dbDir)) { + fs.mkdirSync(dbDir, { recursive: true }) + } + this.provider = new SQLiteProvider('databases/c2dDatabase.sqlite') + await this.provider.createC2DTables() + } + + return this + })() as unknown as C2DDatabase + } + + async newJob(job: any) { + // TO DO C2D + } + + async updateJob(job: any) { + // TO DO C2D + } + + async getRunningJobs(job: any) { + // TO DO C2D + } +} + export class Database { ddo: DdoDatabase nonce: NonceDatabase @@ -1064,6 +1119,7 @@ export class Database { logs: LogDatabase order: OrderDatabase ddoState: DdoStateDatabase + c2d: C2DDatabase constructor(private config: OceanNodeDBConfig) { // add this DB transport too @@ -1078,6 +1134,7 @@ export class Database { } return (async (): Promise => { this.nonce = await new NonceDatabase(this.config, schemas.nonceSchemas) + this.c2d = await new C2DDatabase(this.config, schemas.c2dSchemas) if (this.config.url && URLUtils.isValidUrl(this.config.url)) { this.ddo = await new DdoDatabase(this.config, schemas.ddoSchemas) this.indexer = await new IndexerDatabase(this.config, schemas.indexerSchemas) diff --git a/src/components/database/schemas.ts b/src/components/database/schemas.ts index 9e8aacf47..bfa673c77 100644 --- a/src/components/database/schemas.ts +++ b/src/components/database/schemas.ts @@ -48,6 +48,7 @@ export type Schema = TypesenseCollectionCreateSchema export type Schemas = { ddoSchemas: Schema[] nonceSchemas: Schema + c2dSchemas: Schema indexerSchemas: Schema logSchemas: Schema orderSchema: Schema @@ -61,6 +62,13 @@ export const schemas: Schemas = { enable_nested_fields: true, fields: [{ name: 'nonce', type: 'int64' }] }, + c2dSchemas: { + name: 'c2djobs', + enable_nested_fields: true, + fields: [ + // TO DO C2D + ] + }, indexerSchemas: { name: 'indexer', enable_nested_fields: true, diff --git a/src/components/database/sqlite.ts b/src/components/database/sqlite.ts index d1d82ac49..ef5170098 100644 --- a/src/components/database/sqlite.ts +++ b/src/components/database/sqlite.ts @@ -33,6 +33,23 @@ export class SQLiteProvider implements DatabaseProvider { }) } + // eslint-disable-next-line require-await + async createC2DTables() { + // TO DO C2D + const createTableSQL = ` + CREATE TABLE IF NOT EXISTS ${this.schema.name} ( + id TEXT PRIMARY KEY, + nonce INTEGER + ); + ` + return new Promise((resolve, reject) => { + this.db.run(createTableSQL, (err) => { + if (err) reject(err) + else resolve() + }) + }) + } + // eslint-disable-next-line require-await async create(address: string, nonce: number) { const insertSQL = ` diff --git a/src/components/httpRoutes/compute.ts b/src/components/httpRoutes/compute.ts index c127981d7..ef1714448 100644 --- a/src/components/httpRoutes/compute.ts +++ b/src/components/httpRoutes/compute.ts @@ -7,7 +7,11 @@ import { ComputeGetResultHandler, ComputeInitializeHandler } from '../core/compute/index.js' -import type { ComputeAlgorithm, ComputeAsset, ComputeOutput } from '../../@types/C2D.js' +import type { + ComputeAlgorithm, + ComputeAsset, + ComputeOutput +} from '../../@types/C2D/C2D.js' import type { ComputeStartCommand, ComputeStopCommand, diff --git a/src/test/integration/compute.test.ts b/src/test/integration/compute.test.ts index 4524a788e..d108edb13 100644 --- a/src/test/integration/compute.test.ts +++ b/src/test/integration/compute.test.ts @@ -16,7 +16,7 @@ import type { ComputeAsset, ComputeAlgorithm, ComputeEnvironment -} from '../../@types/C2D.js' +} from '../../@types/C2D/C2D.js' import { ENVIRONMENT_VARIABLES, EVENTS, diff --git a/src/utils/config.ts b/src/utils/config.ts index 59b269e68..b1c0f986f 100644 --- a/src/utils/config.ts +++ b/src/utils/config.ts @@ -1,11 +1,6 @@ -import type { - DenyList, - OceanNodeConfig, - OceanNodeKeys, - OceanNodeDockerConfig -} from '../@types/OceanNode' -import type { C2DClusterInfo } from '../@types/C2D.js' -import { C2DClusterType } from '../@types/C2D.js' +import type { DenyList, OceanNodeConfig, OceanNodeKeys } from '../@types/OceanNode' +import type { C2DClusterInfo } from '../@types/C2D/C2D.js' +import { C2DClusterType } from '../@types/C2D/C2D.js' import { createFromPrivKey } from '@libp2p/peer-id-factory' import { keys } from '@libp2p/crypto' import { @@ -305,18 +300,6 @@ function getOceanNodeFees(supportedNetworks: RPCS, isStartup?: boolean): FeeStra } } -function getC2DDockerConfig(isStartup?: boolean): OceanNodeDockerConfig { - const config = { - socketPath: getEnvValue(process.env.DOCKER_SOCKET_PATH, null), - protocol: getEnvValue(process.env.DOCKER_PROTOCOL, null), - host: getEnvValue(process.env.DOCKER_HOST, null), - port: getIntEnvValue(process.env.DOCKER_PORT, 0), - caPath: getEnvValue(process.env.DOCKER_CA_PATH, null), - certPath: getEnvValue(process.env.DOCKER_CERT_PATH, null), - keyPath: getEnvValue(process.env.DOCKER_KEY_PATH, null) - } - return config -} // get C2D environments function getC2DClusterEnvironment(isStartup?: boolean): C2DClusterInfo[] { const clusters: C2DClusterInfo[] = [] @@ -343,6 +326,24 @@ function getC2DClusterEnvironment(isStartup?: boolean): C2DClusterInfo[] { ) } } + // docker clusters + const dockerConfig = { + socketPath: getEnvValue(process.env.DOCKER_SOCKET_PATH, null), + protocol: getEnvValue(process.env.DOCKER_PROTOCOL, null), + host: getEnvValue(process.env.DOCKER_HOST, null), + port: getIntEnvValue(process.env.DOCKER_PORT, 0), + caPath: getEnvValue(process.env.DOCKER_CA_PATH, null), + certPath: getEnvValue(process.env.DOCKER_CERT_PATH, null), + keyPath: getEnvValue(process.env.DOCKER_KEY_PATH, null), + environments: getEnvValue(process.env.DOCKER_COMPUTE_ENVIRONMENTS, null) + } + if (dockerConfig.socketPath || dockerConfig.host) { + clusters.push({ + connection: dockerConfig, + hash: create256Hash(JSON.stringify(dockerConfig)), + type: C2DClusterType.DOCKER + }) + } return clusters } @@ -587,7 +588,6 @@ async function getEnvConfig(isStartup?: boolean): Promise { indexingNetworks, feeStrategy: getOceanNodeFees(supportedNetworks, isStartup), c2dClusters: getC2DClusterEnvironment(isStartup), - dockerConfig: getC2DDockerConfig(isStartup), c2dNodeUri: getEnvValue(process.env.C2D_NODE_URI, ''), accountPurgatoryUrl: getEnvValue(process.env.ACCOUNT_PURGATORY_URL, ''), assetPurgatoryUrl: getEnvValue(process.env.ASSET_PURGATORY_URL, ''), From bfcfc214eb633d90387db8f1f939fe89b30eac95 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Tue, 1 Oct 2024 16:10:20 +0300 Subject: [PATCH 02/30] more on db --- src/@types/C2D/C2D.ts | 9 +++++ src/components/database/index.ts | 18 ++++++---- src/components/database/sqlite.ts | 17 ---------- src/components/database/sqliteCompute.ts | 42 ++++++++++++++++++++++++ 4 files changed, 63 insertions(+), 23 deletions(-) create mode 100644 src/components/database/sqliteCompute.ts diff --git a/src/@types/C2D/C2D.ts b/src/@types/C2D/C2D.ts index 128dc6900..aba4b080e 100644 --- a/src/@types/C2D/C2D.ts +++ b/src/@types/C2D/C2D.ts @@ -69,6 +69,7 @@ export interface ComputeJob { algoDID?: string agreementId?: string expireTimestamp: number + environment?: string } export interface ComputeOutput { @@ -105,3 +106,11 @@ export interface AlgoChecksums { files: string container: string } + +export interface DBComputeJob extends ComputeJob { + configlogURL: string + publishlogURL: string + algologURL: string + outputsURL: string + stopRequested: boolean +} diff --git a/src/components/database/index.ts b/src/components/database/index.ts index c7b4694f6..ecef93dd4 100644 --- a/src/components/database/index.ts +++ b/src/components/database/index.ts @@ -2,6 +2,7 @@ import { OceanNodeDBConfig } from '../../@types/OceanNode.js' import { convertTypesenseConfig, Typesense, TypesenseError } from './typesense.js' import { Schema, schemas } from './schemas.js' import { TypesenseSearchParams } from '../../@types/index.js' +import type { DBComputeJob } from '../../@types/C2D/C2D.js' import { LOG_LEVELS_STR, configureCustomDBTransport, @@ -12,6 +13,7 @@ import { DATABASE_LOGGER } from '../../utils/logging/common.js' import { validateObject } from '../core/utils/validateDdoHandler.js' import { ENVIRONMENT_VARIABLES, TYPESENSE_HITS_CAP } from '../../utils/constants.js' import { SQLiteProvider } from './sqlite.js' +import { SQLiteCompute } from './sqliteCompute.js' import { URLUtils } from '../../utils/url.js' import fs from 'fs' import path from 'path' @@ -1058,7 +1060,7 @@ export class LogDatabase { } export class C2DDatabase { - private provider: Typesense | SQLiteProvider + private provider: Typesense | SQLiteCompute constructor( private config: OceanNodeDBConfig, @@ -1091,23 +1093,27 @@ export class C2DDatabase { if (!fs.existsSync(dbDir)) { fs.mkdirSync(dbDir, { recursive: true }) } - this.provider = new SQLiteProvider('databases/c2dDatabase.sqlite') - await this.provider.createC2DTables() + this.provider = new SQLiteCompute('databases/c2dDatabase.sqlite') + await this.provider.createTable() } return this })() as unknown as C2DDatabase } - async newJob(job: any) { + async newJob(job: DBComputeJob): Promise { + // TO DO C2D + } + + async getJob(jobId: string): Promise { // TO DO C2D } - async updateJob(job: any) { + async updateJob(job: DBComputeJob) { // TO DO C2D } - async getRunningJobs(job: any) { + async getRunningJobs(engine?: string, environment?: string): Promise { // TO DO C2D } } diff --git a/src/components/database/sqlite.ts b/src/components/database/sqlite.ts index ef5170098..d1d82ac49 100644 --- a/src/components/database/sqlite.ts +++ b/src/components/database/sqlite.ts @@ -33,23 +33,6 @@ export class SQLiteProvider implements DatabaseProvider { }) } - // eslint-disable-next-line require-await - async createC2DTables() { - // TO DO C2D - const createTableSQL = ` - CREATE TABLE IF NOT EXISTS ${this.schema.name} ( - id TEXT PRIMARY KEY, - nonce INTEGER - ); - ` - return new Promise((resolve, reject) => { - this.db.run(createTableSQL, (err) => { - if (err) reject(err) - else resolve() - }) - }) - } - // eslint-disable-next-line require-await async create(address: string, nonce: number) { const insertSQL = ` diff --git a/src/components/database/sqliteCompute.ts b/src/components/database/sqliteCompute.ts new file mode 100644 index 000000000..898688bed --- /dev/null +++ b/src/components/database/sqliteCompute.ts @@ -0,0 +1,42 @@ +import { schemas, Schema } from './schemas.js' +import type { DBComputeJob } from '../../@types/C2D/C2D.js' +import sqlite3 from 'sqlite3' + +interface ComputeDatabaseProvider { + newJob(job: DBComputeJob): Promise + getJob(jobId: string): Promise + updateJob(job: DBComputeJob): void + getRunningJobs(engine?: string, environment?: string): Promise +} + +export class SQLiteCompute implements ComputeDatabaseProvider { + private db: sqlite3.Database + private schema: Schema + + constructor(private dbFilePath: string) { + this.db = new sqlite3.Database(dbFilePath) + this.schema = schemas.nonceSchemas + } + + // eslint-disable-next-line require-await + async createTable() { + // TO DO C2D + } + + // eslint-disable-next-line require-await + async newJob(job: DBComputeJob): Promise { + // TO DO C2D + } + + async getJob(jobId: string): Promise { + // TO DO C2D + } + + async updateJob(job: DBComputeJob) { + // TO DO C2D + } + + async getRunningJobs(engine?: string, environment?: string): Promise { + // TO DO C2D + } +} From 30fdbf26e63420a633a181998186b170ba85cd9c Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Wed, 2 Oct 2024 07:31:48 +0300 Subject: [PATCH 03/30] fix c2d database --- src/OceanNode.ts | 8 +++++++- src/components/c2d/compute_engine_docker.ts | 10 +++++++--- src/components/c2d/compute_engines.ts | 7 ++++--- src/components/database/index.ts | 14 +++++++++++--- src/components/database/sqliteCompute.ts | 16 ++++++++++++---- 5 files changed, 41 insertions(+), 14 deletions(-) diff --git a/src/OceanNode.ts b/src/OceanNode.ts index ba40feac9..37da3c89a 100644 --- a/src/OceanNode.ts +++ b/src/OceanNode.ts @@ -60,7 +60,13 @@ export class OceanNode { if (this.c2dEngines) { await this.c2dEngines.stopAllEngines() } - if (_config && _config.c2dClusters) this.c2dEngines = new C2DEngines(_config) + if (_config && _config.c2dClusters) { + if (!this.db || !this.db.c2d) { + OCEAN_NODE_LOGGER.error('C2DDatabase is mandatory for compute engines!') + return + } + this.c2dEngines = new C2DEngines(_config, this.db.c2d) + } } public getP2PNode(): OceanP2P | undefined { diff --git a/src/components/c2d/compute_engine_docker.ts b/src/components/c2d/compute_engine_docker.ts index d04bb8c9d..024b315c1 100644 --- a/src/components/c2d/compute_engine_docker.ts +++ b/src/components/c2d/compute_engine_docker.ts @@ -11,12 +11,16 @@ import type { import { ZeroAddress } from 'ethers' // import { getProviderFeeToken } from '../../components/core/utils/feesHandler.js' import { C2DEngine } from './compute_engine_base.js' +import { C2DDatabase } from '../database/index.js' import { create256Hash } from '../../utils/crypt.js' export class C2DEngineDocker extends C2DEngine { // eslint-disable-next-line no-useless-constructor private envs: ComputeEnvironment[] = [] - public constructor(clusterConfig: C2DClusterInfo) { + private db: C2DDatabase + public constructor(clusterConfig: C2DClusterInfo, db: C2DDatabase) { super(clusterConfig) + this.db = db + // TO DO C2D - create envs } @@ -82,7 +86,7 @@ export class C2DEngineDocker extends C2DEngine { // this uses the docker engine, but exposes only one env, the free one export class C2DEngineDockerFree extends C2DEngineDocker { - public constructor(clusterConfig: C2DClusterInfo) { + public constructor(clusterConfig: C2DClusterInfo, db: C2DDatabase) { // we remove envs, cause we have our own const owerwrite = { type: C2DClusterType.DOCKER, @@ -97,7 +101,7 @@ export class C2DEngineDockerFree extends C2DEngineDocker { keyPath: clusterConfig.connection.keyPath } } - super(owerwrite) + super(owerwrite, db) } // eslint-disable-next-line require-await diff --git a/src/components/c2d/compute_engines.ts b/src/components/c2d/compute_engines.ts index e420abff4..24414a29f 100644 --- a/src/components/c2d/compute_engines.ts +++ b/src/components/c2d/compute_engines.ts @@ -3,10 +3,11 @@ import { C2DEngine } from './compute_engine_base.js' import { C2DEngineOPFK8 } from './compute_engine_opf_k8.js' import { C2DEngineDocker, C2DEngineDockerFree } from './compute_engine_docker.js' import { OceanNodeConfig } from '../../@types/OceanNode.js' +import { C2DDatabase } from '../database/index.js' export class C2DEngines { public engines: C2DEngine[] - public constructor(config: OceanNodeConfig) { + public constructor(config: OceanNodeConfig, db: C2DDatabase) { // let's see what engines do we have and initialize them one by one // for docker, we need to add the "free" let haveFree = false @@ -17,9 +18,9 @@ export class C2DEngines { this.engines.push(new C2DEngineOPFK8(cluster)) } if (cluster.type === C2DClusterType.DOCKER) { - this.engines.push(new C2DEngineDocker(cluster)) + this.engines.push(new C2DEngineDocker(cluster, db)) if (!haveFree) { - this.engines.push(new C2DEngineDockerFree(cluster)) + this.engines.push(new C2DEngineDockerFree(cluster, db)) haveFree = true } } diff --git a/src/components/database/index.ts b/src/components/database/index.ts index ecef93dd4..f5e4e1f6d 100644 --- a/src/components/database/index.ts +++ b/src/components/database/index.ts @@ -1101,20 +1101,28 @@ export class C2DDatabase { })() as unknown as C2DDatabase } + // eslint-disable-next-line require-await async newJob(job: DBComputeJob): Promise { // TO DO C2D + return '' } - async getJob(jobId: string): Promise { - // TO DO C2D + // eslint-disable-next-line require-await + async getJob(jobId: string): Promise { + return null } async updateJob(job: DBComputeJob) { // TO DO C2D } - async getRunningJobs(engine?: string, environment?: string): Promise { + // eslint-disable-next-line require-await + async getRunningJobs( + engine?: string, + environment?: string + ): Promise { // TO DO C2D + return null } } diff --git a/src/components/database/sqliteCompute.ts b/src/components/database/sqliteCompute.ts index 898688bed..b5f687bfe 100644 --- a/src/components/database/sqliteCompute.ts +++ b/src/components/database/sqliteCompute.ts @@ -4,9 +4,9 @@ import sqlite3 from 'sqlite3' interface ComputeDatabaseProvider { newJob(job: DBComputeJob): Promise - getJob(jobId: string): Promise + getJob(jobId: string): Promise updateJob(job: DBComputeJob): void - getRunningJobs(engine?: string, environment?: string): Promise + getRunningJobs(engine?: string, environment?: string): Promise } export class SQLiteCompute implements ComputeDatabaseProvider { @@ -26,17 +26,25 @@ export class SQLiteCompute implements ComputeDatabaseProvider { // eslint-disable-next-line require-await async newJob(job: DBComputeJob): Promise { // TO DO C2D + return null } - async getJob(jobId: string): Promise { + // eslint-disable-next-line require-await + async getJob(jobId: string): Promise { // TO DO C2D + return null } async updateJob(job: DBComputeJob) { // TO DO C2D } - async getRunningJobs(engine?: string, environment?: string): Promise { + // eslint-disable-next-line require-await + async getRunningJobs( + engine?: string, + environment?: string + ): Promise { // TO DO C2D + return null } } From 08f5d34f5dabd2692d6c5ad33942f9e636de0b51 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Wed, 2 Oct 2024 07:43:23 +0300 Subject: [PATCH 04/30] generic c2d schema --- src/components/database/schemas.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/database/schemas.ts b/src/components/database/schemas.ts index bfa673c77..c422cf3ca 100644 --- a/src/components/database/schemas.ts +++ b/src/components/database/schemas.ts @@ -67,6 +67,7 @@ export const schemas: Schemas = { enable_nested_fields: true, fields: [ // TO DO C2D + { name: '.*', type: 'auto', optional: true } ] }, indexerSchemas: { From ab52c8779b7eb5b4e3102e807893b21d9d430bdc Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Sat, 5 Oct 2024 07:55:28 +0300 Subject: [PATCH 05/30] BREAKING: refactor ComputeAsset and ComputeStartCommand interfaces --- src/@types/C2D/C2D.ts | 10 +++---- src/@types/commands.ts | 3 +- src/components/c2d/compute_engine_opf_k8.ts | 33 +++++++++++++++------ src/components/core/compute/startCompute.ts | 10 +++---- src/components/httpRoutes/compute.ts | 6 +--- src/test/integration/compute.test.ts | 26 +++++++++------- src/test/unit/commands.test.ts | 2 +- 7 files changed, 52 insertions(+), 38 deletions(-) diff --git a/src/@types/C2D/C2D.ts b/src/@types/C2D/C2D.ts index aba4b080e..f89a39945 100644 --- a/src/@types/C2D/C2D.ts +++ b/src/@types/C2D/C2D.ts @@ -1,5 +1,5 @@ import type { MetadataAlgorithm } from '../DDO/Metadata.js' - +import type { BaseFileObject } from '../fileObject.js' export enum C2DClusterType { // eslint-disable-next-line no-unused-vars OPF_K8 = 0, @@ -85,9 +85,9 @@ export interface ComputeOutput { } export interface ComputeAsset { - url?: string - documentId: string - serviceId: string + fileObject?: BaseFileObject + documentId?: string + serviceId?: string transferTxId?: string userdata?: { [key: string]: any } } @@ -95,7 +95,7 @@ export interface ComputeAsset { export interface ComputeAlgorithm { documentId?: string serviceId?: string - url?: string + fileObject?: BaseFileObject meta?: MetadataAlgorithm transferTxId?: string algocustomdata?: { [key: string]: any } diff --git a/src/@types/commands.ts b/src/@types/commands.ts index aac2c6dfe..d85ec3853 100644 --- a/src/@types/commands.ts +++ b/src/@types/commands.ts @@ -158,8 +158,7 @@ export interface ComputeStartCommand extends Command { nonce: string environment: string algorithm: ComputeAlgorithm - dataset: ComputeAsset - additionalDatasets?: ComputeAsset[] + datasets?: ComputeAsset[] output?: ComputeOutput } diff --git a/src/components/c2d/compute_engine_opf_k8.ts b/src/components/c2d/compute_engine_opf_k8.ts index b4cac240d..6a771918b 100644 --- a/src/components/c2d/compute_engine_opf_k8.ts +++ b/src/components/c2d/compute_engine_opf_k8.ts @@ -24,7 +24,7 @@ import { ZeroAddress } from 'ethers' import { getProviderFeeToken } from '../../components/core/utils/feesHandler.js' import { URLUtils } from '../../utils/url.js' import { C2DEngine } from './compute_engine_base.js' - +import { Storage } from '../storage/index.js' export class C2DEngineOPFK8 extends C2DEngine { // eslint-disable-next-line no-useless-constructor public constructor(clusterConfig: C2DClusterInfo) { @@ -73,12 +73,19 @@ export class C2DEngineOPFK8 extends C2DEngine { const stagesInput: OPFK8ComputeStageInput[] = [] let index = 0 for (const asset of assets) { - if (asset.url) - stagesInput.push({ - index, - url: [asset.url] - }) - else + if (asset.fileObject) { + try { + // since opf k8 supports only urls, we need to extract them + const storage = Storage.getStorageClass(asset.fileObject, config) + stagesInput.push({ + index, + url: [storage.getDownloadUrl()] + }) + } catch (e) { + const message = `Exception on startCompute. Cannot get URL of asset` + throw new Error(message) + } + } else stagesInput.push({ index, id: asset.documentId, @@ -100,8 +107,16 @@ export class C2DEngineOPFK8 extends C2DEngine { } // continue with algorithm const stageAlgorithm: OPFK8ComputeStageAlgorithm = {} - if (algorithm.url) { - stageAlgorithm.url = algorithm.url + + if (algorithm.fileObject) { + try { + // since opf k8 supports only urls, we need to extract them + const storage = Storage.getStorageClass(algorithm.fileObject, config) + stageAlgorithm.url = storage.getDownloadUrl() + } catch (e) { + const message = `Exception on startCompute. Cannot get URL of asset` + throw new Error(message) + } } else { stageAlgorithm.remote = { txId: algorithm.transferTxId, diff --git a/src/components/core/compute/startCompute.ts b/src/components/core/compute/startCompute.ts index b0c822449..53e8ded9a 100644 --- a/src/components/core/compute/startCompute.ts +++ b/src/components/core/compute/startCompute.ts @@ -1,6 +1,5 @@ import { Readable } from 'stream' import { P2PCommandResponse } from '../../../@types/index.js' -import { ComputeAsset } from '../../../@types/C2D/C2D.js' import { CORE_LOGGER } from '../../../utils/logging/common.js' import { Handler } from '../handler/handler.js' import { ComputeStartCommand } from '../../../@types/commands.js' @@ -36,7 +35,7 @@ export class ComputeStartHandler extends Handler { 'nonce', 'environment', 'algorithm', - 'dataset' + 'datasets' ]) if (commandValidation.valid) { if (!isAddress(command.consumerAddress)) { @@ -72,8 +71,6 @@ export class ComputeStartHandler extends Handler { } } const node = this.getOceanNode() - const assets: ComputeAsset[] = [task.dataset] - if (task.additionalDatasets) assets.push(...task.additionalDatasets) const { algorithm } = task let foundValidCompute = null @@ -93,7 +90,8 @@ export class ComputeStartHandler extends Handler { } } // check algo - for (const elem of [...[task.algorithm], ...assets]) { + for (const elem of [...[task.algorithm], ...task.datasets]) { + console.log(elem) const result: any = { validOrder: false } if ('documentId' in elem && elem.documentId) { result.did = elem.documentId @@ -322,7 +320,7 @@ export class ComputeStartHandler extends Handler { const { validUntil } = foundValidCompute const response = await engine.startComputeJob( - assets, + task.datasets, algorithm, task.output, task.consumerAddress, diff --git a/src/components/httpRoutes/compute.ts b/src/components/httpRoutes/compute.ts index ef1714448..2ad7ae727 100644 --- a/src/components/httpRoutes/compute.ts +++ b/src/components/httpRoutes/compute.ts @@ -94,11 +94,7 @@ computeRoutes.post(`${SERVICES_API_BASE_PATH}/compute`, async (req, res) => { nonce: (req.body.nonce as string) || null, environment: (req.body.environment as string) || null, algorithm: (req.body.algorithm as ComputeAlgorithm) || null, - dataset: (req.body.dataset as unknown as ComputeAsset) || null - } - if (req.body.additionalDatasets) { - startComputeTask.additionalDatasets = req.query - .additionalDatasets as unknown as ComputeAsset[] + datasets: (req.body.dataset as unknown as ComputeAsset[]) || null } if (req.body.output) { startComputeTask.output = req.body.output as ComputeOutput diff --git a/src/test/integration/compute.test.ts b/src/test/integration/compute.test.ts index d108edb13..f120719e0 100644 --- a/src/test/integration/compute.test.ts +++ b/src/test/integration/compute.test.ts @@ -523,11 +523,13 @@ describe('Compute', () => { signature, nonce, environment: firstEnv.id, - dataset: { - documentId: publishedComputeDataset.ddo.id, - serviceId: publishedComputeDataset.ddo.services[0].id, - transferTxId: '0x123' - }, + datasets: [ + { + documentId: publishedComputeDataset.ddo.id, + serviceId: publishedComputeDataset.ddo.services[0].id, + transferTxId: '0x123' + } + ], algorithm: { documentId: publishedAlgoDataset.ddo.id, serviceId: publishedAlgoDataset.ddo.services[0].id, @@ -538,6 +540,8 @@ describe('Compute', () => { // output?: ComputeOutput } const response = await new ComputeStartHandler(oceanNode).handle(startComputeTask) + console.log('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!') + console.log(response) assert(response, 'Failed to get response') // should fail, because txId '0x123' is not a valid order assert(response.status.httpStatus === 500, 'Failed to get 500 response') @@ -560,11 +564,13 @@ describe('Compute', () => { signature, nonce, environment: firstEnv.id, - dataset: { - documentId: publishedComputeDataset.ddo.id, - serviceId: publishedComputeDataset.ddo.services[0].id, - transferTxId: datasetOrderTxId - }, + datasets: [ + { + documentId: publishedComputeDataset.ddo.id, + serviceId: publishedComputeDataset.ddo.services[0].id, + transferTxId: datasetOrderTxId + } + ], algorithm: { documentId: publishedAlgoDataset.ddo.id, serviceId: publishedAlgoDataset.ddo.services[0].id, diff --git a/src/test/unit/commands.test.ts b/src/test/unit/commands.test.ts index e00874075..cbe355dce 100644 --- a/src/test/unit/commands.test.ts +++ b/src/test/unit/commands.test.ts @@ -289,7 +289,7 @@ describe('Commands and handlers', () => { nonce: '', environment: '', algorithm: undefined, - dataset: undefined + datasets: undefined } expect(startEnvHandler.validate(startEnvCommand).valid).to.be.equal(false) // ----------------------------------------- From 0c3f7e3687a0f353d3a979f9840e46af65df5b56 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Sat, 5 Oct 2024 08:27:37 +0300 Subject: [PATCH 06/30] add freeCompute handler --- src/@types/commands.ts | 5 ++ src/components/c2d/compute_engine_base.ts | 10 +-- src/components/c2d/compute_engine_docker.ts | 12 ++-- src/components/c2d/compute_engine_opf_k8.ts | 18 +++-- src/components/c2d/compute_engines.ts | 19 +++++- src/components/core/compute/startCompute.ts | 68 ++++++++++++++++++- .../core/handler/coreHandlersRegistry.ts | 5 ++ src/components/httpRoutes/compute.ts | 41 +++++++++++ src/utils/constants.ts | 2 + 9 files changed, 160 insertions(+), 20 deletions(-) diff --git a/src/@types/commands.ts b/src/@types/commands.ts index d85ec3853..e7438e743 100644 --- a/src/@types/commands.ts +++ b/src/@types/commands.ts @@ -161,6 +161,11 @@ export interface ComputeStartCommand extends Command { datasets?: ComputeAsset[] output?: ComputeOutput } +export interface FreeComputeStartCommand extends Command { + algorithm: ComputeAlgorithm + datasets?: ComputeAsset[] + output?: ComputeOutput +} export interface ComputeStopCommand extends Command { consumerAddress: string diff --git a/src/components/c2d/compute_engine_base.ts b/src/components/c2d/compute_engine_base.ts index 6b8569b80..2190df8ce 100644 --- a/src/components/c2d/compute_engine_base.ts +++ b/src/components/c2d/compute_engine_base.ts @@ -27,7 +27,7 @@ export class C2DEngine { // functions which need to be implemented by all engine types // eslint-disable-next-line require-await - public async getComputeEnvironments(chainId: number): Promise { + public async getComputeEnvironments(chainId?: number): Promise { throw new Error(`Not implemented`) } @@ -82,11 +82,11 @@ export class C2DEngine { assets: ComputeAsset[], algorithm: ComputeAlgorithm, output: ComputeOutput, - owner: string, environment: string, - validUntil: number, - chainId: number, - agreementId: string + owner?: string, + validUntil?: number, + chainId?: number, + agreementId?: string ): Promise { throw new Error(`Not implemented`) } diff --git a/src/components/c2d/compute_engine_docker.ts b/src/components/c2d/compute_engine_docker.ts index 024b315c1..d0a57b9d5 100644 --- a/src/components/c2d/compute_engine_docker.ts +++ b/src/components/c2d/compute_engine_docker.ts @@ -26,7 +26,7 @@ export class C2DEngineDocker extends C2DEngine { // eslint-disable-next-line require-await public override async getComputeEnvironments( - chainId: number + chainId?: number ): Promise { /** * Returns all cluster's compute environments for a specific chainId. Env's id already contains the cluster hash @@ -40,11 +40,11 @@ export class C2DEngineDocker extends C2DEngine { assets: ComputeAsset[], algorithm: ComputeAlgorithm, output: ComputeOutput, - owner: string, environment: string, - validUntil: number, - chainId: number, - agreementId: string + owner?: string, + validUntil?: number, + chainId?: number, + agreementId?: string ): Promise { return null } @@ -106,7 +106,7 @@ export class C2DEngineDockerFree extends C2DEngineDocker { // eslint-disable-next-line require-await public override async getComputeEnvironments( - chainId: number + chainId?: number ): Promise { /** * Returns all cluster's compute environments for a specific chainId. Env's id already contains the cluster hash diff --git a/src/components/c2d/compute_engine_opf_k8.ts b/src/components/c2d/compute_engine_opf_k8.ts index 6a771918b..f3799dc0f 100644 --- a/src/components/c2d/compute_engine_opf_k8.ts +++ b/src/components/c2d/compute_engine_opf_k8.ts @@ -32,7 +32,7 @@ export class C2DEngineOPFK8 extends C2DEngine { } public override async getComputeEnvironments( - chainId: number + chainId?: number ): Promise { /** * Returns all cluster's compute environments for a specific chainId. Env's id already contains the cluster hash @@ -40,7 +40,8 @@ export class C2DEngineOPFK8 extends C2DEngine { const envs: ComputeEnvironment[] = [] const clusterHash = this.getC2DConfig().hash const baseUrl = URLUtils.sanitizeURLPath(this.getC2DConfig().connection) - const url = `${baseUrl}api/v1/operator/environments?chain_id=${chainId}` + let url = `${baseUrl}api/v1/operator/environments` + if (chainId) url += `?chain_id=${chainId}` try { const { data } = await axios.get(url) if (!data) return envs @@ -61,12 +62,17 @@ export class C2DEngineOPFK8 extends C2DEngine { assets: ComputeAsset[], algorithm: ComputeAlgorithm, output: ComputeOutput, - owner: string, environment: string, - validUntil: number, - chainId: number, - agreementId: string + owner?: string, + validUntil?: number, + chainId?: number, + agreementId?: string ): Promise { + // owner, validUntil,chainId, agreementId are not optional for OPF K8 + if (!owner) throw new Error(`Cannot start a c2d job without owner`) + if (!validUntil) throw new Error(`Cannot start a c2d job without validUntil`) + if (!chainId) throw new Error(`Cannot start a c2d job without chainId`) + if (!agreementId) throw new Error(`Cannot start a c2d job without agreementId`) // let's build the stage first // start with stage.input const config = await getConfiguration() diff --git a/src/components/c2d/compute_engines.ts b/src/components/c2d/compute_engines.ts index 24414a29f..495b04db2 100644 --- a/src/components/c2d/compute_engines.ts +++ b/src/components/c2d/compute_engines.ts @@ -75,8 +75,25 @@ export class C2DEngines { throw new Error(`C2D Engine not found by hash: ${clusterHash}`) } + async getC2DByEnvId(envId: string): Promise { + /** + * Searches all envs and returns engine class + * + * @param envId - Environment Id + * + */ + const { engines } = this + for (const i of engines) { + const environments = await i.getComputeEnvironments() + for (const env of environments) { + if (env.id === envId) return i + } + } + throw new Error(`C2D Engine not found by id: ${envId}`) + } + async fetchEnvironments( - chainId: number, + chainId?: number, engine?: C2DEngine ): Promise { /** diff --git a/src/components/core/compute/startCompute.ts b/src/components/core/compute/startCompute.ts index 53e8ded9a..ff04103dd 100644 --- a/src/components/core/compute/startCompute.ts +++ b/src/components/core/compute/startCompute.ts @@ -2,7 +2,7 @@ import { Readable } from 'stream' import { P2PCommandResponse } from '../../../@types/index.js' import { CORE_LOGGER } from '../../../utils/logging/common.js' import { Handler } from '../handler/handler.js' -import { ComputeStartCommand } from '../../../@types/commands.js' +import { FreeComputeStartCommand, ComputeStartCommand } from '../../../@types/commands.js' import { getAlgoChecksums, validateAlgoForDataset } from './utils.js' import { ValidateParams, @@ -323,8 +323,8 @@ export class ComputeStartHandler extends Handler { task.datasets, algorithm, task.output, - task.consumerAddress, envId, + task.consumerAddress, validUntil, chainId, agreementId @@ -353,3 +353,67 @@ export class ComputeStartHandler extends Handler { } } } + +// free compute +// - has no validation +export class FreeComputeStartHandler extends Handler { + validate(command: ComputeStartCommand): ValidateParams { + const commandValidation = validateCommandParameters(command, ['algorithm']) + return commandValidation + } + + async handle(task: FreeComputeStartCommand): Promise { + const validationResponse = await this.verifyParamsAndRateLimits(task) + if (this.shouldDenyTaskHandling(validationResponse)) { + return validationResponse + } + let environment = null + try { + // get all envs and see if we have a free one + const allEnvs = await this.getOceanNode().getC2DEngines().fetchEnvironments() + for (const env of allEnvs) { + if (env.free) { + environment = env + } + } + if (!environment) + return { + stream: null, + status: { + httpStatus: 500, + error: 'This node does not have a free compute env' + } + } + const engine = await this.getOceanNode() + .getC2DEngines() + .getC2DByEnvId(environment.id) + const response = await engine.startComputeJob( + task.datasets, + task.algorithm, + task.output, + environment.id + ) + + CORE_LOGGER.logMessage( + 'FreeComputeStartCommand Response: ' + JSON.stringify(response, null, 2), + true + ) + + return { + stream: Readable.from(JSON.stringify(response)), + status: { + httpStatus: 200 + } + } + } catch (error) { + CORE_LOGGER.error(error.message) + return { + stream: null, + status: { + httpStatus: 500, + error: error.message + } + } + } + } +} diff --git a/src/components/core/handler/coreHandlersRegistry.ts b/src/components/core/handler/coreHandlersRegistry.ts index e4d37feef..a409c2531 100644 --- a/src/components/core/handler/coreHandlersRegistry.ts +++ b/src/components/core/handler/coreHandlersRegistry.ts @@ -23,6 +23,7 @@ import { Command } from '../../../@types/commands.js' import { ComputeGetEnvironmentsHandler, ComputeStartHandler, + FreeComputeStartHandler, ComputeStopHandler, ComputeGetStatusHandler, ComputeGetResultHandler, @@ -95,6 +96,10 @@ export class CoreHandlersRegistry { PROTOCOL_COMMANDS.COMPUTE_START, new ComputeStartHandler(node) ) + this.registerCoreHandler( + PROTOCOL_COMMANDS.FREE_COMPUTE_START, + new FreeComputeStartHandler(node) + ) this.registerCoreHandler(PROTOCOL_COMMANDS.COMPUTE_STOP, new ComputeStopHandler(node)) this.registerCoreHandler( PROTOCOL_COMMANDS.COMPUTE_GET_STATUS, diff --git a/src/components/httpRoutes/compute.ts b/src/components/httpRoutes/compute.ts index 2ad7ae727..514bff465 100644 --- a/src/components/httpRoutes/compute.ts +++ b/src/components/httpRoutes/compute.ts @@ -2,6 +2,7 @@ import express from 'express' import { ComputeGetEnvironmentsHandler, ComputeStartHandler, + FreeComputeStartHandler, ComputeStopHandler, ComputeGetStatusHandler, ComputeGetResultHandler, @@ -14,6 +15,7 @@ import type { } from '../../@types/C2D/C2D.js' import type { ComputeStartCommand, + FreeComputeStartCommand, ComputeStopCommand, ComputeGetResultCommand, ComputeGetStatusCommand @@ -114,6 +116,43 @@ computeRoutes.post(`${SERVICES_API_BASE_PATH}/compute`, async (req, res) => { } }) +// free compute +computeRoutes.post(`${SERVICES_API_BASE_PATH}/freeCompute`, async (req, res) => { + try { + HTTP_LOGGER.logMessage( + `FreeComputeStartCommand request received as body params: ${JSON.stringify( + req.body + )}`, + true + ) + + const startComputeTask: FreeComputeStartCommand = { + command: PROTOCOL_COMMANDS.FREE_COMPUTE_START, + node: (req.body.node as string) || null, + algorithm: (req.body.algorithm as ComputeAlgorithm) || null, + datasets: (req.body.dataset as unknown as ComputeAsset[]) || null + } + if (req.body.output) { + startComputeTask.output = req.body.output as ComputeOutput + } + + const response = await new FreeComputeStartHandler(req.oceanNode).handle( + startComputeTask + ) + if (response?.status?.httpStatus === 200) { + const jobs = await streamToObject(response.stream as Readable) + res.status(200).json(jobs) + } else { + HTTP_LOGGER.log(LOG_LEVELS_STR.LEVEL_INFO, `Error: ${response?.status?.error}`) + res.status(response?.status.httpStatus).json(response?.status?.error) + } + } catch (error) { + HTTP_LOGGER.log(LOG_LEVELS_STR.LEVEL_ERROR, `Error: ${error}`) + res.status(500).send('Internal Server Error') + } +}) + +// stop compute computeRoutes.put(`${SERVICES_API_BASE_PATH}/compute`, async (req, res) => { try { HTTP_LOGGER.logMessage( @@ -141,6 +180,7 @@ computeRoutes.put(`${SERVICES_API_BASE_PATH}/compute`, async (req, res) => { } }) +// get status computeRoutes.get(`${SERVICES_API_BASE_PATH}/compute`, async (req, res) => { try { HTTP_LOGGER.logMessage( @@ -165,6 +205,7 @@ computeRoutes.get(`${SERVICES_API_BASE_PATH}/compute`, async (req, res) => { } }) +// compute results computeRoutes.get(`${SERVICES_API_BASE_PATH}/computeResult`, async (req, res) => { try { HTTP_LOGGER.logMessage( diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 130ad3058..7a9f9b302 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -19,6 +19,7 @@ export const PROTOCOL_COMMANDS = { VALIDATE_DDO: 'validateDDO', COMPUTE_GET_ENVIRONMENTS: 'getComputeEnvironments', COMPUTE_START: 'startCompute', + FREE_COMPUTE_START: 'freeStartCompute', COMPUTE_STOP: 'stopCompute', COMPUTE_GET_STATUS: 'getComputeStatus', COMPUTE_GET_RESULT: 'getComputeResult', @@ -47,6 +48,7 @@ export const SUPPORTED_PROTOCOL_COMMANDS: string[] = [ PROTOCOL_COMMANDS.VALIDATE_DDO, PROTOCOL_COMMANDS.COMPUTE_GET_ENVIRONMENTS, PROTOCOL_COMMANDS.COMPUTE_START, + PROTOCOL_COMMANDS.FREE_COMPUTE_START, PROTOCOL_COMMANDS.COMPUTE_STOP, PROTOCOL_COMMANDS.COMPUTE_GET_STATUS, PROTOCOL_COMMANDS.COMPUTE_GET_RESULT, From da59f18afc1312cef93d8c92f1e250a4fc1ab707 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Sun, 6 Oct 2024 19:01:10 +0300 Subject: [PATCH 07/30] working POC of Docker compute --- src/@types/C2D/C2D.ts | 79 +++ src/@types/OceanNode.ts | 2 +- src/@types/commands.ts | 13 +- src/components/c2d/compute_engine_base.ts | 5 + src/components/c2d/compute_engine_docker.ts | 546 +++++++++++++++++- src/components/core/compute/getStatus.ts | 14 +- .../core/compute/getStreamableLogs.ts | 88 +++ src/components/core/compute/index.ts | 1 + src/components/core/compute/startCompute.ts | 18 +- .../core/handler/coreHandlersRegistry.ts | 7 +- src/components/core/handler/statusHandler.ts | 10 +- src/components/core/utils/statusHandler.ts | 11 +- src/components/database/index.ts | 30 +- src/components/database/sqliteCompute.ts | 10 +- src/components/httpRoutes/compute.ts | 49 +- src/components/storage/index.ts | 5 + src/utils/config.ts | 6 +- src/utils/constants.ts | 2 + 18 files changed, 853 insertions(+), 43 deletions(-) create mode 100644 src/components/core/compute/getStreamableLogs.ts diff --git a/src/@types/C2D/C2D.ts b/src/@types/C2D/C2D.ts index f89a39945..f56776f0b 100644 --- a/src/@types/C2D/C2D.ts +++ b/src/@types/C2D/C2D.ts @@ -16,6 +16,8 @@ export interface C2DClusterInfo { hash: string /** Connection URI */ connection?: any + /** Folder for storing data */ + tempFolder?: string } export interface ComputeEnvironment { @@ -107,10 +109,87 @@ export interface AlgoChecksums { container: string } +// this is the internal structure export interface DBComputeJob extends ComputeJob { + clusterHash: string configlogURL: string publishlogURL: string algologURL: string outputsURL: string stopRequested: boolean + algorithm: ComputeAlgorithm + assets: ComputeAsset[] + isRunning: boolean + isStarted: boolean + containerImage: string +} + +// make sure we keep them both in sync +export enum C2DStatusNumber { + // eslint-disable-next-line no-unused-vars + JobStarted = 0, + // eslint-disable-next-line no-unused-vars + PullImage = 10, + // eslint-disable-next-line no-unused-vars + ConfiguringVolumes = 20, + // eslint-disable-next-line no-unused-vars + VolumeCreationFailed = 21, + // eslint-disable-next-line no-unused-vars + ContainerCreationFailed = 22, + // eslint-disable-next-line no-unused-vars + Provisioning = 30, + // eslint-disable-next-line no-unused-vars + DataProvisioningFailed = 31, + // eslint-disable-next-line no-unused-vars + AlgorithmProvisioningFailed = 32, + // eslint-disable-next-line no-unused-vars + DataUploadFailed = 32, + // eslint-disable-next-line no-unused-vars + RunningAlgorithm = 40, + // eslint-disable-next-line no-unused-vars + AlgorithmFailed = 41, + // eslint-disable-next-line no-unused-vars + FilteringResults = 50, + // eslint-disable-next-line no-unused-vars + PublishingResults = 60, + // eslint-disable-next-line no-unused-vars + ResultsFetchFailed = 61, + // eslint-disable-next-line no-unused-vars + ResultsUploadFailed = 62, + // eslint-disable-next-line no-unused-vars + JobFinished = 70 +} +export enum C2DStatusText { + // eslint-disable-next-line no-unused-vars + JobStarted = 'Job started', + // eslint-disable-next-line no-unused-vars + PullImage = 'Pulling algorithm image', + // eslint-disable-next-line no-unused-vars + ConfiguringVolumes = 'Configuring volumes', + // eslint-disable-next-line no-unused-vars + VolumeCreationFailed = 'Volume creation failed', + // eslint-disable-next-line no-unused-vars + ContainerCreationFailed = 'Container creation failed', + // eslint-disable-next-line no-unused-vars + Provisioning = 'Provisioning data', + // eslint-disable-next-line no-unused-vars + DataProvisioningFailed = 'Data provisioning failed', + // eslint-disable-next-line no-unused-vars + AlgorithmProvisioningFailed = 'Algorithm provisioning failed', + // eslint-disable-next-line no-unused-vars + DataUploadFailed = 'Data upload to container failed', + // eslint-disable-next-line no-unused-vars + RunningAlgorithm = 'Running algorithm ', + // eslint-disable-next-line no-unused-vars + AlgorithmFailed = 'Failed to run algorithm', + // eslint-disable-next-line no-unused-vars + FilteringResults = 'Filtering results', + // eslint-disable-next-line no-unused-vars + PublishingResults = 'Publishing results', + // eslint-disable-next-line no-unused-vars + ResultsFetchFailed = 'Failed to get outputs folder from container', + // eslint-disable-next-line no-unused-vars + ResultsUploadFailed = 'Failed to upload results to storage', + // eslint-disable-next-line no-unused-vars + JobFinished = 'Job finished' } diff --git a/src/@types/OceanNode.ts b/src/@types/OceanNode.ts index b9ea96bba..c4eb71c8c 100644 --- a/src/@types/OceanNode.ts +++ b/src/@types/OceanNode.ts @@ -125,7 +125,7 @@ export interface OceanNodeStatus { codeHash?: string allowedAdmins?: string[] // detailed information - c2dClusters?: C2DClusterInfo[] + c2dClusters?: any[] supportedSchemas?: Schema[] } diff --git a/src/@types/commands.ts b/src/@types/commands.ts index 72b3a7d0c..e5d5108ea 100644 --- a/src/@types/commands.ts +++ b/src/@types/commands.ts @@ -65,7 +65,9 @@ export interface ValidateDDOCommand extends Command { ddo: DDO } -export interface StatusCommand extends Command {} +export interface StatusCommand extends Command { + detailed?: boolean +} export interface DetailedStatusCommand extends StatusCommand {} export interface EchoCommand extends Command {} @@ -164,6 +166,9 @@ export interface ComputeStartCommand extends Command { output?: ComputeOutput } export interface FreeComputeStartCommand extends Command { + consumerAddress: string + signature: string + nonce: string algorithm: ComputeAlgorithm datasets?: ComputeAsset[] output?: ComputeOutput @@ -184,6 +189,12 @@ export interface ComputeGetResultCommand extends Command { jobId: string index: number } +export interface ComputeGetStreamableLogsCommand extends Command { + consumerAddress: string + signature: string + nonce: string + jobId: string +} export interface ComputeGetStatusCommand extends Command { consumerAddress?: string diff --git a/src/components/c2d/compute_engine_base.ts b/src/components/c2d/compute_engine_base.ts index 2190df8ce..bff9c1b7b 100644 --- a/src/components/c2d/compute_engine_base.ts +++ b/src/components/c2d/compute_engine_base.ts @@ -117,6 +117,11 @@ export class C2DEngine { ): Promise { throw new Error(`Not implemented`) } + + // eslint-disable-next-line require-await + public async getStreamableLogs(jobId: string): Promise { + throw new Error(`Not implemented for this engine type`) + } } export class C2DEngineLocal extends C2DEngine { diff --git a/src/components/c2d/compute_engine_docker.ts b/src/components/c2d/compute_engine_docker.ts index d0a57b9d5..2a085d077 100644 --- a/src/components/c2d/compute_engine_docker.ts +++ b/src/components/c2d/compute_engine_docker.ts @@ -1,27 +1,66 @@ import { Readable } from 'stream' -import { C2DClusterType } from '../../@types/C2D/C2D.js' +import { C2DClusterType, C2DStatusNumber, C2DStatusText } from '../../@types/C2D/C2D.js' import type { C2DClusterInfo, ComputeEnvironment, ComputeAlgorithm, ComputeAsset, ComputeJob, - ComputeOutput + ComputeOutput, + DBComputeJob } from '../../@types/C2D/C2D.js' import { ZeroAddress } from 'ethers' // import { getProviderFeeToken } from '../../components/core/utils/feesHandler.js' +import { getConfiguration } from '../../utils/config.js' import { C2DEngine } from './compute_engine_base.js' import { C2DDatabase } from '../database/index.js' import { create256Hash } from '../../utils/crypt.js' +import { Storage } from '../storage/index.js' +import Dockerode from 'dockerode' +import type { ContainerCreateOptions, VolumeCreateOptions } from 'dockerode' +import * as tar from 'tar' +import { createWriteStream, existsSync, mkdirSync, rmSync, writeFileSync } from 'fs' +import { pipeline } from 'node:stream/promises' + export class C2DEngineDocker extends C2DEngine { // eslint-disable-next-line no-useless-constructor private envs: ComputeEnvironment[] = [] private db: C2DDatabase + public docker: Dockerode + private cronTimer: any + private cronTime: number = 2000 public constructor(clusterConfig: C2DClusterInfo, db: C2DDatabase) { super(clusterConfig) this.db = db - + this.docker = null + if (clusterConfig.connection.socketPath) { + try { + this.docker = new Dockerode({ socketPath: clusterConfig.connection.socketPath }) + } catch (e) { + console.log(e) + } + } + if ( + clusterConfig.connection.protocol && + clusterConfig.connection.host && + clusterConfig.connection.port + ) { + try { + this.docker = new Dockerode({ + protocol: clusterConfig.connection.protocol, + host: clusterConfig.connection.host, + port: clusterConfig.connection.port + }) + } catch (e) { + console.log(e) + } + } // TO DO C2D - create envs + try { + if (!existsSync(clusterConfig.tempFolder)) + mkdirSync(clusterConfig.tempFolder, { recursive: true }) + } catch (e) {} + this.setNewTimer() } // eslint-disable-next-line require-await @@ -31,7 +70,7 @@ export class C2DEngineDocker extends C2DEngine { /** * Returns all cluster's compute environments for a specific chainId. Env's id already contains the cluster hash */ - + if (!this.docker) return [] return this.envs } @@ -46,7 +85,57 @@ export class C2DEngineDocker extends C2DEngine { chainId?: number, agreementId?: string ): Promise { - return null + if (!this.docker) return [] + const jobId = create256Hash( + JSON.stringify({ + assets, + algorithm, + output, + environment, + owner, + validUntil, + chainId, + agreementId + }) + ) + // TO DO C2D - Check image, check arhitecture, etc + let { image } = algorithm.meta.container + if (algorithm.meta.container.checksum) + image = image + '@' + algorithm.meta.container.checksum + else if (algorithm.meta.container.tag) + image = image + ':' + algorithm.meta.container.checksum + else image = image + ':latest' + console.log('Using image: ' + image) + + const job: DBComputeJob = { + clusterHash: this.getC2DConfig().hash, + containerImage: image, + owner, + jobId, + dateCreated: String(Date.now() / 1000), + dateFinished: null, + status: C2DStatusNumber.JobStarted, + statusText: C2DStatusText.JobStarted, + results: [], + algorithm, + assets, + agreementId, + expireTimestamp: Date.now() / 1000 + validUntil, + environment, + configlogURL: null, + publishlogURL: null, + algologURL: null, + outputsURL: null, + stopRequested: false, + isRunning: true, + isStarted: false + } + await this.makeJobFolders(job) + this.db.newJob(job) + const cjob: ComputeJob = JSON.parse(JSON.stringify(job)) as ComputeJob + // we add cluster hash to user output + cjob.jobId = this.getC2DConfig().hash + '-' + cjob.jobId + return [cjob] } // eslint-disable-next-line require-await @@ -64,6 +153,11 @@ export class C2DEngineDocker extends C2DEngine { agreementId?: string, jobId?: string ): Promise { + const job = await this.db.getJob(jobId) + if (job) { + const res: ComputeJob[] = [job as ComputeJob] + return res + } return null } @@ -76,11 +170,406 @@ export class C2DEngineDocker extends C2DEngine { return null } + // eslint-disable-next-line require-await + public override async getStreamableLogs(jobId: string): Promise { + const job = await this.db.getJob(jobId) + if (!job) return null + try { + const container = await this.docker.getContainer(job.jobId + '-algoritm') + return await container.logs({ + stdout: true, + stderr: true, + follow: true + }) + } catch (e) { + return null + } + } + + private async setNewTimer() { + // don't set the cron if we don't have compute environments + if ((await this.getComputeEnvironments()).length > 0) + this.cronTimer = setInterval(this.InternalLoop.bind(this), this.cronTime) + } + private async InternalLoop() { // this is the internal loop of docker engine + // gets list of all running jobs and process them one by one + clearInterval(this.cronTimer) + this.cronTimer = null + // get all running jobs + const jobs = await this.db.getRunningJobs(this.getC2DConfig().hash) + console.log('Got jobs for engine ' + this.getC2DConfig().hash) + console.log(jobs) + const promises: any = [] + for (const job of jobs) { + promises.push(this.processJob(job)) + } + // wait for all promises, there is no return + await Promise.all(promises) + // set the cron again + this.setNewTimer() + } + + // eslint-disable-next-line require-await + private async processJob(job: DBComputeJob) { + console.log('Process job started') + console.log(job) // has to : // - monitor running containers and stop them if over limits // - monitor disc space and clean up + /* steps: + - instruct docker to pull image + - create volume + - after image is ready, create the container + - download assets & algo into temp folder + - download DDOS + - tar and upload assets & algo to container + - start the container + - check if container is exceeding validUntil + - if yes, stop it + - download /data/outputs and store it locally (or upload it somewhere) + - delete the container + - delete the volume + */ + if (job.status === C2DStatusNumber.JobStarted) { + // pull docker image + await this.docker.pull(job.containerImage) + job.status = C2DStatusNumber.PullImage + job.statusText = C2DStatusText.PullImage + await this.db.updateJob(job) + return // now we wait until image is ready + } + if (job.status === C2DStatusNumber.PullImage) { + try { + const imageInfo = await this.docker.getImage(job.containerImage) + // console.log(imageInfo) + const details = await imageInfo.inspect() + console.log(details) + job.status = C2DStatusNumber.ConfiguringVolumes + job.statusText = C2DStatusText.ConfiguringVolumes + await this.db.updateJob(job) + // now we can move forward + } catch (e) { + // not ready yet + // console.log(e) + } + return + } + if (job.status === C2DStatusNumber.ConfiguringVolumes) { + // create the volume & create container + // TO DO C2D: Choose driver & size + const volume: VolumeCreateOptions = { + Name: job.jobId + '-volume' + } + try { + await this.docker.createVolume(volume) + } catch (e) { + job.status = C2DStatusNumber.VolumeCreationFailed + job.statusText = C2DStatusText.VolumeCreationFailed + job.isRunning = false + await this.db.updateJob(job) + await this.cleanupJob(job) + } + // create the container + const mountVols: any = { '/data': {} } + const hostConfig: any = { + Mounts: [ + { + Type: 'volume', + Source: volume.Name, + Target: '/data', + ReadOnly: false + } + ] + } + const containerInfo: ContainerCreateOptions = { + name: job.jobId + '-algoritm', + Image: job.containerImage, + AttachStdin: false, + AttachStdout: true, + AttachStderr: true, + Tty: true, + OpenStdin: false, + StdinOnce: false, + Volumes: mountVols, + HostConfig: hostConfig + } + // TO DO - fix the following + if (job.algorithm.meta.container.entrypoint) { + const newEntrypoint = job.algorithm.meta.container.entrypoint.replace( + '$ALGO', + '/data/transformation/algorithm' + ) + containerInfo.Entrypoint = newEntrypoint + } + try { + const container = await this.docker.createContainer(containerInfo) + console.log(container) + job.status = C2DStatusNumber.Provisioning + job.statusText = C2DStatusText.Provisioning + await this.db.updateJob(job) + } catch (e) { + job.status = C2DStatusNumber.ContainerCreationFailed + job.statusText = C2DStatusText.ContainerCreationFailed + job.isRunning = false + await this.db.updateJob(job) + await this.cleanupJob(job) + } + return + } + if (job.status === C2DStatusNumber.Provisioning) { + // download algo & assets + const ret = await this.uploadData(job) + console.log('Upload data') + console.log(ret) + job.status = ret.status + job.statusText = ret.statusText + if (job.status !== C2DStatusNumber.RunningAlgorithm) { + // failed, let's close it + job.isRunning = false + await this.db.updateJob(job) + await this.cleanupJob(job) + } else { + await this.db.updateJob(job) + } + } + if (job.status === C2DStatusNumber.RunningAlgorithm) { + const container = await this.docker.getContainer(job.jobId + '-algoritm') + const details = await container.inspect() + console.log('Container inspect') + console.log(details) + if (job.isStarted === false) { + // make sure is not started + if (details.State.Running === false) { + try { + await container.start() + job.isStarted = true + await this.db.updateJob(job) + return + } catch (e) { + // container failed to start + console.log(e) + job.status = C2DStatusNumber.AlgorithmFailed + job.statusText = C2DStatusText.AlgorithmFailed + job.algologURL = String(e) + job.isRunning = false + await this.db.updateJob(job) + await this.cleanupJob(job) + return + } + } + } else { + // is running, we need to stop it.. + const timeNow = Date.now() / 1000 + console.log('timeNow: ' + timeNow + ' , Expiry: ' + job.expireTimestamp) + if (timeNow > job.expireTimestamp || job.stopRequested) { + // we need to stop the container + // make sure is running + console.log('We need to stop') + console.log(details.State.Running) + if (details.State.Running === true) { + try { + await container.stop() + do {} while ((await container.inspect()).State.Running === true) + } catch (e) { + // we should never reach this, unless the container is already stopped + console.log(e) + } + } + console.log('Stopped') + job.isStarted = false + job.status = C2DStatusNumber.PublishingResults + job.statusText = C2DStatusText.PublishingResults + await this.db.updateJob(job) + return + } else { + if (details.State.Running === false) { + job.isStarted = false + job.status = C2DStatusNumber.PublishingResults + job.statusText = C2DStatusText.PublishingResults + await this.db.updateJob(job) + return + } + } + } + } + if (job.status === C2DStatusNumber.PublishingResults) { + // get output + job.status = C2DStatusNumber.JobFinished + job.statusText = C2DStatusText.JobFinished + const container = await this.docker.getContainer(job.jobId + '-algoritm') + const outputsArchivePath = + this.getC2DConfig().tempFolder + '/' + job.jobId + '/data/outputs/outputs.tar' + try { + await pipeline( + await container.getArchive({ path: '/data/outputs' }), + createWriteStream(outputsArchivePath) + ) + } catch (e) { + console.log(e) + job.status = C2DStatusNumber.ResultsFetchFailed + job.statusText = C2DStatusText.ResultsFetchFailed + } + job.isRunning = false + await this.db.updateJob(job) + await this.cleanupJob(job) + } + } + + // eslint-disable-next-line require-await + private async cleanupJob(job: DBComputeJob) { + // cleaning up + // - get algo logs + // - delete volume + // - delete container + + const container = await this.docker.getContainer(job.jobId + '-algoritm') + try { + writeFileSync( + this.getC2DConfig().tempFolder + '/' + job.jobId + '/data/logs/algorithmLog', + await container.logs({ + stdout: true, + stderr: true, + follow: false + }) + ) + } catch (e) { + console.log(e) + } + + await container.remove() + const volume = await this.docker.getVolume(job.jobId + '-volume') + await volume.remove() + // remove folders + rmSync(this.getC2DConfig().tempFolder + '/' + job.jobId + '/data/inputs', { + recursive: true, + force: true + }) + rmSync(this.getC2DConfig().tempFolder + '/' + job.jobId + '/data/transformations', { + recursive: true, + force: true + }) + } + + private async uploadData( + job: DBComputeJob + ): Promise<{ status: C2DStatusNumber; statusText: C2DStatusText }> { + const config = await getConfiguration() + const ret = { + status: C2DStatusNumber.RunningAlgorithm, + statusText: C2DStatusText.RunningAlgorithm + } + // download algo + if (job.algorithm.fileObject) { + console.log(job.algorithm.fileObject) + const storage = Storage.getStorageClass(job.algorithm.fileObject, config) + + const fullAlgoPath = + this.getC2DConfig().tempFolder + + '/' + + job.jobId + + '/data/transformations/algorithm' + try { + await pipeline( + (await storage.getReadableStream()).stream, + createWriteStream(fullAlgoPath) + ) + } catch (e) { + console.log(e) + return { + status: C2DStatusNumber.AlgorithmProvisioningFailed, + statusText: C2DStatusText.AlgorithmProvisioningFailed + } + } + } + for (const i in job.assets) { + const asset = job.assets[i] + console.log(asset) + const storage = Storage.getStorageClass(asset.fileObject, config) + const fileInfo = await storage.getFileInfo({ + type: storage.getStorageType(asset.fileObject) + }) + const fullPath = + this.getC2DConfig().tempFolder + + '/' + + job.jobId + + '/data/inputs/' + + fileInfo[0].name + try { + await pipeline( + (await storage.getReadableStream()).stream, + createWriteStream(fullPath) + ) + } catch (e) { + console.log(e) + return { + status: C2DStatusNumber.DataProvisioningFailed, + statusText: C2DStatusText.DataProvisioningFailed + } + } + } + // now, we have to create a tar arhive + const folderToTar = this.getC2DConfig().tempFolder + '/' + job.jobId + '/data' + const destination = + this.getC2DConfig().tempFolder + '/' + job.jobId + '/tarData/upload.tar.gz' + tar.create( + { + gzip: true, + file: destination, + sync: true, + C: folderToTar + }, + ['./'] + ) + // now, upload it to the container + const container = await this.docker.getContainer(job.jobId + '-algoritm') + console.log('Start uploading') + try { + const stream = await container.putArchive(destination, { + path: '/data' + }) + console.log('PutArchive') + console.log(stream) + + console.log('Done uploading') + } catch (e) { + console.log('Data upload failed') + console.log(e) + return { + status: C2DStatusNumber.DataUploadFailed, + statusText: C2DStatusText.DataUploadFailed + } + } + rmSync(this.getC2DConfig().tempFolder + '/' + job.jobId + '/data/inputs', { + recursive: true, + force: true + }) + rmSync(this.getC2DConfig().tempFolder + '/' + job.jobId + '/data/transformations', { + recursive: true, + force: true + }) + rmSync(this.getC2DConfig().tempFolder + '/' + job.jobId + '/tarData', { + recursive: true, + force: true + }) + return ret + } + + // eslint-disable-next-line require-await + private async makeJobFolders(job: DBComputeJob) { + try { + const baseFolder = this.getC2DConfig().tempFolder + '/' + job.jobId + if (!existsSync(baseFolder)) mkdirSync(baseFolder) + if (!existsSync(baseFolder + '/data')) mkdirSync(baseFolder + '/data') + if (!existsSync(baseFolder + '/data/inputs')) mkdirSync(baseFolder + '/data/inputs') + if (!existsSync(baseFolder + '/data/transformations')) + mkdirSync(baseFolder + '/data/transformations') + if (!existsSync(baseFolder + '/data/outputs')) + mkdirSync(baseFolder + '/data/outputs') + if (!existsSync(baseFolder + '/data/logs')) mkdirSync(baseFolder + '/data/logs') + if (!existsSync(baseFolder + '/tarData')) mkdirSync(baseFolder + '/tarData') // used to upload and download data + } catch (e) {} } } @@ -88,9 +577,10 @@ export class C2DEngineDocker extends C2DEngine { export class C2DEngineDockerFree extends C2DEngineDocker { public constructor(clusterConfig: C2DClusterInfo, db: C2DDatabase) { // we remove envs, cause we have our own + const hash = create256Hash('free' + clusterConfig.hash) const owerwrite = { type: C2DClusterType.DOCKER, - hash: create256Hash('free' + clusterConfig.hash), + hash, connection: { socketPath: clusterConfig.connection.socketPath, protocol: clusterConfig.connection.protocol, @@ -99,7 +589,8 @@ export class C2DEngineDockerFree extends C2DEngineDocker { caPath: clusterConfig.connection.caPath, certPath: clusterConfig.connection.certPath, keyPath: clusterConfig.connection.keyPath - } + }, + tempFolder: './c2d_storage/' + hash } super(owerwrite, db) } @@ -112,6 +603,7 @@ export class C2DEngineDockerFree extends C2DEngineDocker { * Returns all cluster's compute environments for a specific chainId. Env's id already contains the cluster hash */ // TO DO C2D - fill consts below + if (!this.docker) return [] const cpuType = '' const currentJobs = 0 const consumerAddress = '' @@ -129,11 +621,49 @@ export class C2DEngineDockerFree extends C2DEngineDocker { maxJobs: 1, consumerAddress, storageExpiry: 600, - maxJobDuration: 60, + maxJobDuration: 30, feeToken: ZeroAddress, free: true } ] return envs } + + public override async startComputeJob( + assets: ComputeAsset[], + algorithm: ComputeAlgorithm, + output: ComputeOutput, + environment: string, + owner?: string, + validUntil?: number, + chainId?: number, + agreementId?: string + ): Promise { + // since it's a free job, we need to mangle some params + agreementId = create256Hash( + JSON.stringify({ + owner, + assets, + algorithm, + time: process.hrtime.bigint().toString() + }) + ) + chainId = 0 + const envs = await this.getComputeEnvironments() + if (envs.length < 1) { + // no free env ?? + throw new Error('No free env found') + } + validUntil = envs[0].maxJobDuration + return await super.startComputeJob( + assets, + algorithm, + output, + environment, + owner, + validUntil, + chainId, + agreementId + ) + } } diff --git a/src/components/core/compute/getStatus.ts b/src/components/core/compute/getStatus.ts index 2bd96bd33..408b603a9 100644 --- a/src/components/core/compute/getStatus.ts +++ b/src/components/core/compute/getStatus.ts @@ -44,9 +44,13 @@ export class ComputeGetStatusHandler extends Handler { // split jobId (which is already in hash-jobId format) and get the hash // then get jobId which might contain dashes as well const index = task.jobId.indexOf('-') - const hash = task.jobId.slice(0, index) - engines = [await this.getOceanNode().getC2DEngines().getC2DByHash(hash)] - jobId = task.jobId.slice(index + 1) + if (index > 0) { + const hash = task.jobId.slice(0, index) + engines = [await this.getOceanNode().getC2DEngines().getC2DByHash(hash)] + jobId = task.jobId.slice(index + 1) + } else { + engines = await this.getOceanNode().getC2DEngines().getAllEngines() + } } else { engines = await this.getOceanNode().getC2DEngines().getAllEngines() } @@ -57,7 +61,9 @@ export class ComputeGetStatusHandler extends Handler { task.agreementId, jobId ) - response.push(...jobs) + console.log('GOT JOBS') + console.log(jobs) + if (jobs && jobs.length > 0) response.push(...jobs) } CORE_LOGGER.logMessage( 'ComputeGetStatusCommand Response: ' + JSON.stringify(response, null, 2), diff --git a/src/components/core/compute/getStreamableLogs.ts b/src/components/core/compute/getStreamableLogs.ts new file mode 100644 index 000000000..8ed10bd53 --- /dev/null +++ b/src/components/core/compute/getStreamableLogs.ts @@ -0,0 +1,88 @@ +import { P2PCommandResponse } from '../../../@types/index.js' +import { CORE_LOGGER } from '../../../utils/logging/common.js' +import { Handler } from '../handler/handler.js' +import { ComputeGetStreamableLogsCommand } from '../../../@types/commands.js' +import { checkNonce, NonceResponse } from '../utils/nonceHandler.js' +import { Stream } from 'stream' +import { + buildInvalidRequestMessage, + validateCommandParameters, + ValidateParams +} from '../../httpRoutes/validateCommands.js' +import { isAddress } from 'ethers' + +export class ComputeGetStreamableLogsHandler extends Handler { + validate(command: ComputeGetStreamableLogsCommand): ValidateParams { + const validation = validateCommandParameters(command, [ + 'consumerAddress', + 'signature', + 'nonce', + 'jobId' + ]) + if (validation.valid) { + if (command.consumerAddress && !isAddress(command.consumerAddress)) { + return buildInvalidRequestMessage( + 'Parameter : "consumerAddress" is not a valid web3 address' + ) + } + } + return validation + } + + async handle(task: ComputeGetStreamableLogsCommand): Promise { + const validationResponse = await this.verifyParamsAndRateLimits(task) + if (this.shouldDenyTaskHandling(validationResponse)) { + return validationResponse + } + + // TO DO: signature message to check + + // split jobId (which is already in hash-jobId format) and get the hash + // then get jobId which might contain dashes as well + const index = task.jobId.indexOf('-') + const hash = task.jobId.slice(0, index) + const jobId = task.jobId.slice(index + 1) + + // env might contain + let engine + try { + engine = await this.getOceanNode().getC2DEngines().getC2DByHash(hash) + } catch (e) { + return { + stream: null, + status: { + httpStatus: 500, + error: 'Invalid C2D Environment' + } + } + } + try { + const respStream = await engine.getStreamableLogs(jobId) + if (!respStream) { + return { + stream: null, + status: { + httpStatus: 404 + } + } + } + const response: P2PCommandResponse = { + stream: respStream as unknown as Stream, + status: { + httpStatus: 200 + } + } + + return response + } catch (error) { + CORE_LOGGER.error(error.message) + return { + stream: null, + status: { + httpStatus: 500, + error: error.message + } + } + } + } +} diff --git a/src/components/core/compute/index.ts b/src/components/core/compute/index.ts index 14d73f5cc..2beb8c621 100644 --- a/src/components/core/compute/index.ts +++ b/src/components/core/compute/index.ts @@ -4,3 +4,4 @@ export * from './stopCompute.js' export * from './getStatus.js' export * from './getResults.js' export * from './initialize.js' +export * from './getStreamableLogs.js' diff --git a/src/components/core/compute/startCompute.ts b/src/components/core/compute/startCompute.ts index ff04103dd..66588be47 100644 --- a/src/components/core/compute/startCompute.ts +++ b/src/components/core/compute/startCompute.ts @@ -358,7 +358,20 @@ export class ComputeStartHandler extends Handler { // - has no validation export class FreeComputeStartHandler extends Handler { validate(command: ComputeStartCommand): ValidateParams { - const commandValidation = validateCommandParameters(command, ['algorithm']) + const commandValidation = validateCommandParameters(command, [ + 'algorithm', + 'datasets', + 'consumerAddress', + 'signature', + 'nonce' + ]) + if (commandValidation.valid) { + if (!isAddress(command.consumerAddress)) { + return buildInvalidRequestMessage( + 'Parameter : "consumerAddress" is not a valid web3 address' + ) + } + } return commandValidation } @@ -391,7 +404,8 @@ export class FreeComputeStartHandler extends Handler { task.datasets, task.algorithm, task.output, - environment.id + environment.id, + task.consumerAddress ) CORE_LOGGER.logMessage( diff --git a/src/components/core/handler/coreHandlersRegistry.ts b/src/components/core/handler/coreHandlersRegistry.ts index 1600a314b..2fbc304ae 100644 --- a/src/components/core/handler/coreHandlersRegistry.ts +++ b/src/components/core/handler/coreHandlersRegistry.ts @@ -28,7 +28,8 @@ import { ComputeStopHandler, ComputeGetStatusHandler, ComputeGetResultHandler, - ComputeInitializeHandler + ComputeInitializeHandler, + ComputeGetStreamableLogsHandler } from '../compute/index.js' import { StopNodeHandler } from '../admin/stopNodeHandler.js' import { ReindexTxHandler } from '../admin/reindexTxHandler.js' @@ -115,6 +116,10 @@ export class CoreHandlersRegistry { PROTOCOL_COMMANDS.COMPUTE_GET_RESULT, new ComputeGetResultHandler(node) ) + this.registerCoreHandler( + PROTOCOL_COMMANDS.COMPUTE_GET_STREAMABLE_LOGS, + new ComputeGetStreamableLogsHandler(node) + ) this.registerCoreHandler( PROTOCOL_COMMANDS.COMPUTE_INITIALIZE, new ComputeInitializeHandler(node) diff --git a/src/components/core/handler/statusHandler.ts b/src/components/core/handler/statusHandler.ts index fe61963ec..c314dd924 100644 --- a/src/components/core/handler/statusHandler.ts +++ b/src/components/core/handler/statusHandler.ts @@ -14,16 +14,13 @@ export class StatusHandler extends Handler { return validateCommandParameters(command, []) } - async handle( - task: StatusCommand, - detailed: boolean = false - ): Promise { + async handle(task: StatusCommand): Promise { const checks = await this.verifyParamsAndRateLimits(task) if (checks.status.httpStatus !== 200 || checks.status.error !== null) { return checks } try { - const statusResult = await status(this.getOceanNode(), task.node, detailed) + const statusResult = await status(this.getOceanNode(), task.node, !!task.detailed) if (!statusResult) { return { stream: null, @@ -50,6 +47,7 @@ export class DetailedStatusHandler extends StatusHandler { } async handle(task: StatusCommand): Promise { - return await super.handle(task, true) + task.detailed = true + return await super.handle(task) } } diff --git a/src/components/core/utils/statusHandler.ts b/src/components/core/utils/statusHandler.ts index 4dbe9463c..e374b0a1a 100644 --- a/src/components/core/utils/statusHandler.ts +++ b/src/components/core/utils/statusHandler.ts @@ -151,7 +151,16 @@ export async function status( // depends on request if (detailed) { - nodeStatus.c2dClusters = config.c2dClusters + nodeStatus.c2dClusters = [] + const engines = await oceanNode.getC2DEngines().getAllEngines() + for (const engine of engines) { + const type = await engine.getC2DType() + nodeStatus.c2dClusters.push({ + type, + hash: await engine.getC2DConfig().hash, + environments: await engine.getComputeEnvironments() + }) + } nodeStatus.supportedSchemas = schemas.ddoSchemas } return nodeStatus diff --git a/src/components/database/index.ts b/src/components/database/index.ts index 940c8b087..c611dd866 100644 --- a/src/components/database/index.ts +++ b/src/components/database/index.ts @@ -1062,11 +1062,12 @@ export class LogDatabase { export class C2DDatabase { private provider: Typesense | SQLiteCompute - + private tempMem: DBComputeJob[] constructor( private config: OceanNodeDBConfig, private schema: Schema ) { + this.tempMem = [] return (async (): Promise => { if (this.config.url && URLUtils.isValidUrl(this.config.url)) { try { @@ -1105,25 +1106,40 @@ export class C2DDatabase { // eslint-disable-next-line require-await async newJob(job: DBComputeJob): Promise { // TO DO C2D - return '' + this.tempMem.push(job) + return job.agreementId } // eslint-disable-next-line require-await async getJob(jobId: string): Promise { + console.log('GetJob jobId:' + jobId) + console.log(this.tempMem) + for (const i in this.tempMem) { + if (this.tempMem[i].jobId === jobId) return this.tempMem[i] + } return null } + // eslint-disable-next-line require-await async updateJob(job: DBComputeJob) { // TO DO C2D + for (const i in this.tempMem) { + if (this.tempMem[i].jobId === job.jobId) this.tempMem[i] = job + } } // eslint-disable-next-line require-await - async getRunningJobs( - engine?: string, - environment?: string - ): Promise { + async getRunningJobs(engine?: string, environment?: string): Promise { // TO DO C2D - return null + const runningJobs: DBComputeJob[] = [] + for (const i in this.tempMem) { + if (this.tempMem[i].isRunning === true) { + if (engine && this.tempMem[i].clusterHash !== engine) continue + if (environment && this.tempMem[i].environment !== environment) continue + runningJobs.push(this.tempMem[i]) + } + } + return runningJobs } } diff --git a/src/components/database/sqliteCompute.ts b/src/components/database/sqliteCompute.ts index b5f687bfe..c654c444f 100644 --- a/src/components/database/sqliteCompute.ts +++ b/src/components/database/sqliteCompute.ts @@ -6,7 +6,7 @@ interface ComputeDatabaseProvider { newJob(job: DBComputeJob): Promise getJob(jobId: string): Promise updateJob(job: DBComputeJob): void - getRunningJobs(engine?: string, environment?: string): Promise + getRunningJobs(engine?: string, environment?: string): Promise } export class SQLiteCompute implements ComputeDatabaseProvider { @@ -35,16 +35,14 @@ export class SQLiteCompute implements ComputeDatabaseProvider { return null } + // eslint-disable-next-line require-await async updateJob(job: DBComputeJob) { // TO DO C2D } // eslint-disable-next-line require-await - async getRunningJobs( - engine?: string, - environment?: string - ): Promise { + async getRunningJobs(engine?: string, environment?: string): Promise { // TO DO C2D - return null + return [] } } diff --git a/src/components/httpRoutes/compute.ts b/src/components/httpRoutes/compute.ts index 514bff465..76c95d64c 100644 --- a/src/components/httpRoutes/compute.ts +++ b/src/components/httpRoutes/compute.ts @@ -6,7 +6,8 @@ import { ComputeStopHandler, ComputeGetStatusHandler, ComputeGetResultHandler, - ComputeInitializeHandler + ComputeInitializeHandler, + ComputeGetStreamableLogsHandler } from '../core/compute/index.js' import type { ComputeAlgorithm, @@ -18,7 +19,8 @@ import type { FreeComputeStartCommand, ComputeStopCommand, ComputeGetResultCommand, - ComputeGetStatusCommand + ComputeGetStatusCommand, + ComputeGetStreamableLogsCommand } from '../../@types/commands.js' import { streamToObject, streamToString } from '../../utils/util.js' @@ -81,6 +83,7 @@ computeRoutes.get(`${SERVICES_API_BASE_PATH}/computeEnvironments`, async (req, r } }) +// start compute computeRoutes.post(`${SERVICES_API_BASE_PATH}/compute`, async (req, res) => { try { HTTP_LOGGER.logMessage( @@ -96,7 +99,7 @@ computeRoutes.post(`${SERVICES_API_BASE_PATH}/compute`, async (req, res) => { nonce: (req.body.nonce as string) || null, environment: (req.body.environment as string) || null, algorithm: (req.body.algorithm as ComputeAlgorithm) || null, - datasets: (req.body.dataset as unknown as ComputeAsset[]) || null + datasets: (req.body.datasets as unknown as ComputeAsset[]) || null } if (req.body.output) { startComputeTask.output = req.body.output as ComputeOutput @@ -129,8 +132,11 @@ computeRoutes.post(`${SERVICES_API_BASE_PATH}/freeCompute`, async (req, res) => const startComputeTask: FreeComputeStartCommand = { command: PROTOCOL_COMMANDS.FREE_COMPUTE_START, node: (req.body.node as string) || null, + consumerAddress: (req.body.consumerAddress as string) || null, + signature: (req.body.signature as string) || null, + nonce: (req.body.nonce as string) || null, algorithm: (req.body.algorithm as ComputeAlgorithm) || null, - datasets: (req.body.dataset as unknown as ComputeAsset[]) || null + datasets: (req.body.datasets as unknown as ComputeAsset[]) || null } if (req.body.output) { startComputeTask.output = req.body.output as ComputeOutput @@ -237,6 +243,41 @@ computeRoutes.get(`${SERVICES_API_BASE_PATH}/computeResult`, async (req, res) => res.status(500).send('Internal Server Error') } }) + +// streaming logs +computeRoutes.get(`${SERVICES_API_BASE_PATH}/computeStreamableLogs`, async (req, res) => { + try { + HTTP_LOGGER.logMessage( + `ComputeGetStreamableLogsCommand request received with query: ${JSON.stringify( + req.query + )}`, + true + ) + const resultComputeTask: ComputeGetStreamableLogsCommand = { + command: PROTOCOL_COMMANDS.COMPUTE_GET_RESULT, + node: (req.query.node as string) || null, + consumerAddress: (req.query.consumerAddress as string) || null, + jobId: (req.query.jobId as string) || null, + signature: (req.query.signature as string) || null, + nonce: (req.query.nonce as string) || null + } + + const response = await new ComputeGetStreamableLogsHandler(req.oceanNode).handle( + resultComputeTask + ) + if (response.stream) { + res.status(response.status.httpStatus) + res.set(response.status.headers) + response.stream.pipe(res) + } else { + res.status(response.status.httpStatus).send(response.status.error) + } + } catch (error) { + HTTP_LOGGER.log(LOG_LEVELS_STR.LEVEL_ERROR, `Error: ${error}`) + res.status(500).send('Internal Server Error') + } +}) + computeRoutes.post(`${SERVICES_API_BASE_PATH}/initializeCompute`, async (req, res) => { try { HTTP_LOGGER.logMessage( diff --git a/src/components/storage/index.ts b/src/components/storage/index.ts index 90ddfd866..53fb0e5cf 100644 --- a/src/components/storage/index.ts +++ b/src/components/storage/index.ts @@ -77,6 +77,11 @@ export abstract class Storage { } } + getStorageType(file: any): FileObjectType { + const { type } = file + return type + } + async getFileInfo( fileInfoRequest: FileInfoRequest, forceChecksum: boolean = false diff --git a/src/utils/config.ts b/src/utils/config.ts index b1c0f986f..13b28ecef 100644 --- a/src/utils/config.ts +++ b/src/utils/config.ts @@ -338,10 +338,12 @@ function getC2DClusterEnvironment(isStartup?: boolean): C2DClusterInfo[] { environments: getEnvValue(process.env.DOCKER_COMPUTE_ENVIRONMENTS, null) } if (dockerConfig.socketPath || dockerConfig.host) { + const hash = create256Hash(JSON.stringify(dockerConfig)) clusters.push({ connection: dockerConfig, - hash: create256Hash(JSON.stringify(dockerConfig)), - type: C2DClusterType.DOCKER + hash, + type: C2DClusterType.DOCKER, + tempFolder: './c2d_storage/' + hash }) } diff --git a/src/utils/constants.ts b/src/utils/constants.ts index a564bacd1..26140fdf3 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -22,6 +22,7 @@ export const PROTOCOL_COMMANDS = { FREE_COMPUTE_START: 'freeStartCompute', COMPUTE_STOP: 'stopCompute', COMPUTE_GET_STATUS: 'getComputeStatus', + COMPUTE_GET_STREAMABLE_LOGS: 'getComputeStreamableLogs', COMPUTE_GET_RESULT: 'getComputeResult', COMPUTE_INITIALIZE: 'initializeCompute', STOP_NODE: 'stopNode', @@ -53,6 +54,7 @@ export const SUPPORTED_PROTOCOL_COMMANDS: string[] = [ PROTOCOL_COMMANDS.COMPUTE_STOP, PROTOCOL_COMMANDS.COMPUTE_GET_STATUS, PROTOCOL_COMMANDS.COMPUTE_GET_RESULT, + PROTOCOL_COMMANDS.COMPUTE_GET_STREAMABLE_LOGS, PROTOCOL_COMMANDS.COMPUTE_INITIALIZE, PROTOCOL_COMMANDS.STOP_NODE, PROTOCOL_COMMANDS.REINDEX_TX, From b85325cf5f44fc549760940aeff61eb2d4caa1de Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Sun, 6 Oct 2024 22:42:43 +0300 Subject: [PATCH 08/30] small fixes --- src/components/c2d/compute_engine_docker.ts | 6 ++++-- src/components/core/compute/getStreamableLogs.ts | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/components/c2d/compute_engine_docker.ts b/src/components/c2d/compute_engine_docker.ts index 2a085d077..3eb56be5e 100644 --- a/src/components/c2d/compute_engine_docker.ts +++ b/src/components/c2d/compute_engine_docker.ts @@ -174,8 +174,11 @@ export class C2DEngineDocker extends C2DEngine { public override async getStreamableLogs(jobId: string): Promise { const job = await this.db.getJob(jobId) if (!job) return null + if (!job.isRunning) return null try { const container = await this.docker.getContainer(job.jobId + '-algoritm') + const details = await container.inspect() + if (details.State.Running === false) return null return await container.logs({ stdout: true, stderr: true, @@ -371,9 +374,8 @@ export class C2DEngineDocker extends C2DEngine { if (details.State.Running === true) { try { await container.stop() - do {} while ((await container.inspect()).State.Running === true) } catch (e) { - // we should never reach this, unless the container is already stopped + // we should never reach this, unless the container is already stopped or deleted by someone else console.log(e) } } diff --git a/src/components/core/compute/getStreamableLogs.ts b/src/components/core/compute/getStreamableLogs.ts index 8ed10bd53..9b46e6ad2 100644 --- a/src/components/core/compute/getStreamableLogs.ts +++ b/src/components/core/compute/getStreamableLogs.ts @@ -2,7 +2,7 @@ import { P2PCommandResponse } from '../../../@types/index.js' import { CORE_LOGGER } from '../../../utils/logging/common.js' import { Handler } from '../handler/handler.js' import { ComputeGetStreamableLogsCommand } from '../../../@types/commands.js' -import { checkNonce, NonceResponse } from '../utils/nonceHandler.js' +// import { checkNonce, NonceResponse } from '../utils/nonceHandler.js' import { Stream } from 'stream' import { buildInvalidRequestMessage, From c3887aab60a65592c49cb06b3fbe1d63e34efb9b Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Sun, 6 Oct 2024 22:55:01 +0300 Subject: [PATCH 09/30] update deps --- package-lock.json | 1315 ++++++++++++++++++++++++++++++++++++++++++++- package.json | 4 + 2 files changed, 1294 insertions(+), 25 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4d7408326..c6b0e31d4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -49,6 +49,8 @@ "base58-js": "^2.0.0", "cors": "^2.8.5", "delay": "^5.0.0", + "docker-registry-client": "^3.4.0", + "dockerode": "^4.0.2", "dotenv": "^16.3.1", "eciesjs": "^0.4.5", "eth-crypto": "^2.6.0", @@ -70,6 +72,7 @@ "sinon": "^17.0.1", "sqlite3": "^5.1.7", "stream-concat": "^1.0.0", + "tar": "^7.4.3", "ts-node": "^10.9.1", "tsoa": "^5.1.1", "uint8arrays": "^4.0.6", @@ -81,6 +84,7 @@ "devDependencies": { "@types/chai": "^4.3.10", "@types/cors": "^2.8.17", + "@types/dockerode": "^3.3.31", "@types/express": "^4.17.17", "@types/ip": "^1.1.3", "@types/lzma-native": "^4.0.4", @@ -1314,6 +1318,12 @@ "node": ">=6.9.0" } }, + "node_modules/@balena/dockerignore": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@balena/dockerignore/-/dockerignore-1.0.2.tgz", + "integrity": "sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q==", + "license": "Apache-2.0" + }, "node_modules/@bcoe/v8-coverage": { "version": "0.2.3", "license": "MIT" @@ -2385,6 +2395,114 @@ "node": ">=18" } }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@isaacs/fs-minipass/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "license": "ISC", @@ -4061,6 +4179,15 @@ "typescript": "^3 || ^4" } }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "optional": true, + "engines": { + "node": ">=14" + } + }, "node_modules/@pkgr/utils": { "version": "2.4.2", "dev": true, @@ -5262,6 +5389,29 @@ "@types/node": "*" } }, + "node_modules/@types/docker-modem": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/docker-modem/-/docker-modem-3.0.6.tgz", + "integrity": "sha512-yKpAGEuKRSS8wwx0joknWxsmLha78wNMe9R2S3UNsVOkZded8UqOrV8KoeDXoXsjndxwyF3eIhyClGbO1SEhEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/ssh2": "*" + } + }, + "node_modules/@types/dockerode": { + "version": "3.3.31", + "resolved": "https://registry.npmjs.org/@types/dockerode/-/dockerode-3.3.31.tgz", + "integrity": "sha512-42R9eoVqJDSvVspV89g7RwRqfNExgievLNWoHkg7NoWIqAmavIbgQBb4oc0qRtHkxE+I3Xxvqv7qVXFABKPBTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/docker-modem": "*", + "@types/node": "*", + "@types/ssh2": "*" + } + }, "node_modules/@types/express": { "version": "4.17.20", "license": "MIT", @@ -5713,6 +5863,26 @@ "version": "8.1.4", "license": "MIT" }, + "node_modules/@types/ssh2": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/@types/ssh2/-/ssh2-1.15.1.tgz", + "integrity": "sha512-ZIbEqKAsi5gj35y4P4vkJYly642wIbY6PqoN0xiyQGshKUGXR9WQjF/iF9mXBQ8uBKy3ezfsCkcoHKhd0BzuDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "^18.11.18" + } + }, + "node_modules/@types/ssh2/node_modules/@types/node": { + "version": "18.19.54", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.54.tgz", + "integrity": "sha512-+BRgt0G5gYjTvdLac9sIeE0iZcJxi4Jc4PV5EUzqi+88jmQLr+fRZdv2tCTV7IHKSGxM6SaLoOXQWWUiLUItMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" + } + }, "node_modules/@types/triple-beam": { "version": "1.3.4", "license": "MIT" @@ -7264,6 +7434,41 @@ "node": ">=0.10.0" } }, + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/asn1.js": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-2.2.1.tgz", + "integrity": "sha512-x1HxYATfjnV+SrtHZR9rxzRvTgZaGAtT/nJB3TPmBxtoEVQVRPArNSzCA+1fVYlHYV/zmMLUJhtZVRcn7WMjfQ==", + "license": "MIT", + "dependencies": { + "bn.js": "^2.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/asn1.js-rfc3280": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/asn1.js-rfc3280/-/asn1.js-rfc3280-2.1.1.tgz", + "integrity": "sha512-/mwtgTbv+xElp8rAw0YPzPoBp4PkV2gl/TRHt9KuK7ZyQXWnTeclQpDJnKZlxCluKr5WAc9tO1NBArLx1egZJQ==", + "license": "MIT", + "peerDependencies": { + "asn1.js": "^2.0.0" + } + }, + "node_modules/asn1.js/node_modules/bn.js": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-2.2.0.tgz", + "integrity": "sha512-nlotTGN6qr+NpeCb8d5mdXR47r6GXiyoX4fEeqBF2u9wp/3XgzIwyftMX9TE+StQRJSOUJtyYr9MVk0rn2ftAg==", + "license": "MIT" + }, "node_modules/asn1js": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.0.5.tgz", @@ -7277,6 +7482,14 @@ "node": ">=12.0.0" } }, + "node_modules/assert-plus": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.5.tgz", + "integrity": "sha512-brU24g7ryhRwGCI2y+1dGQmQXiZF7TtIj583S96y0jjdajIe6wn8BuXyELYhvD22dtIxDQVFk04YTJwwdwOYJw==", + "engines": { + "node": ">=0.8" + } + }, "node_modules/assertion-error": { "version": "1.1.0", "license": "MIT", @@ -7437,6 +7650,18 @@ "version": "0.4.4", "license": "MIT" }, + "node_modules/backoff": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/backoff/-/backoff-2.5.0.tgz", + "integrity": "sha512-wC5ihrnUXmR2douXmXLCe5O3zg3GKIyvRi/hi58a/XyRxVI+3/yM0PYueQOZXPXQ9pxBislYkw+sF9b7C/RuMA==", + "license": "MIT", + "dependencies": { + "precond": "0.2" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "license": "MIT" @@ -7473,6 +7698,100 @@ ], "license": "MIT" }, + "node_modules/base64url": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/base64url/-/base64url-1.0.6.tgz", + "integrity": "sha512-YJUNcKuU8Df1LhS3s9OzoYCAOZYHgAUGnDlPgXFCaJZwRzZLcnQ7uM9KRY6EFaJRvzxZqw2w+wCDigwpe+4XUw==", + "license": "MIT", + "dependencies": { + "concat-stream": "~1.4.7", + "meow": "~2.0.0" + }, + "bin": { + "base64url": "bin/base64url" + } + }, + "node_modules/base64url/node_modules/camelcase": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha512-wzLkDa4K/mzI1OSITC+DUyjgIl/ETNHE9QvYgy6J6Jvqyyz4C0Xfd+lQhb19sX2jMpZV4IssUn0VDVmglV+s4g==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base64url/node_modules/camelcase-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-1.0.0.tgz", + "integrity": "sha512-hwNYKTjJTlDabjJp2xn0h8bRmOpObvXVgYbQmR+Xob/EeBDtYea3xttjr5hqiWqLWtI3/6xO7x1ZAktQ9up+ag==", + "license": "MIT", + "dependencies": { + "camelcase": "^1.0.1", + "map-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base64url/node_modules/get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha512-F5aQMywwJ2n85s4hJPTT9RPxGmubonuB10MNYo17/xph174n2MIR33HRguhzVag10O/npM7SPk73LMZNP+FaWw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base64url/node_modules/indent-string": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-1.2.2.tgz", + "integrity": "sha512-Z1vqf6lDC3f4N2mWqRywY6odjRatPNGDZgUr4DY9MLC14+Fp2/y+CI/RnNGlb8hD6ckscE/8DlZUwHUaiDBshg==", + "license": "MIT", + "dependencies": { + "get-stdin": "^4.0.1", + "minimist": "^1.1.0", + "repeating": "^1.1.0" + }, + "bin": { + "indent-string": "cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base64url/node_modules/map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base64url/node_modules/meow": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-2.0.0.tgz", + "integrity": "sha512-X7rkdgy5Wxxp2MhCiAOkC3lqfkrJkt3iXvW4BY0rYQIn3GMvYvBTsAPEmHHTjTeVzBelrRcQa2F80rYfigz2+A==", + "license": "MIT", + "dependencies": { + "camelcase-keys": "^1.0.0", + "indent-string": "^1.1.0", + "minimist": "^1.1.0", + "object-assign": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base64url/node_modules/object-assign": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-1.0.0.tgz", + "integrity": "sha512-LpUkixU1BUMQ6bwUHbOue4IGGbdRbxi+IEZw7zHniw78erlxrKGHbhfLbHIsI35LGbGqys6QOrjVmLnD2ie+1A==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/basic-ftp": { "version": "5.0.5", "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz", @@ -7482,6 +7801,15 @@ "node": ">=10.0.0" } }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "license": "BSD-3-Clause", + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, "node_modules/bech32": { "version": "1.1.4", "license": "MIT" @@ -7832,6 +8160,12 @@ "node": "*" } }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, "node_modules/buffer-from": { "version": "1.1.2", "license": "MIT" @@ -7853,6 +8187,15 @@ "node": ">=6.14.2" } }, + "node_modules/buildcheck": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/buildcheck/-/buildcheck-0.0.6.tgz", + "integrity": "sha512-8f9ZJCUXyT1M35Jx7MkBgmBMo3oHTTBIPLiY9xyL0pl3T5RwcPEY8cUHr5LBNfu/fk6c2T4DJZuVM/8ZZT2D2A==", + "optional": true, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/builtins": { "version": "5.0.1", "license": "MIT", @@ -7874,6 +8217,24 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/bunyan": { + "version": "1.8.15", + "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.15.tgz", + "integrity": "sha512-0tECWShh6wUysgucJcBAoYegf3JJoZWibxdqhTm7OHPeT42qdjkZ29QCMcKwbgU1kiH+auSIasNRXMLWXafXig==", + "engines": [ + "node >=0.10.0" + ], + "license": "MIT", + "bin": { + "bunyan": "bin/bunyan" + }, + "optionalDependencies": { + "dtrace-provider": "~0.8", + "moment": "^2.19.3", + "mv": "~2", + "safe-json-stringify": "~1" + } + }, "node_modules/bytes": { "version": "3.1.2", "license": "MIT", @@ -8015,6 +8376,32 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/cacache/node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "optional": true, + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cacache/node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "optional": true, + "engines": { + "node": ">=8" + } + }, "node_modules/cacheable-lookup": { "version": "5.0.4", "license": "MIT", @@ -8595,20 +8982,58 @@ "version": "0.0.1", "license": "MIT" }, - "node_modules/concurrently": { - "version": "8.2.2", - "dev": true, + "node_modules/concat-stream": { + "version": "1.4.11", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.4.11.tgz", + "integrity": "sha512-X3JMh8+4je3U1cQpG87+f9lXHDrqcb2MVLg9L7o8b1UZ0DzhRrUpdn65ttzu10PpJPPI3MQNkis+oha6TSA9Mw==", + "engines": [ + "node >= 0.8" + ], "license": "MIT", "dependencies": { - "chalk": "^4.1.2", - "date-fns": "^2.30.0", - "lodash": "^4.17.21", - "rxjs": "^7.8.1", - "shell-quote": "^1.8.1", - "spawn-command": "0.0.2", - "supports-color": "^8.1.1", - "tree-kill": "^1.2.2", - "yargs": "^17.7.2" + "inherits": "~2.0.1", + "readable-stream": "~1.1.9", + "typedarray": "~0.0.5" + } + }, + "node_modules/concat-stream/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "license": "MIT" + }, + "node_modules/concat-stream/node_modules/readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/concat-stream/node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", + "license": "MIT" + }, + "node_modules/concurrently": { + "version": "8.2.2", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.2", + "date-fns": "^2.30.0", + "lodash": "^4.17.21", + "rxjs": "^7.8.1", + "shell-quote": "^1.8.1", + "spawn-command": "0.0.2", + "supports-color": "^8.1.1", + "tree-kill": "^1.2.2", + "yargs": "^17.7.2" }, "bin": { "conc": "dist/bin/concurrently.js", @@ -8877,6 +9302,27 @@ "node": ">=8" } }, + "node_modules/cpu-features": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.10.tgz", + "integrity": "sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "buildcheck": "~0.0.6", + "nan": "^2.19.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/cpu-features/node_modules/nan": { + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.20.0.tgz", + "integrity": "sha512-bk3gXBZDGILuuo/6sKtr0DQmSThYHLtNCdSdXk9YkxD/jK6X2vmCyyXBBxyqZ4XcnzTyYEAThfX3DCEnLf6igw==", + "license": "MIT", + "optional": true + }, "node_modules/cpy": { "version": "9.0.1", "license": "MIT", @@ -10055,6 +10501,90 @@ "node": ">=6" } }, + "node_modules/docker-modem": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-5.0.3.tgz", + "integrity": "sha512-89zhop5YVhcPEt5FpUFGr3cDyceGhq/F9J+ZndQ4KfqNvfbJpPMfgeixFgUj5OjCYAboElqODxY5Z1EBsSa6sg==", + "license": "Apache-2.0", + "dependencies": { + "debug": "^4.1.1", + "readable-stream": "^3.5.0", + "split-ca": "^1.0.1", + "ssh2": "^1.15.0" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/docker-modem/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/docker-registry-client": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/docker-registry-client/-/docker-registry-client-3.4.0.tgz", + "integrity": "sha512-meDdmo1fa3r4D6WLSPb0CcRBpZs+egIHD8x+fydqAvsTomoUgMbcZDD77bVpBdkhwj+lDZUEWgvMCvAbcDBH4g==", + "license": "MPL-2.0", + "dependencies": { + "assert-plus": "^0.1.5", + "base64url": "1.x >=1.0.4", + "bunyan": "1.x >=1.3.3", + "jwk-to-pem": "1.2.0", + "jws": "3.1.0", + "restify-clients": "^1.4.0", + "restify-errors": "^3.0.0", + "strsplit": "1.x", + "tough-cookie": "2.0.x", + "vasync": "1.x >=1.6.1", + "verror": "1.x >=1.6.0", + "www-authenticate": "0.6.x >=0.6.2" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/dockerode": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/dockerode/-/dockerode-4.0.2.tgz", + "integrity": "sha512-9wM1BVpVMFr2Pw3eJNXrYYt6DT9k0xMcsSCjtPvyQ+xa1iPg/Mo3T/gUcwI0B2cczqCeCYRPF8yFYDwtFXT0+w==", + "license": "Apache-2.0", + "dependencies": { + "@balena/dockerignore": "^1.0.2", + "docker-modem": "^5.0.3", + "tar-fs": "~2.0.1" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/dockerode/node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "license": "ISC" + }, + "node_modules/dockerode/node_modules/tar-fs": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.0.1.tgz", + "integrity": "sha512-6tzWDMeroL87uF/+lin46k+Q+46rAJ0SyPGz7OW7wTgblI273hsBqk2C1j0/xNadNLKDTUL9BukSjB7cwgmlPA==", + "license": "MIT", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.0.0" + } + }, "node_modules/doctrine": { "version": "3.0.0", "license": "Apache-2.0", @@ -10108,6 +10638,20 @@ "node": ">=0.10" } }, + "node_modules/dtrace-provider": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.8.tgz", + "integrity": "sha512-b7Z7cNtHPhH9EJhNNbbeqTcXB8LGFFZhq1PGgEvpeHlzd36bhbdTWoE/Ba/YguqpBSlAPKnARWhVlhunCMwfxg==", + "hasInstallScript": true, + "license": "BSD-2-Clause", + "optional": true, + "dependencies": { + "nan": "^2.14.0" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/duplex-to": { "version": "2.0.0", "license": "MIT" @@ -10166,6 +10710,15 @@ "node": ">=4.0.0" } }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, "node_modules/eciesjs": { "version": "0.4.5", "license": "MIT", @@ -12384,6 +12937,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/extsprintf": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz", + "integrity": "sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==", + "engines": [ + "node >=0.6.0" + ], + "license": "MIT" + }, "node_modules/eyes": { "version": "0.1.8", "engines": { @@ -12432,6 +12994,12 @@ "version": "2.0.6", "license": "MIT" }, + "node_modules/fast-safe-stringify": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-1.2.3.tgz", + "integrity": "sha512-QJYT/i0QYoiZBQ71ivxdyTqkwKkQ0oxACXHYxH2zYHJEgzi2LsbjgvtzTbLi1SZcF190Db2YP7I7eTsU2egOlw==", + "license": "MIT" + }, "node_modules/fastq": { "version": "1.15.0", "license": "ISC", @@ -14318,6 +14886,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-finite": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", + "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-fullwidth-code-point": { "version": "1.0.0", "license": "MIT", @@ -15138,6 +15718,20 @@ "set-function-name": "^2.0.1" } }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, "node_modules/java-properties": { "version": "1.0.2", "license": "MIT", @@ -15353,6 +15947,56 @@ "version": "4.2.1", "license": "MIT" }, + "node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jwk-to-pem": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/jwk-to-pem/-/jwk-to-pem-1.2.0.tgz", + "integrity": "sha512-c/RUawllCF0Qfp+oJ2cm8DaeiIP7b2V1xljEY3mibpkRsF3RoL2ELW33D/VESwcrbLluMQYgOVSp9i/OlJa1gg==", + "license": "Apache-2.0", + "dependencies": { + "asn1.js": "^2.2.0", + "asn1.js-rfc3280": "^2.1.0", + "elliptic": "^3.0.4" + } + }, + "node_modules/jwk-to-pem/node_modules/bn.js": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-2.2.0.tgz", + "integrity": "sha512-nlotTGN6qr+NpeCb8d5mdXR47r6GXiyoX4fEeqBF2u9wp/3XgzIwyftMX9TE+StQRJSOUJtyYr9MVk0rn2ftAg==", + "license": "MIT" + }, + "node_modules/jwk-to-pem/node_modules/elliptic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-3.1.0.tgz", + "integrity": "sha512-kjzyQvz5tdIrz+O8EAaDU5oeICcg5mMevSFEEi/cprAl1GID1BoV/1tpRu56rDJ6tiXM2b+ZKh3mNrVhA3Y/2Q==", + "license": "MIT", + "dependencies": { + "bn.js": "^2.0.3", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "inherits": "^2.0.1" + } + }, + "node_modules/jws": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.1.0.tgz", + "integrity": "sha512-dIhjVxxfhs93IKornyqxfkx/H/fupqwrUzXAXu/zMkgnPyGH0qXKVtet0Fu7I7o0BlV3SDUkAKOCHpzPItPOoQ==", + "license": "MIT", + "dependencies": { + "base64url": "~1.0.4", + "jwa": "^1.1.0" + } + }, "node_modules/keccak": { "version": "3.0.4", "hasInstallScript": true, @@ -15378,6 +16022,12 @@ "node": ">= 6" } }, + "node_modules/keep-alive-agent": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/keep-alive-agent/-/keep-alive-agent-0.0.1.tgz", + "integrity": "sha512-fF6aj9/XFwJiE/4zihw/ZdXg+KeyU4nFvmutF+PkAVadSGqP298+Zm6IzWFzgeDBgvLk3o8boBxNtd1g5Kdjfg==", + "license": "MIT" + }, "node_modules/keyv": { "version": "4.5.4", "license": "MIT", @@ -17652,6 +18302,66 @@ "version": "0.0.8", "license": "ISC" }, + "node_modules/mv": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", + "integrity": "sha512-at/ZndSy3xEGJ8i0ygALh8ru9qy7gWW1cmkaqBN29JmMlIvM//MEO9y1sk/avxuwnPcfhkejkLsuPxH81BrkSg==", + "license": "MIT", + "optional": true, + "dependencies": { + "mkdirp": "~0.5.1", + "ncp": "~2.0.0", + "rimraf": "~2.4.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/mv/node_modules/glob": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", + "integrity": "sha512-MKZeRNyYZAVVVG1oZeLaWie1uweH40m9AZwIwxyPbTSX4hHrVYSzLg0Ro5Z5R7XKkIX+Cc6oD1rqeDJnwsB8/A==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", + "optional": true, + "dependencies": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mv/node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "license": "MIT", + "optional": true, + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/mv/node_modules/rimraf": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", + "integrity": "sha512-J5xnxTyqaiw06JjMftq7L9ouA448dw/E7dKghkP9WpKNuwmARNNg+Gk8/u5ryb9N/Yo2+z3MCwuqFK/+qPOPfQ==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "license": "ISC", + "optional": true, + "dependencies": { + "glob": "^6.0.1" + }, + "bin": { + "rimraf": "bin.js" + } + }, "node_modules/n3": { "version": "1.17.2", "license": "MIT", @@ -17724,6 +18434,16 @@ "version": "1.4.0", "license": "MIT" }, + "node_modules/ncp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", + "integrity": "sha512-zIdGUrPRFTUELUvr3Gmc7KZ2Sw/h1PiVM0Af/oHB6zgnV1ikqSfRk+TOufi79aHYCW3NiOXmr1BP5nWbzojLaA==", + "license": "MIT", + "optional": true, + "bin": { + "ncp": "bin/ncp" + } + }, "node_modules/negotiator": { "version": "0.6.3", "license": "MIT", @@ -17938,6 +18658,32 @@ "node": ">=6" } }, + "node_modules/node-gyp/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/node-gyp/node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "optional": true, + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/node-preload": { "version": "0.2.1", "license": "MIT", @@ -21508,6 +22254,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==" + }, "node_modules/package-json/node_modules/@sindresorhus/is": { "version": "5.6.0", "license": "MIT", @@ -21875,6 +22626,34 @@ "version": "1.0.7", "license": "MIT" }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==" + }, + "node_modules/path-scurry/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/path-to-regexp": { "version": "0.1.7", "license": "MIT" @@ -22505,6 +23284,14 @@ "node": ">=6.0" } }, + "node_modules/precond": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/precond/-/precond-0.2.3.tgz", + "integrity": "sha512-QCYG84SgGyGzqJ/vlMsxeXd/pgL/I94ixdNFyh1PusWmTCyVfPJjZ1K1jvHtsbfnXQs2TSkEP2fR7QiMZAnKFQ==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "license": "MIT", @@ -24864,6 +25651,21 @@ "node": ">=4" } }, + "node_modules/repeating": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-1.1.3.tgz", + "integrity": "sha512-Nh30JLeMHdoI+AsQ5eblhZ7YlTsM9wiJQe/AHIunlK3KWzvXhXb36IJ7K1IOeRjIOtzMjdUHjwXUFxKJoPTSOg==", + "license": "MIT", + "dependencies": { + "is-finite": "^1.0.0" + }, + "bin": { + "repeating": "cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/require-directory": { "version": "2.1.1", "license": "MIT", @@ -24933,6 +25735,112 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/restify-clients": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/restify-clients/-/restify-clients-1.6.0.tgz", + "integrity": "sha512-q5kF/KHkwC10PhEjZkgQnWCIVCq5rlKF+fbqjl51e28ArkztJNI5czFzwCd/4Qz3HRrfwidk1XcAKLxY75dT6w==", + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0", + "backoff": "^2.4.1", + "bunyan": "^1.8.3", + "fast-safe-stringify": "^1.1.3", + "keep-alive-agent": "0.0.1", + "lodash": "^4.7.0", + "lru-cache": "^4.0.1", + "mime": "^1.3.4", + "once": "^1.3.2", + "restify-errors": "^3.1.0", + "semver": "^5.0.1", + "tunnel-agent": "^0.6.0", + "uuid": "^3.0.1" + }, + "optionalDependencies": { + "dtrace-provider": "^0.8.3" + } + }, + "node_modules/restify-clients/node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/restify-clients/node_modules/lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "license": "ISC", + "dependencies": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "node_modules/restify-clients/node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/restify-clients/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/restify-clients/node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "license": "MIT", + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/restify-clients/node_modules/yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", + "license": "ISC" + }, + "node_modules/restify-errors": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restify-errors/-/restify-errors-3.1.0.tgz", + "integrity": "sha512-4RDQs4zirMPXH03y5LKIFoAs+LvO9HTd5Ig4KfD5h4yRtTC5aWK/F2L1g9O2CSjTsgNIc+d0ib0f1rSob3FjNg==", + "license": "MIT", + "dependencies": { + "assert-plus": "^0.2.0", + "lodash": "^3.10.1", + "verror": "^1.6.0" + } + }, + "node_modules/restify-errors/node_modules/assert-plus": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", + "integrity": "sha512-u1L0ZLywRziOVjUhRxI0Qg9G+4RnFB9H/Rq40YWn0dieDgO7vAYeJz6jKAO6t/aruzlDFLAPkQTT87e+f8Imaw==", + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/restify-errors/node_modules/lodash": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", + "integrity": "sha512-9mDDwqVIma6OZX79ZlDACZl8sBm0TEnkf99zV3iMA4GzkIT/9hiqP5mY0HoT1iNLCrKc/R1HByV+yJfRWVJryQ==", + "license": "MIT" + }, "node_modules/restore-cursor": { "version": "2.0.0", "license": "MIT", @@ -25216,6 +26124,13 @@ ], "license": "MIT" }, + "node_modules/safe-json-stringify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz", + "integrity": "sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg==", + "license": "MIT", + "optional": true + }, "node_modules/safe-regex": { "version": "2.1.1", "dev": true, @@ -26520,6 +27435,12 @@ "node": "*" } }, + "node_modules/split-ca": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split-ca/-/split-ca-1.0.1.tgz", + "integrity": "sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ==", + "license": "ISC" + }, "node_modules/split2": { "version": "3.2.2", "license": "ISC", @@ -26567,11 +27488,59 @@ } } }, + "node_modules/sqlite3/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "engines": { + "node": ">=8" + } + }, "node_modules/sqlite3/node_modules/node-addon-api": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==" }, + "node_modules/sqlite3/node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ssh2": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.16.0.tgz", + "integrity": "sha512-r1X4KsBGedJqo7h8F5c4Ybpcr5RjyP+aWIG007uBPRjmdQWfEiVLzSK71Zji1B9sKxwaCvD8y8cwSkYrlLiRRg==", + "hasInstallScript": true, + "dependencies": { + "asn1": "^0.2.6", + "bcrypt-pbkdf": "^1.0.2" + }, + "engines": { + "node": ">=10.16.0" + }, + "optionalDependencies": { + "cpu-features": "~0.0.10", + "nan": "^2.20.0" + } + }, + "node_modules/ssh2/node_modules/nan": { + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.20.0.tgz", + "integrity": "sha512-bk3gXBZDGILuuo/6sKtr0DQmSThYHLtNCdSdXk9YkxD/jK6X2vmCyyXBBxyqZ4XcnzTyYEAThfX3DCEnLf6igw==", + "license": "MIT", + "optional": true + }, "node_modules/ssri": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", @@ -26881,6 +27850,28 @@ "node": ">=0.10.0" } }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, "node_modules/string-width/node_modules/ansi-regex": { "version": "2.1.1", "license": "MIT", @@ -26965,6 +27956,18 @@ "node": ">=8" } }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-bom": { "version": "5.0.0", "license": "MIT", @@ -27029,6 +28032,12 @@ "node": ">=0.8.0" } }, + "node_modules/strsplit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strsplit/-/strsplit-1.0.0.tgz", + "integrity": "sha512-efXqQImOEC0nyQqFzPUqa7NvF4B0ZPW2YM5nS+uXTB76sQt002brfZWQo/NSkAt771RTvv/brVQqtxJL7UBHMw==", + "license": "MIT" + }, "node_modules/sumchecker": { "version": "3.0.1", "license": "Apache-2.0", @@ -27254,19 +28263,19 @@ } }, "node_modules/tar": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", - "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", + "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^5.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" }, "engines": { - "node": ">=10" + "node": ">=18" } }, "node_modules/tar-fs": { @@ -27346,12 +28355,135 @@ "node": ">= 6" } }, + "node_modules/tar/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/tar/node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "engines": { + "node": ">=18" + } + }, + "node_modules/tar/node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/tar/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/tar/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/tar/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/tar/node_modules/minizlib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.1.tgz", + "integrity": "sha512-umcy022ILvb5/3Djuu8LWeqUa8D68JaBzlttKeMWen48SjabqS3iY5w/vzeMzMUNhLDifyhbOwKDSznB1vvrwg==", + "dependencies": { + "minipass": "^7.0.4", + "rimraf": "^5.0.5" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/tar/node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/tar/node_modules/rimraf": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz", + "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==", + "dependencies": { + "glob": "^10.3.7" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/tar/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/tar/node_modules/yallist": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", "engines": { - "node": ">=8" + "node": ">=18" } }, "node_modules/temp-dir": { @@ -27515,6 +28647,16 @@ "node": ">=6" } }, + "node_modules/tough-cookie": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.0.0.tgz", + "integrity": "sha512-qYeH1zA+4+36nVi2waxBoFcbL54iInWYs6NuMQztwijcfhPZqeCm/fjRkDrnEtkYzOIh19SkKrjs5A+VDx+5sA==", + "deprecated": "ReDoS vulnerability parsing Set-Cookie https://nodesecurity.io/advisories/130", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/tr46": { "version": "0.0.3", "license": "MIT" @@ -27783,6 +28925,12 @@ "node": "*" } }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", + "license": "Unlicense" + }, "node_modules/type": { "version": "1.2.0", "license": "ISC" @@ -27882,6 +29030,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/typedarray": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.7.tgz", + "integrity": "sha512-ueeb9YybpjhivjbHP2LdFDAjbS948fGEPj+ACAMs4xCMmh72OCOMQWBQKlaN4ZNQ04yfLSDLSx1tGRIoWimObQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/typedarray-to-buffer": { "version": "3.1.5", "license": "MIT", @@ -28436,6 +29593,68 @@ "node": ">= 0.8" } }, + "node_modules/vasync": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/vasync/-/vasync-1.6.4.tgz", + "integrity": "sha512-3oQMomVgQgHzNe5iKuT8PGOhMCQcg1wfh00Nh/Kl39ERdTlw/uNS7kbrhEraDMDKWHdDdc0iBFahPEd/Ft2b+A==", + "engines": [ + "node >=0.6.0" + ], + "license": "MIT", + "dependencies": { + "verror": "1.6.0" + } + }, + "node_modules/vasync/node_modules/extsprintf": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.2.0.tgz", + "integrity": "sha512-T3PYC6HucmF4OfunfZb5d1nRvTSvWYhsr/Og33HANcCuCtGPUtWVyt/tTs8SU9sR0SGh5Z/xQCuX/D72ph2H+A==", + "engines": [ + "node >=0.6.0" + ], + "license": "MIT" + }, + "node_modules/vasync/node_modules/verror": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.6.0.tgz", + "integrity": "sha512-bIOaZx4+Bf6a7sIORfmYnyKLDLk/lhVym6rjYlq+vkitYKnhFmUpmPpDTCltWFrUTlGKs6sCeoDWfMA0oOOneA==", + "engines": [ + "node >=0.6.0" + ], + "license": "MIT", + "dependencies": { + "extsprintf": "1.2.0" + } + }, + "node_modules/verror": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz", + "integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==", + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/verror/node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/verror/node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", + "license": "MIT" + }, "node_modules/version-guard": { "version": "1.1.1", "license": "0BSD", @@ -28863,6 +30082,44 @@ "node": ">=4" } }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/wrap-ansi/node_modules/ansi-regex": { "version": "3.0.1", "license": "MIT", @@ -28932,6 +30189,14 @@ } } }, + "node_modules/www-authenticate": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/www-authenticate/-/www-authenticate-0.6.3.tgz", + "integrity": "sha512-8VkdLBJiBh5aXlJvcVaPykwSI//OA+Sxw7g84vIyCqoqlXtLupGNhyXxbgVuZ7g5ZS+lCJ4bTtcw/gJciqEuAg==", + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/xdg-basedir": { "version": "5.1.0", "license": "MIT", diff --git a/package.json b/package.json index 15ea3024c..bcac9d77c 100644 --- a/package.json +++ b/package.json @@ -87,6 +87,8 @@ "base58-js": "^2.0.0", "cors": "^2.8.5", "delay": "^5.0.0", + "docker-registry-client": "^3.4.0", + "dockerode": "^4.0.2", "dotenv": "^16.3.1", "eciesjs": "^0.4.5", "eth-crypto": "^2.6.0", @@ -108,6 +110,7 @@ "sinon": "^17.0.1", "sqlite3": "^5.1.7", "stream-concat": "^1.0.0", + "tar": "^7.4.3", "ts-node": "^10.9.1", "tsoa": "^5.1.1", "uint8arrays": "^4.0.6", @@ -119,6 +122,7 @@ "devDependencies": { "@types/chai": "^4.3.10", "@types/cors": "^2.8.17", + "@types/dockerode": "^3.3.31", "@types/express": "^4.17.17", "@types/ip": "^1.1.3", "@types/lzma-native": "^4.0.4", From 610bc4e4c266bcba0090fb940e4855bb40dd13fc Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Sun, 6 Oct 2024 22:55:26 +0300 Subject: [PATCH 10/30] update ignores --- .dockerignore | 1 + .gitignore | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.dockerignore b/.dockerignore index 5c69ffc54..62ffd3f04 100644 --- a/.dockerignore +++ b/.dockerignore @@ -2,5 +2,6 @@ node_modules /dist/* !/dist/dashboard logs +c2d_storage .env.local .env \ No newline at end of file diff --git a/.gitignore b/.gitignore index 4e12c8c52..f943b46a0 100644 --- a/.gitignore +++ b/.gitignore @@ -154,4 +154,5 @@ html-report.html # databases *.sqlite -databases/* \ No newline at end of file +databases/* +c2d_storage/* \ No newline at end of file From d26a8c50a68ef4812de2291d3c2193b693a2015a Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Mon, 7 Oct 2024 09:31:15 +0300 Subject: [PATCH 11/30] fix getComputeJobResult and getComputeJobStatus --- src/components/c2d/compute_engine_docker.ts | 73 +++++++++++++++++++-- 1 file changed, 67 insertions(+), 6 deletions(-) diff --git a/src/components/c2d/compute_engine_docker.ts b/src/components/c2d/compute_engine_docker.ts index 3eb56be5e..e19f0d218 100644 --- a/src/components/c2d/compute_engine_docker.ts +++ b/src/components/c2d/compute_engine_docker.ts @@ -7,7 +7,8 @@ import type { ComputeAsset, ComputeJob, ComputeOutput, - DBComputeJob + DBComputeJob, + ComputeResult } from '../../@types/C2D/C2D.js' import { ZeroAddress } from 'ethers' // import { getProviderFeeToken } from '../../components/core/utils/feesHandler.js' @@ -19,7 +20,15 @@ import { Storage } from '../storage/index.js' import Dockerode from 'dockerode' import type { ContainerCreateOptions, VolumeCreateOptions } from 'dockerode' import * as tar from 'tar' -import { createWriteStream, existsSync, mkdirSync, rmSync, writeFileSync } from 'fs' +import { + createWriteStream, + existsSync, + mkdirSync, + rmSync, + writeFileSync, + statSync, + createReadStream +} from 'fs' import { pipeline } from 'node:stream/promises' export class C2DEngineDocker extends C2DEngine { @@ -147,6 +156,37 @@ export class C2DEngineDocker extends C2DEngine { return null } + // eslint-disable-next-line require-await + private async getResults(jobId: string): Promise { + const res: ComputeResult[] = [] + let index = 0 + const logStat = statSync( + this.getC2DConfig().tempFolder + '/' + jobId + '/data/logs/algorithmLog' + ) + if (logStat) { + res.push({ + filename: 'algorithmLog', + filesize: logStat.size, + type: 'algorithmLog', + index + }) + index = index + 1 + } + const outputStat = statSync( + this.getC2DConfig().tempFolder + '/' + jobId + '/data/outputs/outputs.tar' + ) + if (outputStat) { + res.push({ + filename: 'outputs.tar', + filesize: outputStat.size, + type: 'output', + index + }) + index = index + 1 + } + return res + } + // eslint-disable-next-line require-await public override async getComputeJobStatus( consumerAddress?: string, @@ -154,11 +194,13 @@ export class C2DEngineDocker extends C2DEngine { jobId?: string ): Promise { const job = await this.db.getJob(jobId) - if (job) { - const res: ComputeJob[] = [job as ComputeJob] - return res + if (!job) { + return [] } - return null + const res: ComputeJob = job as ComputeJob + // add results for algoLogs + res.results = await this.getResults(job.jobId) + return [res] } // eslint-disable-next-line require-await @@ -167,6 +209,25 @@ export class C2DEngineDocker extends C2DEngine { jobId: string, index: number ): Promise { + const job = await this.db.getJob(jobId) + if (!job) { + return null + } + const results = await this.getResults(jobId) + for (const i of results) { + if (i.index === index) { + if (i.type === 'algorithmLog') { + return createReadStream( + this.getC2DConfig().tempFolder + '/' + jobId + '/data/logs/algorithmLog' + ) + } + if (i.type === 'output') { + return createReadStream( + this.getC2DConfig().tempFolder + '/' + jobId + '/data/outputs/outputs.tar' + ) + } + } + } return null } From 88aecf12cdb6cc9d99077ea56cb05c1c4b4d8bdc Mon Sep 17 00:00:00 2001 From: paulo-ocean Date: Mon, 21 Oct 2024 18:33:28 +0100 Subject: [PATCH 12/30] wip: add sql lite db --- src/components/database/BaseDatabase.ts | 35 +--- src/components/database/C2DDatabase.ts | 63 ++++--- src/components/database/DatabaseFactory.ts | 5 + src/components/database/TypesenseSchemas.ts | 12 +- src/components/database/sqlite.ts | 2 +- src/components/database/sqliteCompute.ts | 175 ++++++++++++++++++-- src/test/unit/compute.test.ts | 114 +++++++++++++ 7 files changed, 330 insertions(+), 76 deletions(-) create mode 100644 src/test/unit/compute.test.ts diff --git a/src/components/database/BaseDatabase.ts b/src/components/database/BaseDatabase.ts index 637b82aec..cd2b7ea7c 100644 --- a/src/components/database/BaseDatabase.ts +++ b/src/components/database/BaseDatabase.ts @@ -5,7 +5,7 @@ import { DATABASE_LOGGER } from '../../utils/logging/common.js' import { ElasticsearchSchema } from './ElasticSchemas.js' import { TypesenseSchema } from './TypesenseSchemas.js' -export abstract class AbstractNonceDatabase { +export abstract class AbstractDatabase { protected config: OceanNodeDBConfig protected schema: TypesenseSchema @@ -13,7 +13,8 @@ export abstract class AbstractNonceDatabase { this.config = config this.schema = schema } - +} +export abstract class AbstractNonceDatabase extends AbstractDatabase { abstract create(address: string, nonce: number): Promise abstract retrieve(address: string): Promise abstract update(address: string, nonce: number): Promise @@ -30,30 +31,14 @@ export abstract class AbstractNonceDatabase { } } -export abstract class AbstractIndexerDatabase { - protected config: OceanNodeDBConfig - protected schema: TypesenseSchema - - constructor(config: OceanNodeDBConfig, schema?: TypesenseSchema) { - this.config = config - this.schema = schema - } - +export abstract class AbstractIndexerDatabase extends AbstractDatabase { abstract create(network: number, lastIndexedBlock: number): Promise abstract retrieve(network: number): Promise abstract update(network: number, lastIndexedBlock: number): Promise abstract delete(network: number): Promise } -export abstract class AbstractLogDatabase { - protected config: OceanNodeDBConfig - protected schema: TypesenseSchema - - constructor(config: OceanNodeDBConfig, schema?: TypesenseSchema) { - this.config = config - this.schema = schema - } - +export abstract class AbstractLogDatabase extends AbstractDatabase { abstract insertLog(logEntry: Record): Promise abstract retrieveLog(id: string): Promise | null> abstract retrieveMultipleLogs( @@ -70,15 +55,7 @@ export abstract class AbstractLogDatabase { abstract getLogsCount(): Promise } -export abstract class AbstractDdoStateDatabase { - protected config: OceanNodeDBConfig - protected schema: TypesenseSchema - - constructor(config: OceanNodeDBConfig, schema?: TypesenseSchema) { - this.config = config - this.schema = schema - } - +export abstract class AbstractDdoStateDatabase extends AbstractDatabase { abstract create( chainId: number, did: string, diff --git a/src/components/database/C2DDatabase.ts b/src/components/database/C2DDatabase.ts index c29f9ee7b..888e12f92 100644 --- a/src/components/database/C2DDatabase.ts +++ b/src/components/database/C2DDatabase.ts @@ -3,26 +3,19 @@ import fs from 'fs' import { DBComputeJob } from '../../@types/C2D/C2D.js' import { SQLiteCompute } from './sqliteCompute.js' import { DATABASE_LOGGER } from '../../utils/logging/common.js' -import { GENERIC_EMOJIS, LOG_LEVELS_STR } from '../../utils/logging/Logger.js' import { OceanNodeDBConfig } from '../../@types/OceanNode.js' import { TypesenseSchema } from './TypesenseSchemas.js' +import { AbstractDatabase } from './BaseDatabase.js' -export class C2DDatabase { +export class C2DDatabase extends AbstractDatabase { private provider: SQLiteCompute - private tempMem: DBComputeJob[] - constructor( - private config: OceanNodeDBConfig, - private schema: TypesenseSchema - ) { - this.tempMem = [] + // private tempMem: DBComputeJob[] + constructor(config: OceanNodeDBConfig, schema: TypesenseSchema) { + super(config, schema) + // this.tempMem = [] return (async (): Promise => { // Fall back to SQLite - DATABASE_LOGGER.logMessageWithEmoji( - 'C2DDatabase: Typesense not available, falling back to SQLite', - true, - GENERIC_EMOJIS.EMOJI_CROSS_MARK, - LOG_LEVELS_STR.LEVEL_WARN - ) + DATABASE_LOGGER.info('Creating C2DDatabase with SQLite') // Ensure the directory exists before instantiating SQLiteProvider const dbDir = path.dirname('databases/c2dDatabase.sqlite') @@ -39,39 +32,41 @@ export class C2DDatabase { // eslint-disable-next-line require-await async newJob(job: DBComputeJob): Promise { // TO DO C2D - this.tempMem.push(job) - return job.agreementId + // this.tempMem.push(job) + const jobId = await this.provider.newJob(job) + return jobId } // eslint-disable-next-line require-await async getJob(jobId: string): Promise { - console.log('GetJob jobId:' + jobId) - console.log(this.tempMem) - for (const i in this.tempMem) { - if (this.tempMem[i].jobId === jobId) return this.tempMem[i] - } - return null + const job = this.provider.getJob(jobId) + return job || null } // eslint-disable-next-line require-await async updateJob(job: DBComputeJob) { // TO DO C2D - for (const i in this.tempMem) { - if (this.tempMem[i].jobId === job.jobId) this.tempMem[i] = job - } + // for (const i in this.tempMem) { + // if (this.tempMem[i].jobId === job.jobId) this.tempMem[i] = job + // } } // eslint-disable-next-line require-await async getRunningJobs(engine?: string, environment?: string): Promise { // TO DO C2D - const runningJobs: DBComputeJob[] = [] - for (const i in this.tempMem) { - if (this.tempMem[i].isRunning === true) { - if (engine && this.tempMem[i].clusterHash !== engine) continue - if (environment && this.tempMem[i].environment !== environment) continue - runningJobs.push(this.tempMem[i]) - } - } - return runningJobs + // const runningJobs: DBComputeJob[] = [] + // for (const i in this.tempMem) { + // if (this.tempMem[i].isRunning === true) { + // if (engine && this.tempMem[i].clusterHash !== engine) continue + // if (environment && this.tempMem[i].environment !== environment) continue + // runningJobs.push(this.tempMem[i]) + // } + // } + // return runningJobs + return null + } + + async deleteJob(jobId: string): Promise { + return await this.provider.deleteJob(jobId) } } diff --git a/src/components/database/DatabaseFactory.ts b/src/components/database/DatabaseFactory.ts index 0edf41c62..b94896af3 100644 --- a/src/components/database/DatabaseFactory.ts +++ b/src/components/database/DatabaseFactory.ts @@ -32,6 +32,7 @@ import { TypesenseMetadataQuery } from './TypesenseMetadataQuery.js' import { IMetadataQuery } from '../../@types/DDO/IMetadataQuery.js' import { ElasticSearchMetadataQuery } from './ElasticSearchMetadataQuery.js' import { DB_TYPES } from '../../utils/index.js' +import { C2DDatabase } from './C2DDatabase.js' export class DatabaseFactory { private static databaseMap = { @@ -88,6 +89,10 @@ export class DatabaseFactory { return this.createDatabase('ddo', config) } + static createC2DDatabase(config: OceanNodeDBConfig): C2DDatabase { + return new C2DDatabase(config, typesenseSchemas.c2dSchemas) + } + static createIndexerDatabase( config: OceanNodeDBConfig ): Promise { diff --git a/src/components/database/TypesenseSchemas.ts b/src/components/database/TypesenseSchemas.ts index 61cadd4cd..5746d51f3 100644 --- a/src/components/database/TypesenseSchemas.ts +++ b/src/components/database/TypesenseSchemas.ts @@ -67,7 +67,17 @@ export const typesenseSchemas: TypesenseSchemas = { enable_nested_fields: true, fields: [ // TO DO C2D - { name: '.*', type: 'auto', optional: true } + { name: 'clusterHash', type: 'string', optional: false }, + { name: 'configlogURL', type: 'string', optional: false }, + { name: 'publishlogURL', type: 'string', optional: false }, + { name: 'algologURL', type: 'string', optional: false }, + { name: 'outputsURL', type: 'auto', optional: false }, + { name: 'stopRequested', type: 'bool', optional: false }, + // { name: 'algorithm', type: 'ComputeAlgorithm', optional: false }, + // { name: 'assets', type: 'ComputeAsset[]', optional: false } + { name: 'isRunning', type: 'bool', optional: false }, + { name: 'isStarted', type: 'bool', optional: false }, + { name: 'containerImage', type: 'string', optional: false } ] }, indexerSchemas: { diff --git a/src/components/database/sqlite.ts b/src/components/database/sqlite.ts index e2f77de1a..8a17040a4 100644 --- a/src/components/database/sqlite.ts +++ b/src/components/database/sqlite.ts @@ -12,7 +12,7 @@ export class SQLiteProvider implements DatabaseProvider { private db: sqlite3.Database private schema: TypesenseSchema - constructor(private dbFilePath: string) { + constructor(dbFilePath: string) { this.db = new sqlite3.Database(dbFilePath) this.schema = typesenseSchemas.nonceSchemas } diff --git a/src/components/database/sqliteCompute.ts b/src/components/database/sqliteCompute.ts index 344f7a01f..bd1c8081c 100644 --- a/src/components/database/sqliteCompute.ts +++ b/src/components/database/sqliteCompute.ts @@ -7,32 +7,185 @@ interface ComputeDatabaseProvider { getJob(jobId: string): Promise updateJob(job: DBComputeJob): void getRunningJobs(engine?: string, environment?: string): Promise + deleteJob(jobId: string): Promise +} + +/** + * export interface ComputeJob { + owner: string + did?: string + jobId: string + dateCreated: string + dateFinished: string + status: number + statusText: string + results: ComputeResult[] + inputDID?: string[] + algoDID?: string + agreementId?: string + expireTimestamp: number + environment?: string + + // internal structure + clusterHash: string + configlogURL: string + publishlogURL: string + algologURL: string + outputsURL: string + stopRequested: boolean + algorithm: ComputeAlgorithm + assets: ComputeAsset[] + isRunning: boolean + isStarted: boolean + containerImage: string +} + */ + +export function generateUniqueID(): string { + return crypto.randomUUID().toString() +} + +export function generateBlobFromJSON(obj: any): Buffer { + return Buffer.from(JSON.stringify(obj)) +} + +export function generateJSONFromBlob(blob: any): Promise { + return JSON.parse(blob.toString()) +} + +export const STRING_SEPARATOR = '__,__' +export function convertArrayToString(array: string[]) { + let str: string = '' + for (let i = 0; i < array.length; i++) { + str = str + array[i] + // Do not append comma at the end of last element + if (i < array.length - 1) { + str = str + STRING_SEPARATOR + } + } + return str +} +export function convertStringToArray(str: string) { + const arr: string[] = str.split(STRING_SEPARATOR) + return arr } export class SQLiteCompute implements ComputeDatabaseProvider { private db: sqlite3.Database private schema: TypesenseSchema - constructor(private dbFilePath: string) { + constructor(dbFilePath: string) { this.db = new sqlite3.Database(dbFilePath) - this.schema = typesenseSchemas.nonceSchemas + this.schema = typesenseSchemas.c2dSchemas } - // eslint-disable-next-line require-await - async createTable() { - // TO DO C2D + deleteJob(jobId: string): Promise { + const deleteSQL = ` + DELETE FROM ${this.schema.name} WHERE jobId = ? + ` + return new Promise((resolve, reject) => { + this.db.run(deleteSQL, [jobId], (err) => { + if (err) reject(err) + else resolve(true) + }) + }) } - // eslint-disable-next-line require-await - async newJob(job: DBComputeJob): Promise { + createTable() { + const createTableSQL = ` + CREATE TABLE IF NOT EXISTS ${this.schema.name} ( + owner TEXT, + did TEXT, + jobId TEXT PRIMARY KEY, + dateCreated TEXT, + dateFinished TEXT, + status INTEGER, + statusText TEXT, + results BLOB, + inputDID TEXT, + algoDID TEXT, + agreementId TEXT, + expireTimestamp INTEGER, + environment TEXT, + body BLOB + ); + ` + return new Promise((resolve, reject) => { + this.db.run(createTableSQL, (err) => { + if (err) reject(err) + else resolve() + }) + }) + } + + newJob(job: DBComputeJob): Promise { // TO DO C2D - return null + const insertSQL = ` + INSERT INTO ${this.schema.name} + ( + owner, + did, + jobId, + dateCreated, + status, + statusText, + inputDID, + algoDID, + agreementId, + expireTimestamp, + environment, + body + ) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?); + ` + const jobId = generateUniqueID() + job.jobId = jobId + return new Promise((resolve, reject) => { + this.db.run( + insertSQL, + [ + job.owner, + job.did, + jobId, + new Date().toISOString(), + job.status || 1, + job.statusText || 'Warming up', + job.inputDID ? convertArrayToString(job.inputDID) : job.inputDID, + job.algoDID, + job.agreementId, + job.expireTimestamp, + job.environment, + generateBlobFromJSON(job) + ], + (err) => { + if (err) reject(err) + else resolve(jobId) + } + ) + }) } - // eslint-disable-next-line require-await - async getJob(jobId: string): Promise { + getJob(jobId: string): Promise { // TO DO C2D - return null + const selectSQL = ` + SELECT * FROM ${this.schema.name} WHERE jobId = ? + ` + return new Promise((resolve, reject) => { + this.db.get(selectSQL, [jobId], (err, row: any | undefined) => { + if (err) { + reject(err) + } else { + // also decode the internal data into job data + if (row && row.body) { + const body: any = generateJSONFromBlob(row.body) + const job: DBComputeJob = { ...body } + resolve(job) + } else { + resolve(null) + } + } + }) + }) } // eslint-disable-next-line require-await diff --git a/src/test/unit/compute.test.ts b/src/test/unit/compute.test.ts new file mode 100644 index 000000000..d261772ce --- /dev/null +++ b/src/test/unit/compute.test.ts @@ -0,0 +1,114 @@ +// import { expect } from 'chai' +import { C2DDatabase } from '../../components/database/C2DDatabase.js' +import { getConfiguration } from '../../utils/config.js' +import { typesenseSchemas } from '../../components/database/TypesenseSchemas.js' +import { ComputeAlgorithm, ComputeAsset, DBComputeJob } from '../../@types/C2D/C2D.js' +// import { computeAsset } from '../data/assets' +import { assert } from 'console' +import { expect } from 'chai' +import { + convertArrayToString, + convertStringToArray, + STRING_SEPARATOR +} from '../../components/database/sqliteCompute.js' + +describe('Compute Jobs', () => { + let db: C2DDatabase = null + let jobId: string = null + before(async () => { + const config = await getConfiguration(true) + db = await new C2DDatabase(config.dbConfig, typesenseSchemas.c2dSchemas) + }) + + it('should create a new C2D Job', async () => { + /** + * export interface ComputeJob { + owner: string + did?: string + jobId: string + dateCreated: string + dateFinished: string + status: number + statusText: string + results: ComputeResult[] + inputDID?: string[] + algoDID?: string + agreementId?: string + expireTimestamp: number + environment?: string + + // internal structure + clusterHash: string + configlogURL: string + publishlogURL: string + algologURL: string + outputsURL: string + stopRequested: boolean + algorithm: ComputeAlgorithm + assets: ComputeAsset[] + isRunning: boolean + isStarted: boolean + containerImage: string + } + */ + const algorithm: ComputeAlgorithm = { + documentId: 'did:op:12345', + serviceId: '0x1828228' + } + const dataset: ComputeAsset = { + documentId: 'did:op:12345', + serviceId: '0x12345abc' + } + + const job: DBComputeJob = { + owner: '0xe2DD09d719Da89e5a3D0F2549c7E24566e947260', + jobId: null, + dateCreated: null, + dateFinished: null, + status: 1, + statusText: 'Warming up', + results: null, + inputDID: ['did:op:1', 'did:op:2', 'did:op:3'], + expireTimestamp: 0, + + // internal structure + clusterHash: 'clusterHash', + configlogURL: 'http://localhost:8001', + publishlogURL: 'http://localhost:8001', + algologURL: 'http://localhost:8001', + outputsURL: 'http://localhost:8001', + stopRequested: false, + algorithm, + assets: [dataset], + isRunning: false, + isStarted: false, + containerImage: 'some container image' + } + + jobId = await db.newJob(job) + assert(jobId, 'Missing jobId identifier') + }) + + it('should get job by jobId', async () => { + const job = await db.getJob(jobId) + assert(job, 'Job should not be null') + }) + + it('should delete the job by jobId', async () => { + const deleted = await db.deleteJob(jobId) + expect(deleted === true, 'Job was not deleted!') + }) + + it('should convert array of strings to a string', () => { + const inputDID = ['did:op:1', 'did:op:2', 'did:op:3'] + const expectedStr = + 'did:op:1' + STRING_SEPARATOR + 'did:op:2' + STRING_SEPARATOR + 'did:op:3' + expect(convertArrayToString(inputDID)).to.equal(expectedStr) + }) + + it('should convert concatenated string to a string array', () => { + const expectedArray = ['did:op:1', 'did:op:2', 'did:op:3'] + const str = 'did:op:1' + STRING_SEPARATOR + 'did:op:2' + STRING_SEPARATOR + 'did:op:3' + expect(convertStringToArray(str)).to.deep.equal(expectedArray) + }) +}) From e13a027c036a5206fa189b9c09582dd7ad9547b1 Mon Sep 17 00:00:00 2001 From: paulo-ocean Date: Tue, 22 Oct 2024 15:36:43 +0100 Subject: [PATCH 13/30] more changes + refactor internal blob/body --- src/components/database/C2DDatabase.ts | 16 +++- src/components/database/sqliteCompute.ts | 105 +++++++++++++++++------ src/test/unit/compute.test.ts | 62 ++++++------- 3 files changed, 118 insertions(+), 65 deletions(-) diff --git a/src/components/database/C2DDatabase.ts b/src/components/database/C2DDatabase.ts index 888e12f92..5ca29a624 100644 --- a/src/components/database/C2DDatabase.ts +++ b/src/components/database/C2DDatabase.ts @@ -29,7 +29,6 @@ export class C2DDatabase extends AbstractDatabase { })() as unknown as C2DDatabase } - // eslint-disable-next-line require-await async newJob(job: DBComputeJob): Promise { // TO DO C2D // this.tempMem.push(job) @@ -37,18 +36,27 @@ export class C2DDatabase extends AbstractDatabase { return jobId } - // eslint-disable-next-line require-await async getJob(jobId: string): Promise { - const job = this.provider.getJob(jobId) + const job = await this.provider.getJob(jobId) return job || null } // eslint-disable-next-line require-await - async updateJob(job: DBComputeJob) { + async updateJob(job: DBComputeJob): Promise { // TO DO C2D // for (const i in this.tempMem) { // if (this.tempMem[i].jobId === job.jobId) this.tempMem[i] = job // } + let updated = 0 + let previouslySaved: DBComputeJob = await this.getJob(job.jobId) + if (previouslySaved) { + previouslySaved = job + updated = await this.provider.updateJob(previouslySaved) + if (!updated) { + DATABASE_LOGGER.error('Unable to update job') + } + } + return updated } // eslint-disable-next-line require-await diff --git a/src/components/database/sqliteCompute.ts b/src/components/database/sqliteCompute.ts index bd1c8081c..cc1160760 100644 --- a/src/components/database/sqliteCompute.ts +++ b/src/components/database/sqliteCompute.ts @@ -1,11 +1,16 @@ import { typesenseSchemas, TypesenseSchema } from './TypesenseSchemas.js' -import type { DBComputeJob } from '../../@types/C2D/C2D.js' -import sqlite3 from 'sqlite3' +import { + C2DStatusNumber, + C2DStatusText, + type DBComputeJob +} from '../../@types/C2D/C2D.js' +import sqlite3, { RunResult } from 'sqlite3' +import { DATABASE_LOGGER } from '../../utils/logging/common.js' interface ComputeDatabaseProvider { newJob(job: DBComputeJob): Promise getJob(jobId: string): Promise - updateJob(job: DBComputeJob): void + updateJob(job: DBComputeJob): Promise getRunningJobs(engine?: string, environment?: string): Promise deleteJob(jobId: string): Promise } @@ -25,28 +30,42 @@ interface ComputeDatabaseProvider { agreementId?: string expireTimestamp: number environment?: string - - // internal structure - clusterHash: string - configlogURL: string - publishlogURL: string - algologURL: string - outputsURL: string - stopRequested: boolean - algorithm: ComputeAlgorithm - assets: ComputeAsset[] - isRunning: boolean - isStarted: boolean - containerImage: string } */ export function generateUniqueID(): string { return crypto.randomUUID().toString() } - -export function generateBlobFromJSON(obj: any): Buffer { - return Buffer.from(JSON.stringify(obj)) +// internal structure, added by DBComputeJob +// clusterHash: string +// configlogURL: string +// publishlogURL: string +// algologURL: string +// outputsURL: string +// stopRequested: boolean +// algorithm: ComputeAlgorithm +// assets: ComputeAsset[] +// isRunning: boolean +// isStarted: boolean +// containerImage: string +function getInternalStructure(job: DBComputeJob): any { + const internalBlob = { + clusterHash: job.clusterHash, + configlogURL: job.configlogURL, + publishlogURL: job.publishlogURL, + algologURL: job.algologURL, + outputsURL: job.outputsURL, + stopRequested: job.stopRequested, + algorithm: job.algorithm, + assets: job.assets, + isRunning: job.isRunning, + isStarted: job.isStarted, + containerImage: job.containerImage + } + return internalBlob +} +export function generateBlobFromJSON(job: DBComputeJob): Buffer { + return Buffer.from(JSON.stringify(getInternalStructure(job))) } export function generateJSONFromBlob(blob: any): Promise { @@ -84,9 +103,9 @@ export class SQLiteCompute implements ComputeDatabaseProvider { DELETE FROM ${this.schema.name} WHERE jobId = ? ` return new Promise((resolve, reject) => { - this.db.run(deleteSQL, [jobId], (err) => { + this.db.run(deleteSQL, [jobId], function (this: RunResult, err) { if (err) reject(err) - else resolve(true) + else resolve(this.changes === 1) }) }) } @@ -140,6 +159,7 @@ export class SQLiteCompute implements ComputeDatabaseProvider { ` const jobId = generateUniqueID() job.jobId = jobId + job.dateCreated = new Date().toISOString() return new Promise((resolve, reject) => { this.db.run( insertSQL, @@ -147,9 +167,9 @@ export class SQLiteCompute implements ComputeDatabaseProvider { job.owner, job.did, jobId, - new Date().toISOString(), - job.status || 1, - job.statusText || 'Warming up', + job.dateCreated, + job.status || C2DStatusNumber.JobStarted, + job.statusText || C2DStatusText.JobStarted, job.inputDID ? convertArrayToString(job.inputDID) : job.inputDID, job.algoDID, job.agreementId, @@ -177,8 +197,10 @@ export class SQLiteCompute implements ComputeDatabaseProvider { } else { // also decode the internal data into job data if (row && row.body) { - const body: any = generateJSONFromBlob(row.body) - const job: DBComputeJob = { ...body } + const bodyEncoded = row.body + const body: any = generateJSONFromBlob(bodyEncoded) + delete row.body + const job: DBComputeJob = { ...row, ...body } resolve(job) } else { resolve(null) @@ -189,8 +211,37 @@ export class SQLiteCompute implements ComputeDatabaseProvider { } // eslint-disable-next-line require-await - async updateJob(job: DBComputeJob) { + async updateJob(job: DBComputeJob): Promise { // TO DO C2D + const data: any[] = [ + job.owner, + job.status, + job.statusText, + job.expireTimestamp, + generateBlobFromJSON(job), + job.jobId + ] + const updateSQL = ` + UPDATE ${this.schema.name} + SET + owner = ?, + status = ?, + statusText = ?, + expireTimestamp = ?, + body = ? + WHERE jobId = ?; + ` + return new Promise((resolve, reject) => { + this.db.run(updateSQL, data, function (this: RunResult, err: Error | null) { + if (err) { + DATABASE_LOGGER.error(`Error while updating job: ${err.message}`) + reject(err) + } else { + // number of rows updated successfully + resolve(this.changes) + } + }) + }) } // eslint-disable-next-line require-await diff --git a/src/test/unit/compute.test.ts b/src/test/unit/compute.test.ts index d261772ce..1668f32e3 100644 --- a/src/test/unit/compute.test.ts +++ b/src/test/unit/compute.test.ts @@ -2,7 +2,13 @@ import { C2DDatabase } from '../../components/database/C2DDatabase.js' import { getConfiguration } from '../../utils/config.js' import { typesenseSchemas } from '../../components/database/TypesenseSchemas.js' -import { ComputeAlgorithm, ComputeAsset, DBComputeJob } from '../../@types/C2D/C2D.js' +import { + C2DStatusNumber, + C2DStatusText, + ComputeAlgorithm, + ComputeAsset, + DBComputeJob +} from '../../@types/C2D/C2D.js' // import { computeAsset } from '../data/assets' import { assert } from 'console' import { expect } from 'chai' @@ -12,7 +18,7 @@ import { STRING_SEPARATOR } from '../../components/database/sqliteCompute.js' -describe('Compute Jobs', () => { +describe('Compute Jobs Database', () => { let db: C2DDatabase = null let jobId: string = null before(async () => { @@ -21,36 +27,6 @@ describe('Compute Jobs', () => { }) it('should create a new C2D Job', async () => { - /** - * export interface ComputeJob { - owner: string - did?: string - jobId: string - dateCreated: string - dateFinished: string - status: number - statusText: string - results: ComputeResult[] - inputDID?: string[] - algoDID?: string - agreementId?: string - expireTimestamp: number - environment?: string - - // internal structure - clusterHash: string - configlogURL: string - publishlogURL: string - algologURL: string - outputsURL: string - stopRequested: boolean - algorithm: ComputeAlgorithm - assets: ComputeAsset[] - isRunning: boolean - isStarted: boolean - containerImage: string - } - */ const algorithm: ComputeAlgorithm = { documentId: 'did:op:12345', serviceId: '0x1828228' @@ -65,8 +41,8 @@ describe('Compute Jobs', () => { jobId: null, dateCreated: null, dateFinished: null, - status: 1, - statusText: 'Warming up', + status: C2DStatusNumber.JobStarted, + statusText: C2DStatusText.JobStarted, results: null, inputDID: ['did:op:1', 'did:op:2', 'did:op:3'], expireTimestamp: 0, @@ -94,6 +70,24 @@ describe('Compute Jobs', () => { assert(job, 'Job should not be null') }) + it('should update job', async () => { + const job = await db.getJob(jobId) + console.log('job before update', job) + // will update some fields + job.status = C2DStatusNumber.PullImage + job.isRunning = true + job.statusText = C2DStatusText.PullImage + + // update on DB + const updates = await db.updateJob(job) + expect(updates).to.be.equal(1) // updated 1 row + const updatedJob = await db.getJob(jobId) + assert(updatedJob, 'Job should not be null') + expect(updatedJob.status).to.be.equal(C2DStatusNumber.PullImage) + expect(updatedJob.isRunning).to.be.equal(true) + expect(updatedJob.statusText).to.be.equal(C2DStatusText.PullImage) + }) + it('should delete the job by jobId', async () => { const deleted = await db.deleteJob(jobId) expect(deleted === true, 'Job was not deleted!') From 07851cae102ff4e4f5e8cbbdfa4374f6a3774541 Mon Sep 17 00:00:00 2001 From: paulo-ocean Date: Tue, 22 Oct 2024 19:09:28 +0100 Subject: [PATCH 14/30] add get running jobs --- src/components/database/C2DDatabase.ts | 29 +++-------- src/components/database/sqliteCompute.ts | 48 +++++++++++++++--- src/test/unit/compute.test.ts | 63 +++++++++++++++++++----- 3 files changed, 98 insertions(+), 42 deletions(-) diff --git a/src/components/database/C2DDatabase.ts b/src/components/database/C2DDatabase.ts index 5ca29a624..5b59da756 100644 --- a/src/components/database/C2DDatabase.ts +++ b/src/components/database/C2DDatabase.ts @@ -9,10 +9,9 @@ import { AbstractDatabase } from './BaseDatabase.js' export class C2DDatabase extends AbstractDatabase { private provider: SQLiteCompute - // private tempMem: DBComputeJob[] + constructor(config: OceanNodeDBConfig, schema: TypesenseSchema) { super(config, schema) - // this.tempMem = [] return (async (): Promise => { // Fall back to SQLite DATABASE_LOGGER.info('Creating C2DDatabase with SQLite') @@ -30,8 +29,6 @@ export class C2DDatabase extends AbstractDatabase { } async newJob(job: DBComputeJob): Promise { - // TO DO C2D - // this.tempMem.push(job) const jobId = await this.provider.newJob(job) return jobId } @@ -41,37 +38,25 @@ export class C2DDatabase extends AbstractDatabase { return job || null } - // eslint-disable-next-line require-await async updateJob(job: DBComputeJob): Promise { - // TO DO C2D - // for (const i in this.tempMem) { - // if (this.tempMem[i].jobId === job.jobId) this.tempMem[i] = job - // } let updated = 0 let previouslySaved: DBComputeJob = await this.getJob(job.jobId) if (previouslySaved) { previouslySaved = job updated = await this.provider.updateJob(previouslySaved) if (!updated) { - DATABASE_LOGGER.error('Unable to update job') + DATABASE_LOGGER.error(`Unable to update job: ${job.jobId}. No rows affected!`) } + } else { + DATABASE_LOGGER.error( + `Unable to update job: ${job.jobId}. It seems this jobID does not exist!` + ) } return updated } - // eslint-disable-next-line require-await async getRunningJobs(engine?: string, environment?: string): Promise { - // TO DO C2D - // const runningJobs: DBComputeJob[] = [] - // for (const i in this.tempMem) { - // if (this.tempMem[i].isRunning === true) { - // if (engine && this.tempMem[i].clusterHash !== engine) continue - // if (environment && this.tempMem[i].environment !== environment) continue - // runningJobs.push(this.tempMem[i]) - // } - // } - // return runningJobs - return null + return await this.provider.getRunningJobs(engine, environment) } async deleteJob(jobId: string): Promise { diff --git a/src/components/database/sqliteCompute.ts b/src/components/database/sqliteCompute.ts index cc1160760..e7bd8a84b 100644 --- a/src/components/database/sqliteCompute.ts +++ b/src/components/database/sqliteCompute.ts @@ -117,7 +117,7 @@ export class SQLiteCompute implements ComputeDatabaseProvider { did TEXT, jobId TEXT PRIMARY KEY, dateCreated TEXT, - dateFinished TEXT, + dateFinished TEXT DEFAULT NULL, status INTEGER, statusText TEXT, results BLOB, @@ -210,8 +210,10 @@ export class SQLiteCompute implements ComputeDatabaseProvider { }) } - // eslint-disable-next-line require-await - async updateJob(job: DBComputeJob): Promise { + updateJob(job: DBComputeJob): Promise { + if (job.dateFinished && job.isRunning) { + job.isRunning = false + } // TO DO C2D const data: any[] = [ job.owner, @@ -244,9 +246,41 @@ export class SQLiteCompute implements ComputeDatabaseProvider { }) } - // eslint-disable-next-line require-await - async getRunningJobs(engine?: string, environment?: string): Promise { - // TO DO C2D - return [] + getRunningJobs(engine?: string, environment?: string): Promise { + const selectSQL = ` + SELECT * FROM ${this.schema.name} WHERE dateFinished IS NULL + ` + return new Promise((resolve, reject) => { + this.db.all(selectSQL, (err, rows: any[] | undefined) => { + if (err) { + reject(err) + } else { + // also decode the internal data into job data + // get them all running + if (rows && rows.length > 0) { + const all: DBComputeJob[] = rows.map((row) => { + const body = generateJSONFromBlob(row.body) + delete row.body + const job: DBComputeJob = { ...row, ...body } + return job + }) + // filter them out + const filtered = all.filter((job) => { + let include = true + if (engine && engine !== job.clusterHash) { + include = false + } + if (environment && environment !== job.environment) { + include = false + } + return include + }) + resolve(filtered) + } else { + resolve([]) + } + } + }) + }) } } diff --git a/src/test/unit/compute.test.ts b/src/test/unit/compute.test.ts index 1668f32e3..da28415c9 100644 --- a/src/test/unit/compute.test.ts +++ b/src/test/unit/compute.test.ts @@ -10,8 +10,7 @@ import { DBComputeJob } from '../../@types/C2D/C2D.js' // import { computeAsset } from '../data/assets' -import { assert } from 'console' -import { expect } from 'chai' +import { assert, expect } from 'chai' import { convertArrayToString, convertStringToArray, @@ -21,21 +20,21 @@ import { describe('Compute Jobs Database', () => { let db: C2DDatabase = null let jobId: string = null + + const algorithm: ComputeAlgorithm = { + documentId: 'did:op:12345', + serviceId: '0x1828228' + } + const dataset: ComputeAsset = { + documentId: 'did:op:12345', + serviceId: '0x12345abc' + } before(async () => { const config = await getConfiguration(true) db = await new C2DDatabase(config.dbConfig, typesenseSchemas.c2dSchemas) }) it('should create a new C2D Job', async () => { - const algorithm: ComputeAlgorithm = { - documentId: 'did:op:12345', - serviceId: '0x1828228' - } - const dataset: ComputeAsset = { - documentId: 'did:op:12345', - serviceId: '0x12345abc' - } - const job: DBComputeJob = { owner: '0xe2DD09d719Da89e5a3D0F2549c7E24566e947260', jobId: null, @@ -72,7 +71,6 @@ describe('Compute Jobs Database', () => { it('should update job', async () => { const job = await db.getJob(jobId) - console.log('job before update', job) // will update some fields job.status = C2DStatusNumber.PullImage job.isRunning = true @@ -88,9 +86,48 @@ describe('Compute Jobs Database', () => { expect(updatedJob.statusText).to.be.equal(C2DStatusText.PullImage) }) + it('should get running jobs', async () => { + const job: DBComputeJob = { + owner: '0xe2DD09d719Da89e5a3D0F2549c7E24566e947261', + jobId: null, + dateCreated: null, + dateFinished: null, + status: C2DStatusNumber.JobStarted, + statusText: C2DStatusText.JobStarted, + results: null, + inputDID: ['did:op:1', 'did:op:2'], + expireTimestamp: 1, + + // internal structure + clusterHash: 'clusterHash', + configlogURL: 'http://localhost:8000', + publishlogURL: 'http://localhost:8000', + algologURL: 'http://localhost:8000', + outputsURL: 'http://localhost:8000', + stopRequested: false, + algorithm, + assets: [dataset], + isRunning: false, + isStarted: false, + containerImage: 'another container image' + } + + const jobId = await db.newJob(job) + assert(jobId, 'Missing jobId identifier') + const existing = await db.getRunningJobs() + expect(existing.length === 2, 'No running jobs were found!') + + // Create a filter + const withEnv = await db.getRunningJobs(null, 'some environment') + expect(withEnv.length === 0, 'No running jobs were found for this environment') + // delete it + const deleted = await db.deleteJob(jobId) + expect(deleted === true, `Job ${jobId} was not deleted!`) + }) + it('should delete the job by jobId', async () => { const deleted = await db.deleteJob(jobId) - expect(deleted === true, 'Job was not deleted!') + expect(deleted === true, `Job ${jobId} was not deleted!`) }) it('should convert array of strings to a string', () => { From 04ca13505a4f3e47be2709c188f2c38b0251f77b Mon Sep 17 00:00:00 2001 From: paulo-ocean Date: Wed, 23 Oct 2024 12:10:36 +0100 Subject: [PATCH 15/30] minor changes on DB struct, defaults --- src/components/database/sqliteCompute.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/components/database/sqliteCompute.ts b/src/components/database/sqliteCompute.ts index e7bd8a84b..c03e57f6c 100644 --- a/src/components/database/sqliteCompute.ts +++ b/src/components/database/sqliteCompute.ts @@ -114,18 +114,18 @@ export class SQLiteCompute implements ComputeDatabaseProvider { const createTableSQL = ` CREATE TABLE IF NOT EXISTS ${this.schema.name} ( owner TEXT, - did TEXT, + did TEXT DEFAULT NULL, jobId TEXT PRIMARY KEY, dateCreated TEXT, dateFinished TEXT DEFAULT NULL, status INTEGER, statusText TEXT, results BLOB, - inputDID TEXT, - algoDID TEXT, - agreementId TEXT, + inputDID TEXT DEFAULT NULL, + algoDID TEXT DEFAULT NULL, + agreementId TEXT DEFAULT NULL, expireTimestamp INTEGER, - environment TEXT, + environment TEXT DEFAULT NULL, body BLOB ); ` @@ -159,7 +159,6 @@ export class SQLiteCompute implements ComputeDatabaseProvider { ` const jobId = generateUniqueID() job.jobId = jobId - job.dateCreated = new Date().toISOString() return new Promise((resolve, reject) => { this.db.run( insertSQL, @@ -167,7 +166,7 @@ export class SQLiteCompute implements ComputeDatabaseProvider { job.owner, job.did, jobId, - job.dateCreated, + job.dateCreated || String(Date.now() / 1000), // seconds from epoch, job.status || C2DStatusNumber.JobStarted, job.statusText || C2DStatusText.JobStarted, job.inputDID ? convertArrayToString(job.inputDID) : job.inputDID, From 66d97f7b786a7f45d13de11feddf9453f019845e Mon Sep 17 00:00:00 2001 From: paulo-ocean Date: Wed, 23 Oct 2024 18:57:59 +0100 Subject: [PATCH 16/30] add logging --- src/components/database/TypesenseSchemas.ts | 6 +-- src/components/database/sqliteCompute.ts | 46 +++++++-------------- 2 files changed, 17 insertions(+), 35 deletions(-) diff --git a/src/components/database/TypesenseSchemas.ts b/src/components/database/TypesenseSchemas.ts index 5746d51f3..0cdf7ea5e 100644 --- a/src/components/database/TypesenseSchemas.ts +++ b/src/components/database/TypesenseSchemas.ts @@ -66,15 +66,15 @@ export const typesenseSchemas: TypesenseSchemas = { name: 'c2djobs', enable_nested_fields: true, fields: [ - // TO DO C2D + // not really needed because it will be SQL Lite { name: 'clusterHash', type: 'string', optional: false }, { name: 'configlogURL', type: 'string', optional: false }, { name: 'publishlogURL', type: 'string', optional: false }, { name: 'algologURL', type: 'string', optional: false }, { name: 'outputsURL', type: 'auto', optional: false }, { name: 'stopRequested', type: 'bool', optional: false }, - // { name: 'algorithm', type: 'ComputeAlgorithm', optional: false }, - // { name: 'assets', type: 'ComputeAsset[]', optional: false } + { name: 'algorithm', type: 'auto', optional: false }, + { name: 'assets', type: 'auto', optional: false }, { name: 'isRunning', type: 'bool', optional: false }, { name: 'isStarted', type: 'bool', optional: false }, { name: 'containerImage', type: 'string', optional: false } diff --git a/src/components/database/sqliteCompute.ts b/src/components/database/sqliteCompute.ts index c03e57f6c..26a51f5ec 100644 --- a/src/components/database/sqliteCompute.ts +++ b/src/components/database/sqliteCompute.ts @@ -15,39 +15,10 @@ interface ComputeDatabaseProvider { deleteJob(jobId: string): Promise } -/** - * export interface ComputeJob { - owner: string - did?: string - jobId: string - dateCreated: string - dateFinished: string - status: number - statusText: string - results: ComputeResult[] - inputDID?: string[] - algoDID?: string - agreementId?: string - expireTimestamp: number - environment?: string -} - */ - export function generateUniqueID(): string { return crypto.randomUUID().toString() } -// internal structure, added by DBComputeJob -// clusterHash: string -// configlogURL: string -// publishlogURL: string -// algologURL: string -// outputsURL: string -// stopRequested: boolean -// algorithm: ComputeAlgorithm -// assets: ComputeAsset[] -// isRunning: boolean -// isStarted: boolean -// containerImage: string + function getInternalStructure(job: DBComputeJob): any { const internalBlob = { clusterHash: job.clusterHash, @@ -72,7 +43,9 @@ export function generateJSONFromBlob(blob: any): Promise { return JSON.parse(blob.toString()) } +// we cannot store array of strings, so we use string separators instead export const STRING_SEPARATOR = '__,__' + export function convertArrayToString(array: string[]) { let str: string = '' for (let i = 0; i < array.length; i++) { @@ -177,8 +150,13 @@ export class SQLiteCompute implements ComputeDatabaseProvider { generateBlobFromJSON(job) ], (err) => { - if (err) reject(err) - else resolve(jobId) + if (err) { + DATABASE_LOGGER.error(err.message) + reject(err) + } else { + DATABASE_LOGGER.info('Successfully inserted job with id:' + jobId) + resolve(jobId) + } } ) }) @@ -192,6 +170,7 @@ export class SQLiteCompute implements ComputeDatabaseProvider { return new Promise((resolve, reject) => { this.db.get(selectSQL, [jobId], (err, row: any | undefined) => { if (err) { + DATABASE_LOGGER.error(err.message) reject(err) } else { // also decode the internal data into job data @@ -202,6 +181,7 @@ export class SQLiteCompute implements ComputeDatabaseProvider { const job: DBComputeJob = { ...row, ...body } resolve(job) } else { + DATABASE_LOGGER.error(`Could not find job id: ${jobId} in database!`) resolve(null) } } @@ -252,6 +232,7 @@ export class SQLiteCompute implements ComputeDatabaseProvider { return new Promise((resolve, reject) => { this.db.all(selectSQL, (err, rows: any[] | undefined) => { if (err) { + DATABASE_LOGGER.error(err.message) reject(err) } else { // also decode the internal data into job data @@ -276,6 +257,7 @@ export class SQLiteCompute implements ComputeDatabaseProvider { }) resolve(filtered) } else { + DATABASE_LOGGER.info('Could not find any running jobs!') resolve([]) } } From 36a8b956642836c50d05ef1358a5216fe9848bdf Mon Sep 17 00:00:00 2001 From: paulo-ocean Date: Thu, 24 Oct 2024 10:19:22 +0100 Subject: [PATCH 17/30] small refactor --- src/components/database/DatabaseFactory.ts | 15 ++++++--------- src/components/database/index.ts | 4 ++-- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/components/database/DatabaseFactory.ts b/src/components/database/DatabaseFactory.ts index e78a5e28a..870df32d6 100644 --- a/src/components/database/DatabaseFactory.ts +++ b/src/components/database/DatabaseFactory.ts @@ -4,7 +4,6 @@ import { AbstractDdoStateDatabase, AbstractIndexerDatabase, AbstractLogDatabase, - AbstractNonceDatabase, AbstractOrderDatabase } from './BaseDatabase.js' import { @@ -36,8 +35,6 @@ import { SQLLiteNonceDatabase } from './SQLLiteNonceDatabase.js' export class DatabaseFactory { private static databaseMap = { elasticsearch: { - nonce: (config: OceanNodeDBConfig) => - new SQLLiteNonceDatabase(config, typesenseSchemas.nonceSchemas), ddo: (config: OceanNodeDBConfig) => new ElasticsearchDdoDatabase(config, elasticSchemas.ddoSchemas), indexer: (config: OceanNodeDBConfig) => new ElasticsearchIndexerDatabase(config), @@ -49,8 +46,6 @@ export class DatabaseFactory { metadataQuery: () => new ElasticSearchMetadataQuery() }, typesense: { - nonce: (config: OceanNodeDBConfig) => - new SQLLiteNonceDatabase(config, typesenseSchemas.nonceSchemas), ddo: (config: OceanNodeDBConfig) => new TypesenseDdoDatabase(config, typesenseSchemas.ddoSchemas), indexer: (config: OceanNodeDBConfig) => @@ -81,16 +76,18 @@ export class DatabaseFactory { return databaseCreator(config) as T } - static createNonceDatabase(config: OceanNodeDBConfig): Promise { - return this.createDatabase('nonce', config) + static async createNonceDatabase( + config: OceanNodeDBConfig + ): Promise { + return await new SQLLiteNonceDatabase(config, typesenseSchemas.nonceSchemas) } static createDdoDatabase(config: OceanNodeDBConfig): Promise { return this.createDatabase('ddo', config) } - static createC2DDatabase(config: OceanNodeDBConfig): C2DDatabase { - return new C2DDatabase(config, typesenseSchemas.c2dSchemas) + static async createC2DDatabase(config: OceanNodeDBConfig): Promise { + return await new C2DDatabase(config, typesenseSchemas.c2dSchemas) } static createIndexerDatabase( diff --git a/src/components/database/index.ts b/src/components/database/index.ts index b5444db52..0684f150b 100644 --- a/src/components/database/index.ts +++ b/src/components/database/index.ts @@ -10,19 +10,19 @@ import { AbstractDdoStateDatabase, AbstractIndexerDatabase, AbstractLogDatabase, - AbstractNonceDatabase, AbstractOrderDatabase } from './BaseDatabase.js' import { C2DDatabase } from './C2DDatabase.js' import { DatabaseFactory } from './DatabaseFactory.js' import { ElasticsearchSchema } from './ElasticSchemas.js' +import { SQLLiteNonceDatabase } from './SQLLiteNonceDatabase.js' import { TypesenseSchema } from './TypesenseSchemas.js' export type Schema = ElasticsearchSchema | TypesenseSchema export class Database { ddo: AbstractDdoDatabase - nonce: AbstractNonceDatabase + nonce: SQLLiteNonceDatabase indexer: AbstractIndexerDatabase logs: AbstractLogDatabase order: AbstractOrderDatabase From 4a4c6d7d5d18b67bd0d5c79ab87b8fa92afad450 Mon Sep 17 00:00:00 2001 From: paulo-ocean Date: Thu, 24 Oct 2024 10:21:27 +0100 Subject: [PATCH 18/30] change log to inof --- src/components/database/SQLLiteNonceDatabase.ts | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/components/database/SQLLiteNonceDatabase.ts b/src/components/database/SQLLiteNonceDatabase.ts index 9e5de95b4..1eca067de 100644 --- a/src/components/database/SQLLiteNonceDatabase.ts +++ b/src/components/database/SQLLiteNonceDatabase.ts @@ -13,13 +13,7 @@ export class SQLLiteNonceDatabase extends AbstractNonceDatabase { constructor(config: OceanNodeDBConfig, schema: TypesenseSchema) { super(config, schema) return (async (): Promise => { - // Fall back to SQLite - DATABASE_LOGGER.logMessageWithEmoji( - 'Nonce Database initiated with SQLite provider', - true, - GENERIC_EMOJIS.EMOJI_CROSS_MARK, - LOG_LEVELS_STR.LEVEL_WARN - ) + DATABASE_LOGGER.info('Nonce Database initiated with SQLite provider') // Ensure the directory exists before instantiating SQLiteProvider const dbDir = path.dirname('databases/nonceDatabase.sqlite') From 28dd303091bdd20c688cc932654c8c14a82d1a06 Mon Sep 17 00:00:00 2001 From: paulo-ocean Date: Fri, 25 Oct 2024 11:36:44 +0100 Subject: [PATCH 19/30] wip: adding cron to delete expired storage and expired jobs --- src/components/database/C2DDatabase.ts | 15 ++++++- src/components/database/sqliteCompute.ts | 56 ++++++++++++++++++++++++ src/index.ts | 2 +- src/utils/cronjobs/scheduleCronJobs.ts | 41 +++++++++++++++++ src/utils/logging/logDeleteCron.ts | 23 ---------- 5 files changed, 112 insertions(+), 25 deletions(-) create mode 100644 src/utils/cronjobs/scheduleCronJobs.ts delete mode 100644 src/utils/logging/logDeleteCron.ts diff --git a/src/components/database/C2DDatabase.ts b/src/components/database/C2DDatabase.ts index 5b59da756..ff70d7803 100644 --- a/src/components/database/C2DDatabase.ts +++ b/src/components/database/C2DDatabase.ts @@ -1,6 +1,6 @@ import path from 'path' import fs from 'fs' -import { DBComputeJob } from '../../@types/C2D/C2D.js' +import { ComputeEnvironment, DBComputeJob } from '../../@types/C2D/C2D.js' import { SQLiteCompute } from './sqliteCompute.js' import { DATABASE_LOGGER } from '../../utils/logging/common.js' import { OceanNodeDBConfig } from '../../@types/OceanNode.js' @@ -62,4 +62,17 @@ export class C2DDatabase extends AbstractDatabase { async deleteJob(jobId: string): Promise { return await this.provider.deleteJob(jobId) } + + async cleanExpiredJobs(computeEnvironment?: ComputeEnvironment) { + const finishedOrExpired: DBComputeJob[] = + await this.provider.getFinishedOrExpiredJobs(computeEnvironment) + for (const job of finishedOrExpired) { + if (computeEnvironment && computeEnvironment.storageExpiry > Date.now() / 1000) { + // TODO + // delete the storage + } + // delete the job + await this.provider.deleteJob(job.jobId) + } + } } diff --git a/src/components/database/sqliteCompute.ts b/src/components/database/sqliteCompute.ts index 26a51f5ec..b9e3ec602 100644 --- a/src/components/database/sqliteCompute.ts +++ b/src/components/database/sqliteCompute.ts @@ -2,6 +2,7 @@ import { typesenseSchemas, TypesenseSchema } from './TypesenseSchemas.js' import { C2DStatusNumber, C2DStatusText, + ComputeEnvironment, type DBComputeJob } from '../../@types/C2D/C2D.js' import sqlite3, { RunResult } from 'sqlite3' @@ -13,6 +14,7 @@ interface ComputeDatabaseProvider { updateJob(job: DBComputeJob): Promise getRunningJobs(engine?: string, environment?: string): Promise deleteJob(jobId: string): Promise + getFinishedOrExpiredJobs(): Promise } export function generateUniqueID(): string { @@ -264,4 +266,58 @@ export class SQLiteCompute implements ComputeDatabaseProvider { }) }) } + + /** + * + * @param environment compute environment to check for + * + * All compute engines have compute environments, + * and each compute environment specifies how long the output produced by + * a job is held by the node, before being deleted. + * When a job expiry is overdue, the node will delete all storage used by that job, + * and also delete the job record from the database + * @returns array of eexpired jobs + */ + getFinishedOrExpiredJobs(environment?: ComputeEnvironment): Promise { + // get jobs that already finished (have results), for this environment, and clear storage + job if expired + const selectSQL = ` + SELECT * FROM ${this.schema.name} WHERE environment = ? AND dateFinished IS NOT NULL OR expireTimestamp > ? + ` + return new Promise((resolve, reject) => { + this.db.all( + selectSQL, + [environment.id, Date.now() / 1000], + (err, rows: any[] | undefined) => { + if (err) { + DATABASE_LOGGER.error(err.message) + reject(err) + } else { + // also decode the internal data into job data + // get them all running + if (rows && rows.length > 0) { + const all: DBComputeJob[] = rows.map((row) => { + const body = generateJSONFromBlob(row.body) + delete row.body + const job: DBComputeJob = { ...row, ...body } + return job + }) + if (!environment) { + resolve(all) + } + // filter them out + const filtered = all.filter((job) => { + return environment && environment.id === job.environment + }) + resolve(filtered) + } else { + DATABASE_LOGGER.info( + 'Could not find any jobs for the specified enviroment: ' + environment.id + ) + resolve([]) + } + } + } + ) + }) + } } diff --git a/src/index.ts b/src/index.ts index 1796a87b9..89c5b1e7c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -17,7 +17,7 @@ import { OCEAN_NODE_LOGGER } from './utils/logging/common.js' import path from 'path' import { fileURLToPath } from 'url' import cors from 'cors' -import { scheduleCronJobs } from './utils/logging/logDeleteCron.js' +import { scheduleCronJobs } from './utils/cronjobs/scheduleCronJobs.js' import { requestValidator } from './components/httpRoutes/requestValidator.js' import { hasValidDBConfiguration } from './utils/database.js' diff --git a/src/utils/cronjobs/scheduleCronJobs.ts b/src/utils/cronjobs/scheduleCronJobs.ts new file mode 100644 index 000000000..16d714d3f --- /dev/null +++ b/src/utils/cronjobs/scheduleCronJobs.ts @@ -0,0 +1,41 @@ +// scheduleCronJobs.ts + +import { Database } from '../../components/database/index.js' +import { OCEAN_NODE_LOGGER } from '../logging/common.js' +import * as cron from 'node-cron' + +export function scheduleCronJobs(dbconn: Database | null) { + scheduleDeleteLogsJob(dbconn) + scheduleCleanExpiredC2DJobs(dbconn) +} + +function scheduleDeleteLogsJob(dbconn: Database | null) { + // Schedule the cron job to run daily at midnight + cron.schedule('0 0 * * *', async () => { + if (dbconn && dbconn.logs) { + const deletedLogsNum = await dbconn.logs.deleteOldLogs() + OCEAN_NODE_LOGGER.logMessage( + `${deletedLogsNum} old logs deleted successfully.`, + true + ) + } else { + OCEAN_NODE_LOGGER.warn( + 'Logs CronJob: Database connection not established or logs instance not available.' + ) + } + }) +} + +function scheduleCleanExpiredC2DJobs(dbconn: Database | null) { + // Schedule the cron job to run daily at 5 minutes past midnight + cron.schedule('5 0 * * *', async () => { + if (dbconn && dbconn.c2d) { + await dbconn.c2d.cleanExpiredJobs() + OCEAN_NODE_LOGGER.info('old C2D jobs cleaned successfully.') + } else { + OCEAN_NODE_LOGGER.warn( + 'C2D CronJob: Database connection not established or C2D instance not available.' + ) + } + }) +} diff --git a/src/utils/logging/logDeleteCron.ts b/src/utils/logging/logDeleteCron.ts deleted file mode 100644 index 80bc7d49a..000000000 --- a/src/utils/logging/logDeleteCron.ts +++ /dev/null @@ -1,23 +0,0 @@ -// scheduleCronJobs.ts - -import { Database } from '../../components/database/index.js' -import { OCEAN_NODE_LOGGER } from './common.js' -import * as cron from 'node-cron' - -export function scheduleCronJobs(dbconn: Database | null) { - // Schedule the cron job to run daily at midnight - cron.schedule('0 0 * * *', async () => { - if (dbconn && dbconn.logs) { - const deletedLogsNum = await dbconn.logs.deleteOldLogs() - OCEAN_NODE_LOGGER.logMessage( - `${deletedLogsNum} old logs deleted successfully.`, - true - ) - } else { - OCEAN_NODE_LOGGER.logMessage( - 'Database connection not established or logs instance not available.', - true - ) - } - }) -} From ed5d2ec3a51d6facef32a0ef68be2b4c778da7c2 Mon Sep 17 00:00:00 2001 From: paulo-ocean Date: Mon, 28 Oct 2024 10:47:05 +0000 Subject: [PATCH 20/30] add test for getting expied and clean --- src/components/database/C2DDatabase.ts | 5 +- src/components/database/sqliteCompute.ts | 2 +- src/test/unit/compute.test.ts | 59 ++++++++++++++++++++++++ src/utils/cronjobs/scheduleCronJobs.ts | 34 +++++++------- 4 files changed, 82 insertions(+), 18 deletions(-) diff --git a/src/components/database/C2DDatabase.ts b/src/components/database/C2DDatabase.ts index ff70d7803..1f92fdf23 100644 --- a/src/components/database/C2DDatabase.ts +++ b/src/components/database/C2DDatabase.ts @@ -63,7 +63,8 @@ export class C2DDatabase extends AbstractDatabase { return await this.provider.deleteJob(jobId) } - async cleanExpiredJobs(computeEnvironment?: ComputeEnvironment) { + async cleanExpiredJobs(computeEnvironment?: ComputeEnvironment): Promise { + let cleaned = 0 const finishedOrExpired: DBComputeJob[] = await this.provider.getFinishedOrExpiredJobs(computeEnvironment) for (const job of finishedOrExpired) { @@ -73,6 +74,8 @@ export class C2DDatabase extends AbstractDatabase { } // delete the job await this.provider.deleteJob(job.jobId) + cleaned++ } + return cleaned } } diff --git a/src/components/database/sqliteCompute.ts b/src/components/database/sqliteCompute.ts index b9e3ec602..155ccd92a 100644 --- a/src/components/database/sqliteCompute.ts +++ b/src/components/database/sqliteCompute.ts @@ -281,7 +281,7 @@ export class SQLiteCompute implements ComputeDatabaseProvider { getFinishedOrExpiredJobs(environment?: ComputeEnvironment): Promise { // get jobs that already finished (have results), for this environment, and clear storage + job if expired const selectSQL = ` - SELECT * FROM ${this.schema.name} WHERE environment = ? AND dateFinished IS NOT NULL OR expireTimestamp > ? + SELECT * FROM ${this.schema.name} WHERE environment = ? AND dateFinished IS NOT NULL OR expireTimestamp < ? ` return new Promise((resolve, reject) => { this.db.all( diff --git a/src/test/unit/compute.test.ts b/src/test/unit/compute.test.ts index da28415c9..23f4e072f 100644 --- a/src/test/unit/compute.test.ts +++ b/src/test/unit/compute.test.ts @@ -7,6 +7,7 @@ import { C2DStatusText, ComputeAlgorithm, ComputeAsset, + ComputeEnvironment, DBComputeJob } from '../../@types/C2D/C2D.js' // import { computeAsset } from '../data/assets' @@ -16,6 +17,9 @@ import { convertStringToArray, STRING_SEPARATOR } from '../../components/database/sqliteCompute.js' +import { DEFAULT_TEST_TIMEOUT } from '../utils/utils.js' +import { sleep } from '../../utils/util.js' +import { ZeroAddress } from 'ethers' describe('Compute Jobs Database', () => { let db: C2DDatabase = null @@ -142,4 +146,59 @@ describe('Compute Jobs Database', () => { const str = 'did:op:1' + STRING_SEPARATOR + 'did:op:2' + STRING_SEPARATOR + 'did:op:3' expect(convertStringToArray(str)).to.deep.equal(expectedArray) }) + + it('should clean expired job', async function () { + this.timeout(DEFAULT_TEST_TIMEOUT * 3) + const job: DBComputeJob = { + owner: '0xe2DD09d719Da89e5a3D0F2549c7E24566e947265', + jobId: null, + dateCreated: null, + dateFinished: null, + status: C2DStatusNumber.JobStarted, + statusText: C2DStatusText.JobStarted, + results: null, + inputDID: ['did:op:1', 'did:op:2'], + expireTimestamp: DEFAULT_TEST_TIMEOUT - 1000, // after 19 seconds + environment: 'env_1_id', + + // internal structure + clusterHash: 'clusterHash', + configlogURL: 'http://localhost:8000', + publishlogURL: 'http://localhost:8000', + algologURL: 'http://localhost:8000', + outputsURL: 'http://localhost:8000', + stopRequested: false, + algorithm, + assets: [dataset], + isRunning: true, + isStarted: false, + containerImage: 'another container image' + } + + const jobId = await db.newJob(job) + assert(jobId, 'Missing jobId identifier') + console.log('Waiting for job to expire...') + await sleep(DEFAULT_TEST_TIMEOUT) // sleep enough for the job to expire + const env: ComputeEnvironment = { + id: 'env_1_id', // id of the environment (same as environment on DBComputeJob above) + cpuNumber: 1, + ramGB: 250, + diskGB: 250, + priceMin: 1, + desc: 'Test compute environment', + currentJobs: 0, + maxJobs: 10, + consumerAddress: '0xe2DD09d719Da89e5a3D0F2549c7E24566e947265', + storageExpiry: DEFAULT_TEST_TIMEOUT, + maxJobDuration: DEFAULT_TEST_TIMEOUT, + feeToken: ZeroAddress, + free: true + } + // delete it + const jobsDeleted = await db.cleanExpiredJobs(env) + expect(jobsDeleted).to.be.equal(1) + // try delete it again directly (fails if already deleted before) + const deleted = await db.deleteJob(jobId) + expect(deleted === false, `Job ${jobId} was already deleted!`) + }) }) diff --git a/src/utils/cronjobs/scheduleCronJobs.ts b/src/utils/cronjobs/scheduleCronJobs.ts index 16d714d3f..470ff3ab5 100644 --- a/src/utils/cronjobs/scheduleCronJobs.ts +++ b/src/utils/cronjobs/scheduleCronJobs.ts @@ -11,31 +11,33 @@ export function scheduleCronJobs(dbconn: Database | null) { function scheduleDeleteLogsJob(dbconn: Database | null) { // Schedule the cron job to run daily at midnight - cron.schedule('0 0 * * *', async () => { - if (dbconn && dbconn.logs) { + + if (dbconn && dbconn.logs) { + cron.schedule('0 0 * * *', async () => { const deletedLogsNum = await dbconn.logs.deleteOldLogs() OCEAN_NODE_LOGGER.logMessage( `${deletedLogsNum} old logs deleted successfully.`, true ) - } else { - OCEAN_NODE_LOGGER.warn( - 'Logs CronJob: Database connection not established or logs instance not available.' - ) - } - }) + }) + } else { + OCEAN_NODE_LOGGER.warn( + 'Logs CronJob: Database connection not established or logs instance not available (skipped).' + ) + } } function scheduleCleanExpiredC2DJobs(dbconn: Database | null) { // Schedule the cron job to run daily at 5 minutes past midnight - cron.schedule('5 0 * * *', async () => { - if (dbconn && dbconn.c2d) { + + if (dbconn && dbconn.c2d) { + cron.schedule('5 0 * * *', async () => { await dbconn.c2d.cleanExpiredJobs() OCEAN_NODE_LOGGER.info('old C2D jobs cleaned successfully.') - } else { - OCEAN_NODE_LOGGER.warn( - 'C2D CronJob: Database connection not established or C2D instance not available.' - ) - } - }) + }) + } else { + OCEAN_NODE_LOGGER.warn( + 'C2D CronJob: Database connection not established or C2D instance not available (skipped).' + ) + } } From bcd08786a01df8cf5aa38338de1d74fc316a52bf Mon Sep 17 00:00:00 2001 From: paulo-ocean Date: Mon, 28 Oct 2024 16:08:14 +0000 Subject: [PATCH 21/30] refactor deletion, add .env properties --- docs/env.md | 5 ++ src/components/c2d/compute_engine_base.ts | 8 ++- src/components/c2d/compute_engine_docker.ts | 5 ++ src/components/c2d/compute_engine_opf_k8.ts | 8 ++- src/components/database/C2DDatabase.ts | 48 ++++++++++--- src/components/database/sqliteCompute.ts | 75 +++++++++------------ src/test/unit/compute.test.ts | 59 ---------------- src/utils/constants.ts | 10 +++ src/utils/cronjobs/scheduleCronJobs.ts | 15 +++-- 9 files changed, 111 insertions(+), 122 deletions(-) diff --git a/docs/env.md b/docs/env.md index a20500736..11c285f29 100644 --- a/docs/env.md +++ b/docs/env.md @@ -83,3 +83,8 @@ Environmental variables are also tracked in `ENVIRONMENT_VARIABLES` within `src/ - `NODE1_PRIVATE_KEY`: Used on test environments, specifically CI, represents the private key for node 1. Example: `"0xfd5c1ccea015b6d663618850824154a3b3fb2882c46cefb05b9a93fea8c3d215"` - `NODE2_PRIVATE_KEY`: Used on test environments, specifically CI, represents the private key for node 2. Example: `"0x1263dc73bef43a9da06149c7e598f52025bf4027f1d6c13896b71e81bb9233fb"` + +## Cron Jobs + +- `CRON_DELETE_DB_LOGS`: Delete old logs from database Cron expression. Example: `0 0 * * *` (runs every day at midnight) +- `CRON_CLEANUP_C2D_STORAGE`: Clear c2d expired resources/storage and delete old jobs. Example: `*/5 * * * *` (runs every 5 minutes) diff --git a/src/components/c2d/compute_engine_base.ts b/src/components/c2d/compute_engine_base.ts index bff9c1b7b..c5c5a8932 100644 --- a/src/components/c2d/compute_engine_base.ts +++ b/src/components/c2d/compute_engine_base.ts @@ -5,7 +5,8 @@ import type { ComputeAlgorithm, ComputeAsset, ComputeJob, - ComputeOutput + ComputeOutput, + DBComputeJob } from '../../@types/C2D/C2D.js' import { C2DClusterType } from '../../@types/C2D/C2D.js' @@ -122,6 +123,11 @@ export class C2DEngine { public async getStreamableLogs(jobId: string): Promise { throw new Error(`Not implemented for this engine type`) } + + // eslint-disable-next-line require-await + public async cleanupExpiredStorage(job: DBComputeJob) { + throw new Error(`Not implemented`) + } } export class C2DEngineLocal extends C2DEngine { diff --git a/src/components/c2d/compute_engine_docker.ts b/src/components/c2d/compute_engine_docker.ts index 534293cd3..252af8cf2 100644 --- a/src/components/c2d/compute_engine_docker.ts +++ b/src/components/c2d/compute_engine_docker.ts @@ -634,6 +634,11 @@ export class C2DEngineDocker extends C2DEngine { if (!existsSync(baseFolder + '/tarData')) mkdirSync(baseFolder + '/tarData') // used to upload and download data } catch (e) {} } + + // clean up temporary files + public override async cleanupExpiredStorage(job: DBComputeJob) { + await this.cleanupJob(job) + } } // this uses the docker engine, but exposes only one env, the free one diff --git a/src/components/c2d/compute_engine_opf_k8.ts b/src/components/c2d/compute_engine_opf_k8.ts index f3799dc0f..fea32769c 100644 --- a/src/components/c2d/compute_engine_opf_k8.ts +++ b/src/components/c2d/compute_engine_opf_k8.ts @@ -5,7 +5,8 @@ import type { ComputeAlgorithm, ComputeAsset, ComputeJob, - ComputeOutput + ComputeOutput, + DBComputeJob } from '../../@types/C2D/C2D.js' import type { OPFK8ComputeStage, @@ -306,4 +307,9 @@ export class C2DEngineOPFK8 extends C2DEngine { } throw new Error(`getComputeJobStatus Failure`) } + + // eslint-disable-next-line require-await + public override async cleanupExpiredStorage(job: DBComputeJob) { + throw new Error(`Not implemented`) + } } diff --git a/src/components/database/C2DDatabase.ts b/src/components/database/C2DDatabase.ts index 1f92fdf23..838a96607 100644 --- a/src/components/database/C2DDatabase.ts +++ b/src/components/database/C2DDatabase.ts @@ -1,11 +1,13 @@ import path from 'path' import fs from 'fs' -import { ComputeEnvironment, DBComputeJob } from '../../@types/C2D/C2D.js' +import { DBComputeJob } from '../../@types/C2D/C2D.js' import { SQLiteCompute } from './sqliteCompute.js' import { DATABASE_LOGGER } from '../../utils/logging/common.js' import { OceanNodeDBConfig } from '../../@types/OceanNode.js' import { TypesenseSchema } from './TypesenseSchemas.js' import { AbstractDatabase } from './BaseDatabase.js' +import { OceanNode } from '../../OceanNode.js' +import { getDatabase } from '../../utils/database.js' export class C2DDatabase extends AbstractDatabase { private provider: SQLiteCompute @@ -63,19 +65,43 @@ export class C2DDatabase extends AbstractDatabase { return await this.provider.deleteJob(jobId) } - async cleanExpiredJobs(computeEnvironment?: ComputeEnvironment): Promise { + /** + * + * @param environment compute environment to check for + * + * All compute engines have compute environments, + * and each compute environment specifies how long the output produced by + * a job is held by the node, before being deleted. + * When a job expiry is overdue, the node will delete all storage used by that job, + * and also delete the job record from the database + * @returns array of eexpired jobs + */ + async cleanStorageExpiredJobs(): Promise { + const allEngines = await OceanNode.getInstance(await getDatabase()).getC2DEngines() + .engines + let cleaned = 0 - const finishedOrExpired: DBComputeJob[] = - await this.provider.getFinishedOrExpiredJobs(computeEnvironment) - for (const job of finishedOrExpired) { - if (computeEnvironment && computeEnvironment.storageExpiry > Date.now() / 1000) { - // TODO - // delete the storage + for (const engine of allEngines) { + const allEnvironments = await engine.getComputeEnvironments() + for (const computeEnvironment of allEnvironments) { + const finishedOrExpired: DBComputeJob[] = + await this.provider.getFinishedJobs(computeEnvironment) + for (const job of finishedOrExpired) { + if ( + computeEnvironment && + computeEnvironment.storageExpiry > Date.now() / 1000 + ) { + // TODO + // delete the storage + engine.cleanupExpiredStorage(job) + } + // delete the job + await this.provider.deleteJob(job.jobId) + cleaned++ + } } - // delete the job - await this.provider.deleteJob(job.jobId) - cleaned++ } + return cleaned } } diff --git a/src/components/database/sqliteCompute.ts b/src/components/database/sqliteCompute.ts index 155ccd92a..93ea947df 100644 --- a/src/components/database/sqliteCompute.ts +++ b/src/components/database/sqliteCompute.ts @@ -14,7 +14,7 @@ interface ComputeDatabaseProvider { updateJob(job: DBComputeJob): Promise getRunningJobs(engine?: string, environment?: string): Promise deleteJob(jobId: string): Promise - getFinishedOrExpiredJobs(): Promise + getFinishedJobs(): Promise } export function generateUniqueID(): string { @@ -267,57 +267,42 @@ export class SQLiteCompute implements ComputeDatabaseProvider { }) } - /** - * - * @param environment compute environment to check for - * - * All compute engines have compute environments, - * and each compute environment specifies how long the output produced by - * a job is held by the node, before being deleted. - * When a job expiry is overdue, the node will delete all storage used by that job, - * and also delete the job record from the database - * @returns array of eexpired jobs - */ - getFinishedOrExpiredJobs(environment?: ComputeEnvironment): Promise { + getFinishedJobs(environment?: ComputeEnvironment): Promise { // get jobs that already finished (have results), for this environment, and clear storage + job if expired const selectSQL = ` - SELECT * FROM ${this.schema.name} WHERE environment = ? AND dateFinished IS NOT NULL OR expireTimestamp < ? + SELECT * FROM ${this.schema.name} WHERE environment = ? AND dateFinished IS NOT NULL OR results IS NOT NULL ` return new Promise((resolve, reject) => { - this.db.all( - selectSQL, - [environment.id, Date.now() / 1000], - (err, rows: any[] | undefined) => { - if (err) { - DATABASE_LOGGER.error(err.message) - reject(err) - } else { - // also decode the internal data into job data - // get them all running - if (rows && rows.length > 0) { - const all: DBComputeJob[] = rows.map((row) => { - const body = generateJSONFromBlob(row.body) - delete row.body - const job: DBComputeJob = { ...row, ...body } - return job - }) - if (!environment) { - resolve(all) - } - // filter them out - const filtered = all.filter((job) => { - return environment && environment.id === job.environment - }) - resolve(filtered) - } else { - DATABASE_LOGGER.info( - 'Could not find any jobs for the specified enviroment: ' + environment.id - ) - resolve([]) + this.db.all(selectSQL, [environment.id], (err, rows: any[] | undefined) => { + if (err) { + DATABASE_LOGGER.error(err.message) + reject(err) + } else { + // also decode the internal data into job data + // get them all running + if (rows && rows.length > 0) { + const all: DBComputeJob[] = rows.map((row) => { + const body = generateJSONFromBlob(row.body) + delete row.body + const job: DBComputeJob = { ...row, ...body } + return job + }) + if (!environment) { + resolve(all) } + // filter them out + const filtered = all.filter((job) => { + return environment && environment.id === job.environment + }) + resolve(filtered) + } else { + DATABASE_LOGGER.info( + 'Could not find any jobs for the specified enviroment: ' + environment.id + ) + resolve([]) } } - ) + }) }) } } diff --git a/src/test/unit/compute.test.ts b/src/test/unit/compute.test.ts index 23f4e072f..da28415c9 100644 --- a/src/test/unit/compute.test.ts +++ b/src/test/unit/compute.test.ts @@ -7,7 +7,6 @@ import { C2DStatusText, ComputeAlgorithm, ComputeAsset, - ComputeEnvironment, DBComputeJob } from '../../@types/C2D/C2D.js' // import { computeAsset } from '../data/assets' @@ -17,9 +16,6 @@ import { convertStringToArray, STRING_SEPARATOR } from '../../components/database/sqliteCompute.js' -import { DEFAULT_TEST_TIMEOUT } from '../utils/utils.js' -import { sleep } from '../../utils/util.js' -import { ZeroAddress } from 'ethers' describe('Compute Jobs Database', () => { let db: C2DDatabase = null @@ -146,59 +142,4 @@ describe('Compute Jobs Database', () => { const str = 'did:op:1' + STRING_SEPARATOR + 'did:op:2' + STRING_SEPARATOR + 'did:op:3' expect(convertStringToArray(str)).to.deep.equal(expectedArray) }) - - it('should clean expired job', async function () { - this.timeout(DEFAULT_TEST_TIMEOUT * 3) - const job: DBComputeJob = { - owner: '0xe2DD09d719Da89e5a3D0F2549c7E24566e947265', - jobId: null, - dateCreated: null, - dateFinished: null, - status: C2DStatusNumber.JobStarted, - statusText: C2DStatusText.JobStarted, - results: null, - inputDID: ['did:op:1', 'did:op:2'], - expireTimestamp: DEFAULT_TEST_TIMEOUT - 1000, // after 19 seconds - environment: 'env_1_id', - - // internal structure - clusterHash: 'clusterHash', - configlogURL: 'http://localhost:8000', - publishlogURL: 'http://localhost:8000', - algologURL: 'http://localhost:8000', - outputsURL: 'http://localhost:8000', - stopRequested: false, - algorithm, - assets: [dataset], - isRunning: true, - isStarted: false, - containerImage: 'another container image' - } - - const jobId = await db.newJob(job) - assert(jobId, 'Missing jobId identifier') - console.log('Waiting for job to expire...') - await sleep(DEFAULT_TEST_TIMEOUT) // sleep enough for the job to expire - const env: ComputeEnvironment = { - id: 'env_1_id', // id of the environment (same as environment on DBComputeJob above) - cpuNumber: 1, - ramGB: 250, - diskGB: 250, - priceMin: 1, - desc: 'Test compute environment', - currentJobs: 0, - maxJobs: 10, - consumerAddress: '0xe2DD09d719Da89e5a3D0F2549c7E24566e947265', - storageExpiry: DEFAULT_TEST_TIMEOUT, - maxJobDuration: DEFAULT_TEST_TIMEOUT, - feeToken: ZeroAddress, - free: true - } - // delete it - const jobsDeleted = await db.cleanExpiredJobs(env) - expect(jobsDeleted).to.be.equal(1) - // try delete it again directly (fails if already deleted before) - const deleted = await db.deleteJob(jobId) - expect(deleted === false, `Job ${jobId} was already deleted!`) - }) }) diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 7127d8338..bee95d0ab 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -321,6 +321,16 @@ export const ENVIRONMENT_VARIABLES: Record = { name: 'DB_TYPE', value: process.env.DB_TYPE, required: false + }, + CRON_DELETE_DB_LOGS: { + name: 'CRON_DELETE_DB_LOGS', + value: process.env.CRON_DELETE_DB_LOGS, + required: false + }, + CRON_CLEANUP_C2D_STORAGE: { + name: 'CRON_CLEANUP_C2D_STORAGE', + value: process.env.CRON_CLEANUP_C2D_STORAGE, + required: false } } diff --git a/src/utils/cronjobs/scheduleCronJobs.ts b/src/utils/cronjobs/scheduleCronJobs.ts index 470ff3ab5..ceacf59ff 100644 --- a/src/utils/cronjobs/scheduleCronJobs.ts +++ b/src/utils/cronjobs/scheduleCronJobs.ts @@ -1,6 +1,7 @@ // scheduleCronJobs.ts import { Database } from '../../components/database/index.js' +import { ENVIRONMENT_VARIABLES } from '../constants.js' import { OCEAN_NODE_LOGGER } from '../logging/common.js' import * as cron from 'node-cron' @@ -13,7 +14,9 @@ function scheduleDeleteLogsJob(dbconn: Database | null) { // Schedule the cron job to run daily at midnight if (dbconn && dbconn.logs) { - cron.schedule('0 0 * * *', async () => { + const expression = + process.env[ENVIRONMENT_VARIABLES.CRON_DELETE_DB_LOGS.name] || '0 0 * * *' + cron.schedule(expression, async () => { const deletedLogsNum = await dbconn.logs.deleteOldLogs() OCEAN_NODE_LOGGER.logMessage( `${deletedLogsNum} old logs deleted successfully.`, @@ -28,12 +31,14 @@ function scheduleDeleteLogsJob(dbconn: Database | null) { } function scheduleCleanExpiredC2DJobs(dbconn: Database | null) { - // Schedule the cron job to run daily at 5 minutes past midnight + // Schedule the cron job to run every 5 minutes or whatever specified if (dbconn && dbconn.c2d) { - cron.schedule('5 0 * * *', async () => { - await dbconn.c2d.cleanExpiredJobs() - OCEAN_NODE_LOGGER.info('old C2D jobs cleaned successfully.') + const expression = + process.env[ENVIRONMENT_VARIABLES.CRON_CLEANUP_C2D_STORAGE.name] || '*/5 * * * *' + cron.schedule(expression, async () => { + const deleted = await dbconn.c2d.cleanStorageExpiredJobs() + OCEAN_NODE_LOGGER.info(`${deleted} old C2D jobs cleaned successfully.`) }) } else { OCEAN_NODE_LOGGER.warn( From ac5effab93e05cf09c81a3b6e9b5651a2f5ac7c8 Mon Sep 17 00:00:00 2001 From: paulo-ocean Date: Mon, 28 Oct 2024 17:50:48 +0000 Subject: [PATCH 22/30] small refactor --- src/components/c2d/compute_engine_base.ts | 2 +- src/components/c2d/compute_engine_docker.ts | 14 ++++++++++++-- src/components/c2d/compute_engine_opf_k8.ts | 2 +- src/components/database/C2DDatabase.ts | 10 ++++------ 4 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/components/c2d/compute_engine_base.ts b/src/components/c2d/compute_engine_base.ts index c5c5a8932..7c4eeaf49 100644 --- a/src/components/c2d/compute_engine_base.ts +++ b/src/components/c2d/compute_engine_base.ts @@ -125,7 +125,7 @@ export class C2DEngine { } // eslint-disable-next-line require-await - public async cleanupExpiredStorage(job: DBComputeJob) { + public async cleanupExpiredStorage(job: DBComputeJob): Promise { throw new Error(`Not implemented`) } } diff --git a/src/components/c2d/compute_engine_docker.ts b/src/components/c2d/compute_engine_docker.ts index 252af8cf2..48e91f4b1 100644 --- a/src/components/c2d/compute_engine_docker.ts +++ b/src/components/c2d/compute_engine_docker.ts @@ -30,6 +30,7 @@ import { createReadStream } from 'fs' import { pipeline } from 'node:stream/promises' +import { CORE_LOGGER } from '../../utils/logging/common.js' export class C2DEngineDocker extends C2DEngine { // eslint-disable-next-line no-useless-constructor @@ -636,8 +637,17 @@ export class C2DEngineDocker extends C2DEngine { } // clean up temporary files - public override async cleanupExpiredStorage(job: DBComputeJob) { - await this.cleanupJob(job) + public override async cleanupExpiredStorage(job: DBComputeJob): Promise { + try { + // delete the storage + await this.cleanupJob(job) + // delete the job + await this.db.deleteJob(job.jobId) + return true + } catch (e) { + CORE_LOGGER.error('Error cleaning up C2D storage and Job: ' + e.message) + } + return false } } diff --git a/src/components/c2d/compute_engine_opf_k8.ts b/src/components/c2d/compute_engine_opf_k8.ts index fea32769c..0ad8cc929 100644 --- a/src/components/c2d/compute_engine_opf_k8.ts +++ b/src/components/c2d/compute_engine_opf_k8.ts @@ -309,7 +309,7 @@ export class C2DEngineOPFK8 extends C2DEngine { } // eslint-disable-next-line require-await - public override async cleanupExpiredStorage(job: DBComputeJob) { + public override async cleanupExpiredStorage(job: DBComputeJob): Promise { throw new Error(`Not implemented`) } } diff --git a/src/components/database/C2DDatabase.ts b/src/components/database/C2DDatabase.ts index 838a96607..b73f8b3d6 100644 --- a/src/components/database/C2DDatabase.ts +++ b/src/components/database/C2DDatabase.ts @@ -92,16 +92,14 @@ export class C2DDatabase extends AbstractDatabase { computeEnvironment.storageExpiry > Date.now() / 1000 ) { // TODO - // delete the storage - engine.cleanupExpiredStorage(job) + + if (await engine.cleanupExpiredStorage(job)) { + cleaned++ + } } - // delete the job - await this.provider.deleteJob(job.jobId) - cleaned++ } } } - return cleaned } } From 710ec29bc03ba96ac77160de78f57b843bd34984 Mon Sep 17 00:00:00 2001 From: paulo-ocean Date: Tue, 29 Oct 2024 11:48:22 +0000 Subject: [PATCH 23/30] cleanup free job aftre download results --- src/components/c2d/compute_engine_base.ts | 90 ++++++++++++++------- src/components/c2d/compute_engine_docker.ts | 24 +++++- 2 files changed, 79 insertions(+), 35 deletions(-) diff --git a/src/components/c2d/compute_engine_base.ts b/src/components/c2d/compute_engine_base.ts index 7c4eeaf49..1b79b8724 100644 --- a/src/components/c2d/compute_engine_base.ts +++ b/src/components/c2d/compute_engine_base.ts @@ -10,7 +10,7 @@ import type { } from '../../@types/C2D/C2D.js' import { C2DClusterType } from '../../@types/C2D/C2D.js' -export class C2DEngine { +export abstract class C2DEngine { private clusterConfig: C2DClusterInfo public constructor(cluster: C2DClusterInfo) { this.clusterConfig = cluster @@ -27,19 +27,49 @@ export class C2DEngine { } // functions which need to be implemented by all engine types - // eslint-disable-next-line require-await - public async getComputeEnvironments(chainId?: number): Promise { - throw new Error(`Not implemented`) - } + public abstract getComputeEnvironments(chainId?: number): Promise - public async start(): Promise { - // overwritten by classes for start actions + // overwritten by classes for start actions + public start(): Promise { + throw new Error('Method not implemented.') } - public async stop(): Promise { - // overwritten by classes for cleanup + // overwritten by classes for cleanup + public stop(): Promise { + throw new Error('Method not implemented.') } + public abstract startComputeJob( + assets: ComputeAsset[], + algorithm: ComputeAlgorithm, + output: ComputeOutput, + environment: string, + owner?: string, + validUntil?: number, + chainId?: number, + agreementId?: string + ): Promise + + public abstract stopComputeJob( + jobId: string, + owner: string, + agreementId?: string + ): Promise + + public abstract getComputeJobStatus( + consumerAddress?: string, + agreementId?: string, + jobId?: string + ): Promise + + public abstract getComputeJobResult( + consumerAddress: string, + jobId: string, + index: number + ): Promise + + public abstract cleanupExpiredStorage(job: DBComputeJob): Promise + public async envExists( chainId: number, envIdWithHash?: string, @@ -78,8 +108,17 @@ export class C2DEngine { return null } - // eslint-disable-next-line require-await - public async startComputeJob( + public getStreamableLogs(jobId: string): Promise { + throw new Error(`Not implemented for this engine type`) + } +} + +export class C2DEngineLocal extends C2DEngine { + public getComputeEnvironments(chainId?: number): Promise { + throw new Error('Method not implemented.') + } + + public startComputeJob( assets: ComputeAsset[], algorithm: ComputeAlgorithm, output: ComputeOutput, @@ -89,48 +128,37 @@ export class C2DEngine { chainId?: number, agreementId?: string ): Promise { - throw new Error(`Not implemented`) + throw new Error('Method not implemented.') } - // eslint-disable-next-line require-await - public async stopComputeJob( + public stopComputeJob( jobId: string, owner: string, agreementId?: string ): Promise { - throw new Error(`Not implemented`) + throw new Error('Method not implemented.') } - // eslint-disable-next-line require-await - public async getComputeJobStatus( + public getComputeJobStatus( consumerAddress?: string, agreementId?: string, jobId?: string ): Promise { - throw new Error(`Not implemented`) + throw new Error('Method not implemented.') } - // eslint-disable-next-line require-await - public async getComputeJobResult( + public getComputeJobResult( consumerAddress: string, jobId: string, index: number ): Promise { - throw new Error(`Not implemented`) - } - - // eslint-disable-next-line require-await - public async getStreamableLogs(jobId: string): Promise { - throw new Error(`Not implemented for this engine type`) + throw new Error('Method not implemented.') } - // eslint-disable-next-line require-await - public async cleanupExpiredStorage(job: DBComputeJob): Promise { - throw new Error(`Not implemented`) + public cleanupExpiredStorage(job: DBComputeJob): Promise { + throw new Error('Method not implemented.') } -} -export class C2DEngineLocal extends C2DEngine { // eslint-disable-next-line no-useless-constructor public constructor(clusterConfig: C2DClusterInfo) { super(clusterConfig) diff --git a/src/components/c2d/compute_engine_docker.ts b/src/components/c2d/compute_engine_docker.ts index 48e91f4b1..8767cc7c0 100644 --- a/src/components/c2d/compute_engine_docker.ts +++ b/src/components/c2d/compute_engine_docker.ts @@ -33,9 +33,8 @@ import { pipeline } from 'node:stream/promises' import { CORE_LOGGER } from '../../utils/logging/common.js' export class C2DEngineDocker extends C2DEngine { - // eslint-disable-next-line no-useless-constructor private envs: ComputeEnvironment[] = [] - private db: C2DDatabase + protected db: C2DDatabase public docker: Dockerode private cronTimer: any private cronTime: number = 2000 @@ -158,7 +157,7 @@ export class C2DEngineDocker extends C2DEngine { } // eslint-disable-next-line require-await - private async getResults(jobId: string): Promise { + protected async getResults(jobId: string): Promise { const res: ComputeResult[] = [] let index = 0 const logStat = statSync( @@ -211,7 +210,7 @@ export class C2DEngineDocker extends C2DEngine { index: number ): Promise { const job = await this.db.getJob(jobId) - if (!job) { + if (!job || job.owner !== consumerAddress) { return null } const results = await this.getResults(jobId) @@ -638,6 +637,7 @@ export class C2DEngineDocker extends C2DEngine { // clean up temporary files public override async cleanupExpiredStorage(job: DBComputeJob): Promise { + if (!job) return false try { // delete the storage await this.cleanupJob(job) @@ -744,4 +744,20 @@ export class C2DEngineDockerFree extends C2DEngineDocker { agreementId ) } + + // eslint-disable-next-line require-await + public override async getComputeJobResult( + consumerAddress: string, + jobId: string, + index: number + ): Promise { + const result = await super.getComputeJobResult(consumerAddress, jobId, index) + if (result !== null) { + setTimeout(async () => { + const job = await this.db.getJob(jobId) + this.cleanupExpiredStorage(job) // + }, 5000) + } + return result + } } From 795efc6eeffb1d9344ba5fe2604254da39d3c2d1 Mon Sep 17 00:00:00 2001 From: paulo-ocean Date: Wed, 30 Oct 2024 10:09:22 +0000 Subject: [PATCH 24/30] some logs --- src/components/c2d/compute_engine_docker.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/components/c2d/compute_engine_docker.ts b/src/components/c2d/compute_engine_docker.ts index 8767cc7c0..9724ae0e2 100644 --- a/src/components/c2d/compute_engine_docker.ts +++ b/src/components/c2d/compute_engine_docker.ts @@ -515,6 +515,13 @@ export class C2DEngineDocker extends C2DEngine { }) } + private deleteOutputFolder(job: DBComputeJob) { + rmSync(this.getC2DConfig().tempFolder + '/' + job.jobId + '/data/outputs/', { + recursive: true, + force: true + }) + } + private async uploadData( job: DBComputeJob ): Promise<{ status: C2DStatusNumber; statusText: C2DStatusText }> { @@ -638,9 +645,12 @@ export class C2DEngineDocker extends C2DEngine { // clean up temporary files public override async cleanupExpiredStorage(job: DBComputeJob): Promise { if (!job) return false + CORE_LOGGER.info('Cleaning up C2D storage for Job: ' + job.jobId) try { // delete the storage await this.cleanupJob(job) + // delete output folders + await this.deleteOutputFolder(job) // delete the job await this.db.deleteJob(job.jobId) return true @@ -755,7 +765,10 @@ export class C2DEngineDockerFree extends C2DEngineDocker { if (result !== null) { setTimeout(async () => { const job = await this.db.getJob(jobId) - this.cleanupExpiredStorage(job) // + CORE_LOGGER.info( + 'Cleaning storage for free container, after retrieving results...' + ) + this.cleanupExpiredStorage(job) // clean the storage, do not wait for it to expire }, 5000) } return result From 3c99d71f731e86ce0997ad4a691d66b51209bcaf Mon Sep 17 00:00:00 2001 From: paulo-ocean Date: Thu, 31 Oct 2024 10:10:08 +0000 Subject: [PATCH 25/30] more logs + small refactor --- src/components/c2d/compute_engine_docker.ts | 57 ++++++++++++++------- src/components/database/sqliteCompute.ts | 9 ++-- 2 files changed, 45 insertions(+), 21 deletions(-) diff --git a/src/components/c2d/compute_engine_docker.ts b/src/components/c2d/compute_engine_docker.ts index 9724ae0e2..370305e1a 100644 --- a/src/components/c2d/compute_engine_docker.ts +++ b/src/components/c2d/compute_engine_docker.ts @@ -31,6 +31,7 @@ import { } from 'fs' import { pipeline } from 'node:stream/promises' import { CORE_LOGGER } from '../../utils/logging/common.js' +import { generateUniqueID } from '../database/sqliteCompute.js' export class C2DEngineDocker extends C2DEngine { private envs: ComputeEnvironment[] = [] @@ -46,7 +47,7 @@ export class C2DEngineDocker extends C2DEngine { try { this.docker = new Dockerode({ socketPath: clusterConfig.connection.socketPath }) } catch (e) { - console.log(e) + CORE_LOGGER.error('Could not create Docker container: ' + e.message) } } if ( @@ -61,15 +62,21 @@ export class C2DEngineDocker extends C2DEngine { port: clusterConfig.connection.port }) } catch (e) { - console.log(e) + CORE_LOGGER.error('Could not create Docker container: ' + e.message) } } // TO DO C2D - create envs try { if (!existsSync(clusterConfig.tempFolder)) mkdirSync(clusterConfig.tempFolder, { recursive: true }) - } catch (e) {} - this.setNewTimer() + } catch (e) { + CORE_LOGGER.error( + 'Could not create Docker container temporary folders: ' + e.message + ) + } + // only when we got the first request to start a compute job, + // no need to start doing this right away + // this.setNewTimer() } // eslint-disable-next-line require-await @@ -95,18 +102,22 @@ export class C2DEngineDocker extends C2DEngine { agreementId?: string ): Promise { if (!this.docker) return [] - const jobId = create256Hash( - JSON.stringify({ - assets, - algorithm, - output, - environment, - owner, - validUntil, - chainId, - agreementId - }) - ) + + const jobId = generateUniqueID() + // NOTE: this does not generate a unique ID... + // if i send 2 times the same startComputeJob parameters i get the same ID twice + // const jobId = create256Hash( + // JSON.stringify({ + // assets, + // algorithm, + // output, + // environment, + // owner, + // validUntil, + // chainId, + // agreementId + // }) + // ) // TO DO C2D - Check image, check arhitecture, etc let { image } = algorithm.meta.container if (algorithm.meta.container.checksum) @@ -140,10 +151,20 @@ export class C2DEngineDocker extends C2DEngine { isStarted: false } await this.makeJobFolders(job) - this.db.newJob(job) + // make sure we actually were able to insert on DB + const addedId = await this.db.newJob(job) + if (!addedId) { + return [] + } + + // only now set the timer + if (!this.cronTimer) { + this.setNewTimer() + } const cjob: ComputeJob = JSON.parse(JSON.stringify(job)) as ComputeJob // we add cluster hash to user output - cjob.jobId = this.getC2DConfig().hash + '-' + cjob.jobId + // cjob.jobId = this.getC2DConfig().hash + '-' + cjob.jobId + cjob.jobId = jobId return [cjob] } diff --git a/src/components/database/sqliteCompute.ts b/src/components/database/sqliteCompute.ts index 93ea947df..57ff414b0 100644 --- a/src/components/database/sqliteCompute.ts +++ b/src/components/database/sqliteCompute.ts @@ -132,7 +132,7 @@ export class SQLiteCompute implements ComputeDatabaseProvider { ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?); ` - const jobId = generateUniqueID() + const jobId = job.jobId || generateUniqueID() job.jobId = jobId return new Promise((resolve, reject) => { this.db.run( @@ -153,7 +153,7 @@ export class SQLiteCompute implements ComputeDatabaseProvider { ], (err) => { if (err) { - DATABASE_LOGGER.error(err.message) + DATABASE_LOGGER.error('Could not insert C2D job on DB: ' + err.message) reject(err) } else { DATABASE_LOGGER.info('Successfully inserted job with id:' + jobId) @@ -255,11 +255,14 @@ export class SQLiteCompute implements ComputeDatabaseProvider { if (environment && environment !== job.environment) { include = false } + if (!job.isRunning) { + include = false + } return include }) resolve(filtered) } else { - DATABASE_LOGGER.info('Could not find any running jobs!') + DATABASE_LOGGER.info('Could not find any running C2D jobs!') resolve([]) } } From 5aae86b7df095a87a918ef5e90f11dcb75fbfcdd Mon Sep 17 00:00:00 2001 From: paulo-ocean Date: Tue, 5 Nov 2024 08:35:33 +0000 Subject: [PATCH 26/30] fix: check for file object --- src/components/c2d/compute_engine_docker.ts | 44 +++++++++++---------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/src/components/c2d/compute_engine_docker.ts b/src/components/c2d/compute_engine_docker.ts index 370305e1a..6df4262ff 100644 --- a/src/components/c2d/compute_engine_docker.ts +++ b/src/components/c2d/compute_engine_docker.ts @@ -577,26 +577,30 @@ export class C2DEngineDocker extends C2DEngine { for (const i in job.assets) { const asset = job.assets[i] console.log(asset) - const storage = Storage.getStorageClass(asset.fileObject, config) - const fileInfo = await storage.getFileInfo({ - type: storage.getStorageType(asset.fileObject) - }) - const fullPath = - this.getC2DConfig().tempFolder + - '/' + - job.jobId + - '/data/inputs/' + - fileInfo[0].name - try { - await pipeline( - (await storage.getReadableStream()).stream, - createWriteStream(fullPath) - ) - } catch (e) { - console.log(e) - return { - status: C2DStatusNumber.DataProvisioningFailed, - statusText: C2DStatusText.DataProvisioningFailed + // without this check it would break if no fileObject is present + if (asset.fileObject) { + const storage = Storage.getStorageClass(asset.fileObject, config) + const fileInfo = await storage.getFileInfo({ + type: storage.getStorageType(asset.fileObject) + }) + const fullPath = + this.getC2DConfig().tempFolder + + '/' + + job.jobId + + '/data/inputs/' + + fileInfo[0].name + + try { + await pipeline( + (await storage.getReadableStream()).stream, + createWriteStream(fullPath) + ) + } catch (e) { + console.log(e) + return { + status: C2DStatusNumber.DataProvisioningFailed, + statusText: C2DStatusText.DataProvisioningFailed + } } } } From ccf3d29f6938e72ebba456241b1a6c39c3653a15 Mon Sep 17 00:00:00 2001 From: paulo-ocean Date: Tue, 5 Nov 2024 11:02:48 +0000 Subject: [PATCH 27/30] wip: add env vars for docket compute envs --- src/utils/config.ts | 50 ++++++++++++++++++++++++++++++++++++++++-- src/utils/constants.ts | 10 +++++++++ 2 files changed, 58 insertions(+), 2 deletions(-) diff --git a/src/utils/config.ts b/src/utils/config.ts index 207137bb5..35e59ae7a 100644 --- a/src/utils/config.ts +++ b/src/utils/config.ts @@ -1,5 +1,5 @@ import type { DenyList, OceanNodeConfig, OceanNodeKeys } from '../@types/OceanNode' -import type { C2DClusterInfo } from '../@types/C2D/C2D.js' +import type { C2DClusterInfo, ComputeEnvironment } from '../@types/C2D/C2D.js' import { C2DClusterType } from '../@types/C2D/C2D.js' import { createFromPrivKey } from '@libp2p/peer-id-factory' import { keys } from '@libp2p/crypto' @@ -13,7 +13,7 @@ import { defaultBootstrapAddresses, knownUnsafeURLs } from '../utils/constants.j import { LOG_LEVELS_STR, GENERIC_EMOJIS, getLoggerLevelEmoji } from './logging/Logger.js' import { RPCS } from '../@types/blockchain' -import { getAddress, Wallet } from 'ethers' +import { getAddress, Wallet, ZeroAddress } from 'ethers' import { FeeAmount, FeeStrategy, FeeTokens } from '../@types/Fees' import { getOceanArtifactsAdresses, @@ -350,6 +350,52 @@ function getC2DClusterEnvironment(isStartup?: boolean): C2DClusterInfo[] { return clusters } +// TODO C2D v2.0 +// eslint-disable-next-line no-unused-vars +function getDockerFreeComputeOptions( + clusterHash: string, + isStartup?: boolean +): ComputeEnvironment { + const defaultOptions: ComputeEnvironment = { + id: `${clusterHash}-free`, + cpuNumber: 1, + cpuType: '', + gpuNumber: 0, + ramGB: 1, + diskGB: 1, + priceMin: 0, + desc: 'Free', + currentJobs: 0, + maxJobs: 1, + consumerAddress: '', + storageExpiry: 600, + maxJobDuration: 30, + feeToken: ZeroAddress, + free: true + } + + if (existsEnvironmentVariable(ENVIRONMENT_VARIABLES.DOCKER_FREE_COMPUTE, isStartup)) { + try { + const options: ComputeEnvironment = JSON.parse( + process.env.DOCKER_FREE_COMPUTE + ) as ComputeEnvironment + return options + } catch (error) { + CONFIG_LOGGER.logMessageWithEmoji( + `Invalid "${ENVIRONMENT_VARIABLES.DOCKER_FREE_COMPUTE.name}" env variable => ${process.env.DOCKER_FREE_COMPUTE}...`, + true, + GENERIC_EMOJIS.EMOJI_CROSS_MARK, + LOG_LEVELS_STR.LEVEL_ERROR + ) + } + } else { + CONFIG_LOGGER.warn( + `No options for ${ENVIRONMENT_VARIABLES.DOCKER_FREE_COMPUTE.name} were specified, using defaults.` + ) + } + return defaultOptions +} + // connect interfaces (p2p or/and http) function getNodeInterfaces(isStartup: boolean = false) { let interfaces: string[] = ['P2P', 'HTTP'] diff --git a/src/utils/constants.ts b/src/utils/constants.ts index bee95d0ab..4b0819675 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -331,6 +331,16 @@ export const ENVIRONMENT_VARIABLES: Record = { name: 'CRON_CLEANUP_C2D_STORAGE', value: process.env.CRON_CLEANUP_C2D_STORAGE, required: false + }, + DOCKER_COMPUTE_ENVS: { + name: 'DOCKER_COMPUTE_ENVS', + value: process.env.DOCKER_COMPUTE_ENVS, + required: false + }, + DOCKER_FREE_COMPUTE: { + name: 'DOCKER_FREE_COMPUTE', + value: process.env.DOCKER_FREE_COMPUTE, + required: false } } From d87275371888173074e49341cccf49a4934d126a Mon Sep 17 00:00:00 2001 From: paulo-ocean Date: Tue, 5 Nov 2024 11:03:12 +0000 Subject: [PATCH 28/30] wip: add env vars for docket compute envs --- src/utils/config.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/utils/config.ts b/src/utils/config.ts index 35e59ae7a..59aed1898 100644 --- a/src/utils/config.ts +++ b/src/utils/config.ts @@ -669,6 +669,7 @@ function configChanged(previous: OceanNodeConfig, current: OceanNodeConfig): boo // useful for debugging purposes export async function printCurrentConfig() { const conf = await getConfiguration(true) + conf.keys.privateKey = '[*** HIDDEN CONTENT ***]' // hide private key console.log(JSON.stringify(conf, null, 4)) } From 40121ab9479df6ab1e6d845ba624f0f8f88c7bd8 Mon Sep 17 00:00:00 2001 From: paulo-ocean Date: Wed, 6 Nov 2024 15:52:35 +0000 Subject: [PATCH 29/30] add config options --- src/@types/C2D/C2D.ts | 12 ++++++++++++ src/utils/config.ts | 40 +++++++++++++++++++++++++++++++++++++--- src/utils/constants.ts | 6 +++--- 3 files changed, 52 insertions(+), 6 deletions(-) diff --git a/src/@types/C2D/C2D.ts b/src/@types/C2D/C2D.ts index f56776f0b..6b66c51fa 100644 --- a/src/@types/C2D/C2D.ts +++ b/src/@types/C2D/C2D.ts @@ -41,6 +41,18 @@ export interface ComputeEnvironment { free: boolean } +export interface C2DDockerConfig { + socketPath: string + protocol: string + host: string + port: number + caPath: string + certPath: string + keyPath: string + environments: ComputeEnvironment[] + freeComputeOptions?: ComputeEnvironment +} + export interface ComputeEnvByChain { [chainId: number]: ComputeEnvironment[] } diff --git a/src/utils/config.ts b/src/utils/config.ts index 59aed1898..881d9eac6 100644 --- a/src/utils/config.ts +++ b/src/utils/config.ts @@ -1,5 +1,9 @@ import type { DenyList, OceanNodeConfig, OceanNodeKeys } from '../@types/OceanNode' -import type { C2DClusterInfo, ComputeEnvironment } from '../@types/C2D/C2D.js' +import type { + C2DClusterInfo, + ComputeEnvironment, + C2DDockerConfig +} from '../@types/C2D/C2D.js' import { C2DClusterType } from '../@types/C2D/C2D.js' import { createFromPrivKey } from '@libp2p/peer-id-factory' import { keys } from '@libp2p/crypto' @@ -327,7 +331,7 @@ function getC2DClusterEnvironment(isStartup?: boolean): C2DClusterInfo[] { } } // docker clusters - const dockerConfig = { + const dockerConfig: C2DDockerConfig = { socketPath: getEnvValue(process.env.DOCKER_SOCKET_PATH, null), protocol: getEnvValue(process.env.DOCKER_PROTOCOL, null), host: getEnvValue(process.env.DOCKER_HOST, null), @@ -335,10 +339,12 @@ function getC2DClusterEnvironment(isStartup?: boolean): C2DClusterInfo[] { caPath: getEnvValue(process.env.DOCKER_CA_PATH, null), certPath: getEnvValue(process.env.DOCKER_CERT_PATH, null), keyPath: getEnvValue(process.env.DOCKER_KEY_PATH, null), - environments: getEnvValue(process.env.DOCKER_COMPUTE_ENVIRONMENTS, null) + environments: getDockerComputeEnvironments(isStartup) } if (dockerConfig.socketPath || dockerConfig.host) { const hash = create256Hash(JSON.stringify(dockerConfig)) + // get env values + dockerConfig.freeComputeOptions = getDockerFreeComputeOptions(hash, isStartup) clusters.push({ connection: dockerConfig, hash, @@ -396,6 +402,34 @@ function getDockerFreeComputeOptions( return defaultOptions } +function getDockerComputeEnvironments(isStartup?: boolean): ComputeEnvironment[] { + if ( + existsEnvironmentVariable( + ENVIRONMENT_VARIABLES.DOCKER_COMPUTE_ENVIRONMENTS, + isStartup + ) + ) { + try { + const options: ComputeEnvironment[] = JSON.parse( + process.env.DOCKER_COMPUTE_ENVIRONMENTS + ) as ComputeEnvironment[] + return options + } catch (error) { + CONFIG_LOGGER.logMessageWithEmoji( + `Invalid "${ENVIRONMENT_VARIABLES.DOCKER_COMPUTE_ENVIRONMENTS.name}" env variable => ${process.env.DOCKER_COMPUTE_ENVIRONMENTS}...`, + true, + GENERIC_EMOJIS.EMOJI_CROSS_MARK, + LOG_LEVELS_STR.LEVEL_ERROR + ) + } + } else { + CONFIG_LOGGER.warn( + `No options for ${ENVIRONMENT_VARIABLES.DOCKER_COMPUTE_ENVIRONMENTS.name} were specified.` + ) + } + return null +} + // connect interfaces (p2p or/and http) function getNodeInterfaces(isStartup: boolean = false) { let interfaces: string[] = ['P2P', 'HTTP'] diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 4b0819675..ab99722f8 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -332,9 +332,9 @@ export const ENVIRONMENT_VARIABLES: Record = { value: process.env.CRON_CLEANUP_C2D_STORAGE, required: false }, - DOCKER_COMPUTE_ENVS: { - name: 'DOCKER_COMPUTE_ENVS', - value: process.env.DOCKER_COMPUTE_ENVS, + DOCKER_COMPUTE_ENVIRONMENTS: { + name: 'DOCKER_COMPUTE_ENVIRONMENTS', + value: process.env.DOCKER_COMPUTE_ENVIRONMENTS, required: false }, DOCKER_FREE_COMPUTE: { From 3bba50243db80398ffb9c0606421de4168f2e580 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Mon, 18 Nov 2024 07:20:01 +0200 Subject: [PATCH 30/30] wip --- package-lock.json | 5495 +++++++++++++---- package.json | 3 + resources/c2d_k8_yaml/algo-job-template.yaml | 31 + resources/c2d_k8_yaml/configmap-template.yaml | 6 + .../c2d_k8_yaml/configure-job-template.yaml | 31 + .../c2d_k8_yaml/publish-job-template.yaml | 31 + resources/c2d_k8_yaml/pvc.yaml | 12 + src/@types/C2D/C2D.ts | 19 +- src/components/c2d/compute_engine_k8.ts | 441 ++ src/components/c2d/compute_engines.ts | 8 + src/utils/config.ts | 52 +- 11 files changed, 5012 insertions(+), 1117 deletions(-) create mode 100644 resources/c2d_k8_yaml/algo-job-template.yaml create mode 100644 resources/c2d_k8_yaml/configmap-template.yaml create mode 100644 resources/c2d_k8_yaml/configure-job-template.yaml create mode 100644 resources/c2d_k8_yaml/publish-job-template.yaml create mode 100644 resources/c2d_k8_yaml/pvc.yaml create mode 100644 src/components/c2d/compute_engine_k8.ts diff --git a/package-lock.json b/package-lock.json index cbaaeccd1..aee171f49 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@chainsafe/libp2p-noise": "^15.1.0", "@chainsafe/libp2p-yamux": "^6.0.2", "@elastic/elasticsearch": "^8.14.0", + "@kubernetes/client-node": "^0.22.2", "@libp2p/autonat": "^1.1.1", "@libp2p/bootstrap": "^10.1.1", "@libp2p/circuit-relay-v2": "^1.1.1", @@ -58,6 +59,7 @@ "ethers": "^6.8.1", "express": "^4.21.1", "hyperdiff": "^2.0.16", + "i": "^0.3.7", "ip": "^2.0.1", "it-pipe": "^3.0.1", "libp2p": "^1.8.0", @@ -65,6 +67,7 @@ "lzma-purejs-requirejs": "^1.0.0", "n3": "^1.17.2", "node-cron": "^3.0.3", + "npm": "^10.9.0", "private-ip": "^3.0.2", "rdf-utils-fs": "^3.0.0", "rdf-validate-shacl": "^0.5.5", @@ -2397,8 +2400,7 @@ "node_modules/@gar/promisify": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", - "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", - "optional": true + "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==" }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.13", @@ -2690,6 +2692,28 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@jsep-plugin/assignment": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@jsep-plugin/assignment/-/assignment-1.3.0.tgz", + "integrity": "sha512-VVgV+CXrhbMI3aSusQyclHkenWSAm95WaiKrMxRFam3JSUiIaQjoMIw2sEs/OX4XifnqeQUN4DYbJjlA8EfktQ==", + "engines": { + "node": ">= 10.16.0" + }, + "peerDependencies": { + "jsep": "^0.4.0||^1.0.0" + } + }, + "node_modules/@jsep-plugin/regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@jsep-plugin/regex/-/regex-1.0.4.tgz", + "integrity": "sha512-q7qL4Mgjs1vByCaTnDFcBnV9HS7GVPJX5vyVoCgZHNSC9rjwIlmbXG5sUuorR5ndfHAIlJ8pVStxvjXHbNvtUg==", + "engines": { + "node": ">= 10.16.0" + }, + "peerDependencies": { + "jsep": "^0.4.0||^1.0.0" + } + }, "node_modules/@kikobeats/time-span": { "version": "1.0.3", "license": "MIT", @@ -2697,6 +2721,26 @@ "node": ">= 18" } }, + "node_modules/@kubernetes/client-node": { + "version": "0.22.2", + "resolved": "https://registry.npmjs.org/@kubernetes/client-node/-/client-node-0.22.2.tgz", + "integrity": "sha512-PPyzUVunPtgISnWNkCTSLp1SoZ3I13XOanrc8XAQRKp8XTnDF7VT9Sf3/haFmgnpONe4ORCtqrWueGp5fl8yzw==", + "dependencies": { + "byline": "^5.0.0", + "isomorphic-ws": "^5.0.0", + "js-yaml": "^4.1.0", + "jsonpath-plus": "^10.0.0", + "request": "^2.88.0", + "rfc4648": "^1.3.0", + "stream-buffers": "^3.0.2", + "tar": "^7.0.0", + "tslib": "^2.4.1", + "ws": "^8.18.0" + }, + "optionalDependencies": { + "openid-client": "^6.1.3" + } + }, "node_modules/@leichtgewicht/ip-codec": { "version": "2.0.4", "license": "MIT" @@ -4059,7 +4103,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", - "optional": true, "dependencies": { "@gar/promisify": "^1.0.1", "semver": "^7.3.5" @@ -4070,7 +4113,6 @@ "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", "deprecated": "This functionality has been moved to @npmcli/fs", - "optional": true, "dependencies": { "mkdirp": "^1.0.4", "rimraf": "^3.0.2" @@ -4999,6 +5041,168 @@ "node": ">=6" } }, + "node_modules/@semantic-release/npm/node_modules/npm": { + "version": "8.19.4", + "resolved": "https://registry.npmjs.org/npm/-/npm-8.19.4.tgz", + "integrity": "sha512-3HANl8i9DKnUA89P4KEgVNN28EjSeDCmvEqbzOAuxCFDzdBZzjUl99zgnGpOUumvW5lvJo2HKcjrsc+tfyv1Hw==", + "bundleDependencies": [ + "@isaacs/string-locale-compare", + "@npmcli/arborist", + "@npmcli/ci-detect", + "@npmcli/config", + "@npmcli/fs", + "@npmcli/map-workspaces", + "@npmcli/package-json", + "@npmcli/run-script", + "abbrev", + "archy", + "cacache", + "chalk", + "chownr", + "cli-columns", + "cli-table3", + "columnify", + "fastest-levenshtein", + "fs-minipass", + "glob", + "graceful-fs", + "hosted-git-info", + "ini", + "init-package-json", + "is-cidr", + "json-parse-even-better-errors", + "libnpmaccess", + "libnpmdiff", + "libnpmexec", + "libnpmfund", + "libnpmhook", + "libnpmorg", + "libnpmpack", + "libnpmpublish", + "libnpmsearch", + "libnpmteam", + "libnpmversion", + "make-fetch-happen", + "minimatch", + "minipass", + "minipass-pipeline", + "mkdirp", + "mkdirp-infer-owner", + "ms", + "node-gyp", + "nopt", + "npm-audit-report", + "npm-install-checks", + "npm-package-arg", + "npm-pick-manifest", + "npm-profile", + "npm-registry-fetch", + "npm-user-validate", + "npmlog", + "opener", + "p-map", + "pacote", + "parse-conflict-json", + "proc-log", + "qrcode-terminal", + "read", + "read-package-json", + "read-package-json-fast", + "readdir-scoped-modules", + "rimraf", + "semver", + "ssri", + "tar", + "text-table", + "tiny-relative-date", + "treeverse", + "validate-npm-package-name", + "which", + "write-file-atomic" + ], + "dependencies": { + "@isaacs/string-locale-compare": "^1.1.0", + "@npmcli/arborist": "^5.6.3", + "@npmcli/ci-detect": "^2.0.0", + "@npmcli/config": "^4.2.1", + "@npmcli/fs": "^2.1.0", + "@npmcli/map-workspaces": "^2.0.3", + "@npmcli/package-json": "^2.0.0", + "@npmcli/run-script": "^4.2.1", + "abbrev": "~1.1.1", + "archy": "~1.0.0", + "cacache": "^16.1.3", + "chalk": "^4.1.2", + "chownr": "^2.0.0", + "cli-columns": "^4.0.0", + "cli-table3": "^0.6.2", + "columnify": "^1.6.0", + "fastest-levenshtein": "^1.0.12", + "fs-minipass": "^2.1.0", + "glob": "^8.0.1", + "graceful-fs": "^4.2.10", + "hosted-git-info": "^5.2.1", + "ini": "^3.0.1", + "init-package-json": "^3.0.2", + "is-cidr": "^4.0.2", + "json-parse-even-better-errors": "^2.3.1", + "libnpmaccess": "^6.0.4", + "libnpmdiff": "^4.0.5", + "libnpmexec": "^4.0.14", + "libnpmfund": "^3.0.5", + "libnpmhook": "^8.0.4", + "libnpmorg": "^4.0.4", + "libnpmpack": "^4.1.3", + "libnpmpublish": "^6.0.5", + "libnpmsearch": "^5.0.4", + "libnpmteam": "^4.0.4", + "libnpmversion": "^3.0.7", + "make-fetch-happen": "^10.2.0", + "minimatch": "^5.1.0", + "minipass": "^3.1.6", + "minipass-pipeline": "^1.2.4", + "mkdirp": "^1.0.4", + "mkdirp-infer-owner": "^2.0.0", + "ms": "^2.1.2", + "node-gyp": "^9.1.0", + "nopt": "^6.0.0", + "npm-audit-report": "^3.0.0", + "npm-install-checks": "^5.0.0", + "npm-package-arg": "^9.1.0", + "npm-pick-manifest": "^7.0.2", + "npm-profile": "^6.2.0", + "npm-registry-fetch": "^13.3.1", + "npm-user-validate": "^1.0.1", + "npmlog": "^6.0.2", + "opener": "^1.5.2", + "p-map": "^4.0.0", + "pacote": "^13.6.2", + "parse-conflict-json": "^2.0.2", + "proc-log": "^2.0.1", + "qrcode-terminal": "^0.12.0", + "read": "~1.0.7", + "read-package-json": "^5.0.2", + "read-package-json-fast": "^2.0.3", + "readdir-scoped-modules": "^1.1.0", + "rimraf": "^3.0.2", + "semver": "^7.3.7", + "ssri": "^9.0.1", + "tar": "^6.1.11", + "text-table": "~0.2.0", + "tiny-relative-date": "^1.3.0", + "treeverse": "^2.0.0", + "validate-npm-package-name": "^4.0.0", + "which": "^2.0.2", + "write-file-atomic": "^4.0.1" + }, + "bin": { + "npm": "bin/npm-cli.js", + "npx": "bin/npx-cli.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, "node_modules/@semantic-release/npm/node_modules/npm-run-path": { "version": "4.0.1", "license": "MIT", @@ -5009,30 +5213,2223 @@ "node": ">=8" } }, - "node_modules/@semantic-release/npm/node_modules/onetime": { - "version": "5.1.2", + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@colors/colors": { + "version": "1.5.0", + "inBundle": true, "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@gar/promisify": { + "version": "1.1.3", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@isaacs/string-locale-compare": { + "version": "1.1.0", + "inBundle": true, + "license": "ISC" + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@npmcli/arborist": { + "version": "5.6.3", + "inBundle": true, + "license": "ISC", "dependencies": { - "mimic-fn": "^2.1.0" + "@isaacs/string-locale-compare": "^1.1.0", + "@npmcli/installed-package-contents": "^1.0.7", + "@npmcli/map-workspaces": "^2.0.3", + "@npmcli/metavuln-calculator": "^3.0.1", + "@npmcli/move-file": "^2.0.0", + "@npmcli/name-from-folder": "^1.0.1", + "@npmcli/node-gyp": "^2.0.0", + "@npmcli/package-json": "^2.0.0", + "@npmcli/query": "^1.2.0", + "@npmcli/run-script": "^4.1.3", + "bin-links": "^3.0.3", + "cacache": "^16.1.3", + "common-ancestor-path": "^1.0.1", + "hosted-git-info": "^5.2.1", + "json-parse-even-better-errors": "^2.3.1", + "json-stringify-nice": "^1.1.4", + "minimatch": "^5.1.0", + "mkdirp": "^1.0.4", + "mkdirp-infer-owner": "^2.0.0", + "nopt": "^6.0.0", + "npm-install-checks": "^5.0.0", + "npm-package-arg": "^9.0.0", + "npm-pick-manifest": "^7.0.2", + "npm-registry-fetch": "^13.0.0", + "npmlog": "^6.0.2", + "pacote": "^13.6.1", + "parse-conflict-json": "^2.0.1", + "proc-log": "^2.0.0", + "promise-all-reject-late": "^1.0.0", + "promise-call-limit": "^1.0.1", + "read-package-json-fast": "^2.0.2", + "readdir-scoped-modules": "^1.1.0", + "rimraf": "^3.0.2", + "semver": "^7.3.7", + "ssri": "^9.0.0", + "treeverse": "^2.0.0", + "walk-up-path": "^1.0.0" }, - "engines": { - "node": ">=6" + "bin": { + "arborist": "bin/index.js" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/@semantic-release/npm/node_modules/strip-final-newline": { + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@npmcli/ci-detect": { "version": "2.0.0", - "license": "MIT", + "inBundle": true, + "license": "ISC", "engines": { - "node": ">=6" + "node": "^12.13.0 || ^14.15.0 || >=16" } }, - "node_modules/@semantic-release/npm/node_modules/tempy": { - "version": "1.0.1", - "license": "MIT", - "dependencies": { + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@npmcli/config": { + "version": "4.2.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/map-workspaces": "^2.0.2", + "ini": "^3.0.0", + "mkdirp-infer-owner": "^2.0.0", + "nopt": "^6.0.0", + "proc-log": "^2.0.0", + "read-package-json-fast": "^2.0.3", + "semver": "^7.3.5", + "walk-up-path": "^1.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@npmcli/disparity-colors": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "ansi-styles": "^4.3.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@npmcli/fs": { + "version": "2.1.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@gar/promisify": "^1.1.3", + "semver": "^7.3.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@npmcli/git": { + "version": "3.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/promise-spawn": "^3.0.0", + "lru-cache": "^7.4.4", + "mkdirp": "^1.0.4", + "npm-pick-manifest": "^7.0.0", + "proc-log": "^2.0.0", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^2.0.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@npmcli/installed-package-contents": { + "version": "1.0.7", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-bundled": "^1.1.1", + "npm-normalize-package-bin": "^1.0.1" + }, + "bin": { + "installed-package-contents": "index.js" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@npmcli/installed-package-contents/node_modules/npm-bundled": { + "version": "1.1.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@npmcli/map-workspaces": { + "version": "2.0.4", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/name-from-folder": "^1.0.1", + "glob": "^8.0.1", + "minimatch": "^5.0.1", + "read-package-json-fast": "^2.0.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@npmcli/metavuln-calculator": { + "version": "3.1.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "cacache": "^16.0.0", + "json-parse-even-better-errors": "^2.3.1", + "pacote": "^13.0.3", + "semver": "^7.3.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@npmcli/move-file": { + "version": "2.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@npmcli/name-from-folder": { + "version": "1.0.1", + "inBundle": true, + "license": "ISC" + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@npmcli/node-gyp": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@npmcli/package-json": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^2.3.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@npmcli/promise-spawn": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "infer-owner": "^1.0.4" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@npmcli/query": { + "version": "1.2.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-package-arg": "^9.1.0", + "postcss-selector-parser": "^6.0.10", + "semver": "^7.3.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@npmcli/run-script": { + "version": "4.2.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/node-gyp": "^2.0.0", + "@npmcli/promise-spawn": "^3.0.0", + "node-gyp": "^9.0.0", + "read-package-json-fast": "^2.0.3", + "which": "^2.0.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@tootallnate/once": { + "version": "2.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/abbrev": { + "version": "1.1.1", + "inBundle": true, + "license": "ISC" + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/agent-base": { + "version": "6.0.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/agentkeepalive": { + "version": "4.2.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.0", + "depd": "^1.1.2", + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/aggregate-error": { + "version": "3.1.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/ansi-regex": { + "version": "5.0.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/ansi-styles": { + "version": "4.3.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/aproba": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC" + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/archy": { + "version": "1.0.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/are-we-there-yet": { + "version": "3.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/asap": { + "version": "2.0.6", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/balanced-match": { + "version": "1.0.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/bin-links": { + "version": "3.0.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "cmd-shim": "^5.0.0", + "mkdirp-infer-owner": "^2.0.0", + "npm-normalize-package-bin": "^2.0.0", + "read-cmd-shim": "^3.0.0", + "rimraf": "^3.0.0", + "write-file-atomic": "^4.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/bin-links/node_modules/npm-normalize-package-bin": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/binary-extensions": { + "version": "2.2.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/brace-expansion": { + "version": "2.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/builtins": { + "version": "5.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "semver": "^7.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/cacache": { + "version": "16.1.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^2.1.0", + "@npmcli/move-file": "^2.0.0", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", + "glob": "^8.0.1", + "infer-owner": "^1.0.4", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "mkdirp": "^1.0.4", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^9.0.0", + "tar": "^6.1.11", + "unique-filename": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/chalk": { + "version": "4.1.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/chownr": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/cidr-regex": { + "version": "3.1.1", + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "ip-regex": "^4.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/clean-stack": { + "version": "2.2.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/cli-columns": { + "version": "4.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/cli-table3": { + "version": "0.6.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "@colors/colors": "1.5.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/clone": { + "version": "1.0.4", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/cmd-shim": { + "version": "5.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "mkdirp-infer-owner": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/color-convert": { + "version": "2.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/color-name": { + "version": "1.1.4", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/color-support": { + "version": "1.1.3", + "inBundle": true, + "license": "ISC", + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/columnify": { + "version": "1.6.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "strip-ansi": "^6.0.1", + "wcwidth": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/common-ancestor-path": { + "version": "1.0.1", + "inBundle": true, + "license": "ISC" + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/concat-map": { + "version": "0.0.1", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/console-control-strings": { + "version": "1.1.0", + "inBundle": true, + "license": "ISC" + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/cssesc": { + "version": "3.0.0", + "inBundle": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/debug": { + "version": "4.3.4", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/debug/node_modules/ms": { + "version": "2.1.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/debuglog": { + "version": "1.0.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/defaults": { + "version": "1.0.3", + "inBundle": true, + "license": "MIT", + "dependencies": { + "clone": "^1.0.2" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/delegates": { + "version": "1.0.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/depd": { + "version": "1.1.2", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/dezalgo": { + "version": "1.0.4", + "inBundle": true, + "license": "ISC", + "dependencies": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/diff": { + "version": "5.1.0", + "inBundle": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/emoji-regex": { + "version": "8.0.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/encoding": { + "version": "0.1.13", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/env-paths": { + "version": "2.2.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/err-code": { + "version": "2.0.3", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/fastest-levenshtein": { + "version": "1.0.12", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/fs-minipass": { + "version": "2.1.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/fs.realpath": { + "version": "1.0.0", + "inBundle": true, + "license": "ISC" + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/function-bind": { + "version": "1.1.1", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/gauge": { + "version": "4.0.4", + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/glob": { + "version": "8.0.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/graceful-fs": { + "version": "4.2.10", + "inBundle": true, + "license": "ISC" + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/has": { + "version": "1.0.3", + "inBundle": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/has-flag": { + "version": "4.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/has-unicode": { + "version": "2.0.1", + "inBundle": true, + "license": "ISC" + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/hosted-git-info": { + "version": "5.2.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^7.5.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/http-cache-semantics": { + "version": "4.1.1", + "inBundle": true, + "license": "BSD-2-Clause" + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/http-proxy-agent": { + "version": "5.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/https-proxy-agent": { + "version": "5.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/humanize-ms": { + "version": "1.2.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ms": "^2.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/iconv-lite": { + "version": "0.6.3", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/ignore-walk": { + "version": "5.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minimatch": "^5.0.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/imurmurhash": { + "version": "0.1.4", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/indent-string": { + "version": "4.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/infer-owner": { + "version": "1.0.4", + "inBundle": true, + "license": "ISC" + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/inflight": { + "version": "1.0.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/inherits": { + "version": "2.0.4", + "inBundle": true, + "license": "ISC" + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/ini": { + "version": "3.0.1", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/init-package-json": { + "version": "3.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-package-arg": "^9.0.1", + "promzard": "^0.3.0", + "read": "^1.0.7", + "read-package-json": "^5.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4", + "validate-npm-package-name": "^4.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/ip": { + "version": "2.0.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/ip-regex": { + "version": "4.3.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/is-cidr": { + "version": "4.0.2", + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "cidr-regex": "^3.1.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/is-core-module": { + "version": "2.10.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/is-lambda": { + "version": "1.0.1", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/isexe": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC" + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/json-stringify-nice": { + "version": "1.1.4", + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/jsonparse": { + "version": "1.3.1", + "engines": [ + "node >= 0.2.0" + ], + "inBundle": true, + "license": "MIT" + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/just-diff": { + "version": "5.1.1", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/just-diff-apply": { + "version": "5.4.1", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/libnpmaccess": { + "version": "6.0.4", + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "minipass": "^3.1.1", + "npm-package-arg": "^9.0.1", + "npm-registry-fetch": "^13.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/libnpmdiff": { + "version": "4.0.5", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/disparity-colors": "^2.0.0", + "@npmcli/installed-package-contents": "^1.0.7", + "binary-extensions": "^2.2.0", + "diff": "^5.1.0", + "minimatch": "^5.0.1", + "npm-package-arg": "^9.0.1", + "pacote": "^13.6.1", + "tar": "^6.1.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/libnpmexec": { + "version": "4.0.14", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^5.6.3", + "@npmcli/ci-detect": "^2.0.0", + "@npmcli/fs": "^2.1.1", + "@npmcli/run-script": "^4.2.0", + "chalk": "^4.1.0", + "mkdirp-infer-owner": "^2.0.0", + "npm-package-arg": "^9.0.1", + "npmlog": "^6.0.2", + "pacote": "^13.6.1", + "proc-log": "^2.0.0", + "read": "^1.0.7", + "read-package-json-fast": "^2.0.2", + "semver": "^7.3.7", + "walk-up-path": "^1.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/libnpmfund": { + "version": "3.0.5", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^5.6.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/libnpmhook": { + "version": "8.0.4", + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^13.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/libnpmorg": { + "version": "4.0.4", + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^13.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/libnpmpack": { + "version": "4.1.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/run-script": "^4.1.3", + "npm-package-arg": "^9.0.1", + "pacote": "^13.6.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/libnpmpublish": { + "version": "6.0.5", + "inBundle": true, + "license": "ISC", + "dependencies": { + "normalize-package-data": "^4.0.0", + "npm-package-arg": "^9.0.1", + "npm-registry-fetch": "^13.0.0", + "semver": "^7.3.7", + "ssri": "^9.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/libnpmsearch": { + "version": "5.0.4", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-registry-fetch": "^13.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/libnpmteam": { + "version": "4.0.4", + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^13.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/libnpmversion": { + "version": "3.0.7", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^3.0.0", + "@npmcli/run-script": "^4.1.3", + "json-parse-even-better-errors": "^2.3.1", + "proc-log": "^2.0.0", + "semver": "^7.3.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/lru-cache": { + "version": "7.13.2", + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/make-fetch-happen": { + "version": "10.2.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "agentkeepalive": "^4.2.1", + "cacache": "^16.1.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^2.0.3", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^7.0.0", + "ssri": "^9.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/minimatch": { + "version": "5.1.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/minipass": { + "version": "3.3.4", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/minipass-collect": { + "version": "1.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/minipass-fetch": { + "version": "2.1.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^3.1.6", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/minipass-flush": { + "version": "1.0.5", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/minipass-json-stream": { + "version": "1.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "jsonparse": "^1.3.1", + "minipass": "^3.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/minipass-pipeline": { + "version": "1.2.4", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/minipass-sized": { + "version": "1.0.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/minizlib": { + "version": "2.1.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/mkdirp": { + "version": "1.0.4", + "inBundle": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/mkdirp-infer-owner": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "infer-owner": "^1.0.4", + "mkdirp": "^1.0.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/ms": { + "version": "2.1.3", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/mute-stream": { + "version": "0.0.8", + "inBundle": true, + "license": "ISC" + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/negotiator": { + "version": "0.6.3", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/node-gyp": { + "version": "9.1.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^10.0.3", + "nopt": "^5.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^2.0.2" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": "^12.22 || ^14.13 || >=16" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/node-gyp/node_modules/brace-expansion": { + "version": "1.1.11", + "inBundle": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/node-gyp/node_modules/glob": { + "version": "7.2.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/node-gyp/node_modules/minimatch": { + "version": "3.1.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/node-gyp/node_modules/nopt": { + "version": "5.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/nopt": { + "version": "6.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "abbrev": "^1.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/normalize-package-data": { + "version": "4.0.1", + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^5.0.0", + "is-core-module": "^2.8.1", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/npm-audit-report": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "chalk": "^4.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/npm-bundled": { + "version": "2.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-normalize-package-bin": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/npm-bundled/node_modules/npm-normalize-package-bin": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/npm-install-checks": { + "version": "5.0.0", + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "semver": "^7.1.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/npm-normalize-package-bin": { + "version": "1.0.1", + "inBundle": true, + "license": "ISC" + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/npm-package-arg": { + "version": "9.1.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "hosted-git-info": "^5.0.0", + "proc-log": "^2.0.1", + "semver": "^7.3.5", + "validate-npm-package-name": "^4.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/npm-packlist": { + "version": "5.1.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "glob": "^8.0.1", + "ignore-walk": "^5.0.1", + "npm-bundled": "^2.0.0", + "npm-normalize-package-bin": "^2.0.0" + }, + "bin": { + "npm-packlist": "bin/index.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/npm-packlist/node_modules/npm-normalize-package-bin": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/npm-pick-manifest": { + "version": "7.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-install-checks": "^5.0.0", + "npm-normalize-package-bin": "^2.0.0", + "npm-package-arg": "^9.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/npm-pick-manifest/node_modules/npm-normalize-package-bin": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/npm-profile": { + "version": "6.2.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-registry-fetch": "^13.0.1", + "proc-log": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/npm-registry-fetch": { + "version": "13.3.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "make-fetch-happen": "^10.0.6", + "minipass": "^3.1.6", + "minipass-fetch": "^2.0.3", + "minipass-json-stream": "^1.0.1", + "minizlib": "^2.1.2", + "npm-package-arg": "^9.0.1", + "proc-log": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/npm-user-validate": { + "version": "1.0.1", + "inBundle": true, + "license": "BSD-2-Clause" + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/npmlog": { + "version": "6.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "are-we-there-yet": "^3.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.3", + "set-blocking": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/once": { + "version": "1.4.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/opener": { + "version": "1.5.2", + "inBundle": true, + "license": "(WTFPL OR MIT)", + "bin": { + "opener": "bin/opener-bin.js" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/p-map": { + "version": "4.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/pacote": { + "version": "13.6.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^3.0.0", + "@npmcli/installed-package-contents": "^1.0.7", + "@npmcli/promise-spawn": "^3.0.0", + "@npmcli/run-script": "^4.1.0", + "cacache": "^16.0.0", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", + "infer-owner": "^1.0.4", + "minipass": "^3.1.6", + "mkdirp": "^1.0.4", + "npm-package-arg": "^9.0.0", + "npm-packlist": "^5.1.0", + "npm-pick-manifest": "^7.0.0", + "npm-registry-fetch": "^13.0.1", + "proc-log": "^2.0.0", + "promise-retry": "^2.0.1", + "read-package-json": "^5.0.0", + "read-package-json-fast": "^2.0.3", + "rimraf": "^3.0.2", + "ssri": "^9.0.0", + "tar": "^6.1.11" + }, + "bin": { + "pacote": "lib/bin.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/parse-conflict-json": { + "version": "2.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^2.3.1", + "just-diff": "^5.0.1", + "just-diff-apply": "^5.2.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/path-is-absolute": { + "version": "1.0.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/postcss-selector-parser": { + "version": "6.0.10", + "inBundle": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/proc-log": { + "version": "2.0.1", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/promise-all-reject-late": { + "version": "1.0.1", + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/promise-call-limit": { + "version": "1.0.1", + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/promise-inflight": { + "version": "1.0.1", + "inBundle": true, + "license": "ISC" + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/promise-retry": { + "version": "2.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/promzard": { + "version": "0.3.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "read": "1" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/qrcode-terminal": { + "version": "0.12.0", + "inBundle": true, + "bin": { + "qrcode-terminal": "bin/qrcode-terminal.js" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/read": { + "version": "1.0.7", + "inBundle": true, + "license": "ISC", + "dependencies": { + "mute-stream": "~0.0.4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/read-cmd-shim": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/read-package-json": { + "version": "5.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "glob": "^8.0.1", + "json-parse-even-better-errors": "^2.3.1", + "normalize-package-data": "^4.0.0", + "npm-normalize-package-bin": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/read-package-json-fast": { + "version": "2.0.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^2.3.0", + "npm-normalize-package-bin": "^1.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/read-package-json/node_modules/npm-normalize-package-bin": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/readable-stream": { + "version": "3.6.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/readdir-scoped-modules": { + "version": "1.1.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "debuglog": "^1.0.1", + "dezalgo": "^1.0.0", + "graceful-fs": "^4.1.2", + "once": "^1.3.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/retry": { + "version": "0.12.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/rimraf": { + "version": "3.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/rimraf/node_modules/brace-expansion": { + "version": "1.1.11", + "inBundle": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/rimraf/node_modules/minimatch": { + "version": "3.1.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/safe-buffer": { + "version": "5.2.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "inBundle": true, + "license": "MIT" + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/safer-buffer": { + "version": "2.1.2", + "inBundle": true, + "license": "MIT", + "optional": true + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/semver": { + "version": "7.3.7", + "inBundle": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/semver/node_modules/lru-cache": { + "version": "6.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/set-blocking": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC" + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/signal-exit": { + "version": "3.0.7", + "inBundle": true, + "license": "ISC" + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/smart-buffer": { + "version": "4.2.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/socks": { + "version": "2.7.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ip": "^2.0.0", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.13.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/socks-proxy-agent": { + "version": "7.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/spdx-correct": { + "version": "3.1.1", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/spdx-exceptions": { + "version": "2.3.0", + "inBundle": true, + "license": "CC-BY-3.0" + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/spdx-expression-parse": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/spdx-license-ids": { + "version": "3.0.11", + "inBundle": true, + "license": "CC0-1.0" + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/ssri": { + "version": "9.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.1.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/string_decoder": { + "version": "1.3.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/string-width": { + "version": "4.2.3", + "inBundle": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/strip-ansi": { + "version": "6.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/supports-color": { + "version": "7.2.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/tar": { + "version": "6.1.11", + "inBundle": true, + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/text-table": { + "version": "0.2.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/tiny-relative-date": { + "version": "1.3.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/treeverse": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/unique-filename": { + "version": "2.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "unique-slug": "^3.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/unique-slug": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/util-deprecate": { + "version": "1.0.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/validate-npm-package-license": { + "version": "3.0.4", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/validate-npm-package-name": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "builtins": "^5.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/walk-up-path": { + "version": "1.0.0", + "inBundle": true, + "license": "ISC" + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/wcwidth": { + "version": "1.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/which": { + "version": "2.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/wide-align": { + "version": "1.1.5", + "inBundle": true, + "license": "ISC", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/wrappy": { + "version": "1.0.2", + "inBundle": true, + "license": "ISC" + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/write-file-atomic": { + "version": "4.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm/node_modules/yallist": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC" + }, + "node_modules/@semantic-release/npm/node_modules/onetime": { + "version": "5.1.2", + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@semantic-release/npm/node_modules/strip-final-newline": { + "version": "2.0.0", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/@semantic-release/npm/node_modules/tempy": { + "version": "1.0.1", + "license": "MIT", + "dependencies": { "del": "^6.0.0", "is-stream": "^2.0.0", "temp-dir": "^2.0.0", @@ -6407,8 +8804,7 @@ "node_modules/abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "optional": true + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" }, "node_modules/abort-controller": { "version": "3.0.0", @@ -7119,7 +9515,6 @@ "version": "4.5.0", "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz", "integrity": "sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==", - "optional": true, "dependencies": { "humanize-ms": "^1.2.1" }, @@ -7274,8 +9669,7 @@ "node_modules/aproba": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", - "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", - "optional": true + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" }, "node_modules/archy": { "version": "1.0.0", @@ -7286,7 +9680,6 @@ "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", "deprecated": "This package is no longer supported.", - "optional": true, "dependencies": { "delegates": "^1.0.0", "readable-stream": "^3.6.0" @@ -7299,7 +9692,6 @@ "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "optional": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -7653,6 +10045,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", + "engines": { + "node": "*" + } + }, + "node_modules/aws4": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.2.tgz", + "integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==" + }, "node_modules/axios": { "version": "1.7.4", "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.4.tgz", @@ -8299,6 +10704,14 @@ "safe-json-stringify": "~1" } }, + "node_modules/byline": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/byline/-/byline-5.0.0.tgz", + "integrity": "sha512-s6webAy+R4SR8XVuJWt2V2rGvhnrhxN+9S15GNuTK3wKPOXFF6RNc+8ug2XhH+2s4f+uudG4kUVYmYOQWL2g0Q==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/bytes": { "version": "3.1.2", "license": "MIT", @@ -8400,7 +10813,6 @@ "version": "15.3.0", "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", - "optional": true, "dependencies": { "@npmcli/fs": "^1.0.0", "@npmcli/move-file": "^1.0.1", @@ -8429,7 +10841,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "optional": true, "dependencies": { "aggregate-error": "^3.0.0" }, @@ -8444,7 +10855,6 @@ "version": "6.2.1", "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", - "optional": true, "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", @@ -8461,7 +10871,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "optional": true, "engines": { "node": ">=8" } @@ -8603,6 +11012,11 @@ "cdl": "bin/cdl.js" } }, + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==" + }, "node_modules/ccount": { "version": "2.0.1", "license": "MIT", @@ -8985,7 +11399,6 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", - "optional": true, "bin": { "color-support": "bin.js" } @@ -9158,8 +11571,7 @@ "node_modules/console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", - "optional": true + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" }, "node_modules/content-disposition": { "version": "0.5.4", @@ -9561,6 +11973,25 @@ "type": "^1.0.1" } }, + "node_modules/dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", + "dependencies": { + "assert-plus": "^1.0.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/dashdash/node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "engines": { + "node": ">=0.8" + } + }, "node_modules/data-uri-to-buffer": { "version": "4.0.1", "license": "MIT", @@ -10023,8 +12454,7 @@ "node_modules/delegates": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", - "optional": true + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" }, "node_modules/denque": { "version": "2.1.0", @@ -10751,6 +13181,20 @@ "version": "0.2.0", "license": "MIT" }, + "node_modules/ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", + "dependencies": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/ecc-jsbn/node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==" + }, "node_modules/eccrypto": { "version": "1.1.6", "hasInstallScript": true, @@ -13376,6 +15820,14 @@ "node": ">=8.0.0" } }, + "node_modules/forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", + "engines": { + "node": "*" + } + }, "node_modules/form-data": { "version": "4.0.0", "license": "MIT", @@ -13537,7 +15989,6 @@ "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", "deprecated": "This package is no longer supported.", - "optional": true, "dependencies": { "aproba": "^1.0.3 || ^2.0.0", "color-support": "^1.1.3", @@ -13556,7 +16007,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "optional": true, "engines": { "node": ">=8" } @@ -13565,7 +16015,6 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "optional": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -13735,6 +16184,22 @@ "node": ">= 14" } }, + "node_modules/getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", + "dependencies": { + "assert-plus": "^1.0.0" + } + }, + "node_modules/getpass/node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "engines": { + "node": ">=0.8" + } + }, "node_modules/gh-pages": { "version": "4.0.0", "license": "MIT", @@ -14087,6 +16552,27 @@ "uglify-js": "^3.1.4" } }, + "node_modules/har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", + "engines": { + "node": ">=4" + } + }, + "node_modules/har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "deprecated": "this library is no longer supported", + "dependencies": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/hard-rejection": { "version": "2.1.0", "license": "MIT", @@ -14184,8 +16670,7 @@ "node_modules/has-unicode": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", - "optional": true + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" }, "node_modules/has-yarn": { "version": "3.0.0", @@ -14359,6 +16844,28 @@ "node": ">= 6" } }, + "node_modules/http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", + "dependencies": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + }, + "engines": { + "node": ">=0.8", + "npm": ">=1.3.7" + } + }, + "node_modules/http-signature/node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "engines": { + "node": ">=0.8" + } + }, "node_modules/http2-wrapper": { "version": "1.0.3", "license": "MIT", @@ -14392,7 +16899,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", - "optional": true, "dependencies": { "ms": "^2.0.0" } @@ -14408,6 +16914,14 @@ "node": ">= 8" } }, + "node_modules/i": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/i/-/i-0.3.7.tgz", + "integrity": "sha512-FYz4wlXgkQwIPqhzC5TdNMLSE5+GS1IIDJZY/1ZiEPCT2S3COUVZeT5OW4BmW4r5LHLQuOosSwsvnroG9GR59Q==", + "engines": { + "node": ">=0.4" + } + }, "node_modules/iconv-lite": { "version": "0.4.24", "license": "MIT", @@ -14495,8 +17009,7 @@ "node_modules/infer-owner": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", - "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", - "optional": true + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==" }, "node_modules/inflight": { "version": "1.0.6", @@ -14838,7 +17351,6 @@ "version": "9.0.5", "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", - "devOptional": true, "dependencies": { "jsbn": "1.1.0", "sprintf-js": "^1.1.3" @@ -15162,8 +17674,7 @@ "node_modules/is-lambda": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", - "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", - "optional": true + "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==" }, "node_modules/is-loopback-addr": { "version": "2.0.2", @@ -15476,6 +17987,14 @@ "version": "2.0.0", "license": "ISC" }, + "node_modules/isomorphic-ws": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz", + "integrity": "sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==", + "peerDependencies": { + "ws": "*" + } + }, "node_modules/isstream": { "version": "0.1.2", "license": "MIT" @@ -15883,6 +18402,15 @@ "node": ">= 0.6.0" } }, + "node_modules/jose": { + "version": "5.9.6", + "resolved": "https://registry.npmjs.org/jose/-/jose-5.9.6.tgz", + "integrity": "sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ==", + "optional": true, + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/js-sha3": { "version": "0.8.0", "license": "MIT" @@ -15904,8 +18432,7 @@ "node_modules/jsbn": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", - "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", - "devOptional": true + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==" }, "node_modules/jsdoc-type-pratt-parser": { "version": "1.2.0", @@ -15914,6 +18441,14 @@ "node": ">=12.0.0" } }, + "node_modules/jsep": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jsep/-/jsep-1.4.0.tgz", + "integrity": "sha512-B7qPcEVE3NVkmSJbaYxvv4cHkVW7DQsZz13pUMrfS8z8Q/BuShN+gcTXrUlPiGqM2/t/EEaI030bpxMqY8gMlw==", + "engines": { + "node": ">= 10.16.0" + } + }, "node_modules/jsesc": { "version": "2.5.2", "license": "MIT", @@ -15936,6 +18471,11 @@ "version": "2.3.1", "license": "MIT" }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "license": "MIT" @@ -16050,6 +18590,23 @@ ], "license": "MIT" }, + "node_modules/jsonpath-plus": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-10.1.0.tgz", + "integrity": "sha512-gHfV1IYqH8uJHYVTs8BJX1XKy2/rR93+f8QQi0xhx95aCiXn1ettYAd5T+7FU6wfqyDoX/wy0pm/fL3jOKJ9Lg==", + "dependencies": { + "@jsep-plugin/assignment": "^1.2.1", + "@jsep-plugin/regex": "^1.0.3", + "jsep": "^1.3.9" + }, + "bin": { + "jsonpath": "bin/jsonpath-cli.js", + "jsonpath-plus": "bin/jsonpath-cli.js" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/JSONStream": { "version": "1.3.5", "license": "(MIT OR Apache-2.0)", @@ -16064,6 +18621,54 @@ "node": "*" } }, + "node_modules/jsprim": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", + "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", + "dependencies": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/jsprim/node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/jsprim/node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==" + }, + "node_modules/jsprim/node_modules/extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", + "engines": [ + "node >=0.6.0" + ] + }, + "node_modules/jsprim/node_modules/verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", + "engines": [ + "node >=0.6.0" + ], + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, "node_modules/jsx-ast-utils": { "version": "3.3.5", "license": "MIT", @@ -16868,7 +19473,6 @@ "version": "9.1.0", "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==", - "optional": true, "dependencies": { "agentkeepalive": "^4.1.3", "cacache": "^15.2.0", @@ -16895,7 +19499,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", - "optional": true, "engines": { "node": ">= 6" } @@ -16904,7 +19507,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", - "optional": true, "dependencies": { "@tootallnate/once": "1", "agent-base": "6", @@ -18055,7 +20657,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", - "optional": true, "dependencies": { "minipass": "^3.0.0" }, @@ -18067,7 +20668,6 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==", - "optional": true, "dependencies": { "minipass": "^3.1.0", "minipass-sized": "^1.0.3", @@ -18084,7 +20684,6 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", - "optional": true, "dependencies": { "minipass": "^3.0.0" }, @@ -18096,7 +20695,6 @@ "version": "1.2.4", "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", - "optional": true, "dependencies": { "minipass": "^3.0.0" }, @@ -18108,7 +20706,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", - "optional": true, "dependencies": { "minipass": "^3.0.0" }, @@ -18771,7 +21368,6 @@ "version": "8.4.1", "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz", "integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==", - "optional": true, "dependencies": { "env-paths": "^2.2.0", "glob": "^7.1.4", @@ -18804,7 +21400,6 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", - "optional": true, "engines": { "node": ">=6" } @@ -18813,7 +21408,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "optional": true, "engines": { "node": ">=8" } @@ -18822,7 +21416,6 @@ "version": "6.2.1", "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", - "optional": true, "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", @@ -18914,7 +21507,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", - "optional": true, "dependencies": { "abbrev": "1" }, @@ -18956,24 +21548,26 @@ } }, "node_modules/npm": { - "version": "8.19.4", + "version": "10.9.0", + "resolved": "https://registry.npmjs.org/npm/-/npm-10.9.0.tgz", + "integrity": "sha512-ZanDioFylI9helNhl2LNd+ErmVD+H5I53ry41ixlLyCBgkuYb+58CvbAp99hW+zr5L9W4X7CchSoeqKdngOLSw==", "bundleDependencies": [ "@isaacs/string-locale-compare", "@npmcli/arborist", - "@npmcli/ci-detect", "@npmcli/config", "@npmcli/fs", "@npmcli/map-workspaces", "@npmcli/package-json", + "@npmcli/promise-spawn", + "@npmcli/redact", "@npmcli/run-script", + "@sigstore/tuf", "abbrev", "archy", "cacache", "chalk", - "chownr", + "ci-info", "cli-columns", - "cli-table3", - "columnify", "fastest-levenshtein", "fs-minipass", "glob", @@ -18998,11 +21592,10 @@ "minimatch", "minipass", "minipass-pipeline", - "mkdirp", - "mkdirp-infer-owner", "ms", "node-gyp", "nopt", + "normalize-package-data", "npm-audit-report", "npm-install-checks", "npm-package-arg", @@ -19010,20 +21603,16 @@ "npm-profile", "npm-registry-fetch", "npm-user-validate", - "npmlog", - "opener", "p-map", "pacote", "parse-conflict-json", "proc-log", "qrcode-terminal", "read", - "read-package-json", - "read-package-json-fast", - "readdir-scoped-modules", - "rimraf", "semver", + "spdx-expression-parse", "ssri", + "supports-color", "tar", "text-table", "tiny-relative-date", @@ -19032,93 +21621,82 @@ "which", "write-file-atomic" ], - "license": "Artistic-2.0", - "workspaces": [ - "docs", - "smoke-tests", - "workspaces/*" - ], "dependencies": { "@isaacs/string-locale-compare": "^1.1.0", - "@npmcli/arborist": "^5.6.3", - "@npmcli/ci-detect": "^2.0.0", - "@npmcli/config": "^4.2.1", - "@npmcli/fs": "^2.1.0", - "@npmcli/map-workspaces": "^2.0.3", - "@npmcli/package-json": "^2.0.0", - "@npmcli/run-script": "^4.2.1", - "abbrev": "~1.1.1", + "@npmcli/arborist": "^8.0.0", + "@npmcli/config": "^9.0.0", + "@npmcli/fs": "^4.0.0", + "@npmcli/map-workspaces": "^4.0.1", + "@npmcli/package-json": "^6.0.1", + "@npmcli/promise-spawn": "^8.0.1", + "@npmcli/redact": "^3.0.0", + "@npmcli/run-script": "^9.0.1", + "@sigstore/tuf": "^2.3.4", + "abbrev": "^3.0.0", "archy": "~1.0.0", - "cacache": "^16.1.3", - "chalk": "^4.1.2", - "chownr": "^2.0.0", + "cacache": "^19.0.1", + "chalk": "^5.3.0", + "ci-info": "^4.0.0", "cli-columns": "^4.0.0", - "cli-table3": "^0.6.2", - "columnify": "^1.6.0", - "fastest-levenshtein": "^1.0.12", - "fs-minipass": "^2.1.0", - "glob": "^8.0.1", - "graceful-fs": "^4.2.10", - "hosted-git-info": "^5.2.1", - "ini": "^3.0.1", - "init-package-json": "^3.0.2", - "is-cidr": "^4.0.2", - "json-parse-even-better-errors": "^2.3.1", - "libnpmaccess": "^6.0.4", - "libnpmdiff": "^4.0.5", - "libnpmexec": "^4.0.14", - "libnpmfund": "^3.0.5", - "libnpmhook": "^8.0.4", - "libnpmorg": "^4.0.4", - "libnpmpack": "^4.1.3", - "libnpmpublish": "^6.0.5", - "libnpmsearch": "^5.0.4", - "libnpmteam": "^4.0.4", - "libnpmversion": "^3.0.7", - "make-fetch-happen": "^10.2.0", - "minimatch": "^5.1.0", - "minipass": "^3.1.6", + "fastest-levenshtein": "^1.0.16", + "fs-minipass": "^3.0.3", + "glob": "^10.4.5", + "graceful-fs": "^4.2.11", + "hosted-git-info": "^8.0.0", + "ini": "^5.0.0", + "init-package-json": "^7.0.1", + "is-cidr": "^5.1.0", + "json-parse-even-better-errors": "^4.0.0", + "libnpmaccess": "^9.0.0", + "libnpmdiff": "^7.0.0", + "libnpmexec": "^9.0.0", + "libnpmfund": "^6.0.0", + "libnpmhook": "^11.0.0", + "libnpmorg": "^7.0.0", + "libnpmpack": "^8.0.0", + "libnpmpublish": "^10.0.0", + "libnpmsearch": "^8.0.0", + "libnpmteam": "^7.0.0", + "libnpmversion": "^7.0.0", + "make-fetch-happen": "^14.0.1", + "minimatch": "^9.0.5", + "minipass": "^7.1.1", "minipass-pipeline": "^1.2.4", - "mkdirp": "^1.0.4", - "mkdirp-infer-owner": "^2.0.0", "ms": "^2.1.2", - "node-gyp": "^9.1.0", - "nopt": "^6.0.0", - "npm-audit-report": "^3.0.0", - "npm-install-checks": "^5.0.0", - "npm-package-arg": "^9.1.0", - "npm-pick-manifest": "^7.0.2", - "npm-profile": "^6.2.0", - "npm-registry-fetch": "^13.3.1", - "npm-user-validate": "^1.0.1", - "npmlog": "^6.0.2", - "opener": "^1.5.2", + "node-gyp": "^10.2.0", + "nopt": "^8.0.0", + "normalize-package-data": "^7.0.0", + "npm-audit-report": "^6.0.0", + "npm-install-checks": "^7.1.0", + "npm-package-arg": "^12.0.0", + "npm-pick-manifest": "^10.0.0", + "npm-profile": "^11.0.1", + "npm-registry-fetch": "^18.0.1", + "npm-user-validate": "^3.0.0", "p-map": "^4.0.0", - "pacote": "^13.6.2", - "parse-conflict-json": "^2.0.2", - "proc-log": "^2.0.1", + "pacote": "^19.0.0", + "parse-conflict-json": "^4.0.0", + "proc-log": "^5.0.0", "qrcode-terminal": "^0.12.0", - "read": "~1.0.7", - "read-package-json": "^5.0.2", - "read-package-json-fast": "^2.0.3", - "readdir-scoped-modules": "^1.1.0", - "rimraf": "^3.0.2", - "semver": "^7.3.7", - "ssri": "^9.0.1", - "tar": "^6.1.11", + "read": "^4.0.0", + "semver": "^7.6.3", + "spdx-expression-parse": "^4.0.0", + "ssri": "^12.0.0", + "supports-color": "^9.4.0", + "tar": "^6.2.1", "text-table": "~0.2.0", "tiny-relative-date": "^1.3.0", - "treeverse": "^2.0.0", - "validate-npm-package-name": "^4.0.0", - "which": "^2.0.2", - "write-file-atomic": "^4.0.1" + "treeverse": "^3.0.0", + "validate-npm-package-name": "^6.0.0", + "which": "^5.0.0", + "write-file-atomic": "^6.0.0" }, "bin": { "npm": "bin/npm-cli.js", "npx": "bin/npx-cli.js" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm-package-json-lint": { @@ -19335,304 +21913,559 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/npm/node_modules/@colors/colors": { - "version": "1.5.0", + "node_modules/npm/node_modules/@isaacs/cliui": { + "version": "8.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.0.1", "inBundle": true, "license": "MIT", - "optional": true, "engines": { - "node": ">=0.1.90" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/npm/node_modules/@gar/promisify": { - "version": "1.1.3", + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", "inBundle": true, "license": "MIT" }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/npm/node_modules/@isaacs/string-locale-compare": { "version": "1.1.0", "inBundle": true, "license": "ISC" }, + "node_modules/npm/node_modules/@npmcli/agent": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "agent-base": "^7.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "lru-cache": "^10.0.1", + "socks-proxy-agent": "^8.0.3" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, "node_modules/npm/node_modules/@npmcli/arborist": { - "version": "5.6.3", + "version": "8.0.0", "inBundle": true, "license": "ISC", "dependencies": { "@isaacs/string-locale-compare": "^1.1.0", - "@npmcli/installed-package-contents": "^1.0.7", - "@npmcli/map-workspaces": "^2.0.3", - "@npmcli/metavuln-calculator": "^3.0.1", - "@npmcli/move-file": "^2.0.0", - "@npmcli/name-from-folder": "^1.0.1", - "@npmcli/node-gyp": "^2.0.0", - "@npmcli/package-json": "^2.0.0", - "@npmcli/query": "^1.2.0", - "@npmcli/run-script": "^4.1.3", - "bin-links": "^3.0.3", - "cacache": "^16.1.3", + "@npmcli/fs": "^4.0.0", + "@npmcli/installed-package-contents": "^3.0.0", + "@npmcli/map-workspaces": "^4.0.1", + "@npmcli/metavuln-calculator": "^8.0.0", + "@npmcli/name-from-folder": "^3.0.0", + "@npmcli/node-gyp": "^4.0.0", + "@npmcli/package-json": "^6.0.1", + "@npmcli/query": "^4.0.0", + "@npmcli/redact": "^3.0.0", + "@npmcli/run-script": "^9.0.1", + "bin-links": "^5.0.0", + "cacache": "^19.0.1", "common-ancestor-path": "^1.0.1", - "hosted-git-info": "^5.2.1", - "json-parse-even-better-errors": "^2.3.1", + "hosted-git-info": "^8.0.0", + "json-parse-even-better-errors": "^4.0.0", "json-stringify-nice": "^1.1.4", - "minimatch": "^5.1.0", - "mkdirp": "^1.0.4", - "mkdirp-infer-owner": "^2.0.0", - "nopt": "^6.0.0", - "npm-install-checks": "^5.0.0", - "npm-package-arg": "^9.0.0", - "npm-pick-manifest": "^7.0.2", - "npm-registry-fetch": "^13.0.0", - "npmlog": "^6.0.2", - "pacote": "^13.6.1", - "parse-conflict-json": "^2.0.1", - "proc-log": "^2.0.0", + "lru-cache": "^10.2.2", + "minimatch": "^9.0.4", + "nopt": "^8.0.0", + "npm-install-checks": "^7.1.0", + "npm-package-arg": "^12.0.0", + "npm-pick-manifest": "^10.0.0", + "npm-registry-fetch": "^18.0.1", + "pacote": "^19.0.0", + "parse-conflict-json": "^4.0.0", + "proc-log": "^5.0.0", + "proggy": "^3.0.0", "promise-all-reject-late": "^1.0.0", - "promise-call-limit": "^1.0.1", - "read-package-json-fast": "^2.0.2", - "readdir-scoped-modules": "^1.1.0", - "rimraf": "^3.0.2", + "promise-call-limit": "^3.0.1", + "read-package-json-fast": "^4.0.0", "semver": "^7.3.7", - "ssri": "^9.0.0", - "treeverse": "^2.0.0", - "walk-up-path": "^1.0.0" + "ssri": "^12.0.0", + "treeverse": "^3.0.0", + "walk-up-path": "^3.0.1" }, "bin": { "arborist": "bin/index.js" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/config": { + "version": "9.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/map-workspaces": "^4.0.1", + "@npmcli/package-json": "^6.0.1", + "ci-info": "^4.0.0", + "ini": "^5.0.0", + "nopt": "^8.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5", + "walk-up-path": "^3.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/fs": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/git": { + "version": "6.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/promise-spawn": "^8.0.0", + "ini": "^5.0.0", + "lru-cache": "^10.0.1", + "npm-pick-manifest": "^10.0.0", + "proc-log": "^5.0.0", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/installed-package-contents": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-bundled": "^4.0.0", + "npm-normalize-package-bin": "^4.0.0" + }, + "bin": { + "installed-package-contents": "bin/index.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/map-workspaces": { + "version": "4.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/name-from-folder": "^3.0.0", + "@npmcli/package-json": "^6.0.0", + "glob": "^10.2.2", + "minimatch": "^9.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/metavuln-calculator": { + "version": "8.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "cacache": "^19.0.0", + "json-parse-even-better-errors": "^4.0.0", + "pacote": "^19.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/name-from-folder": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/node-gyp": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/package-json": { + "version": "6.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^6.0.0", + "glob": "^10.2.2", + "hosted-git-info": "^8.0.0", + "json-parse-even-better-errors": "^4.0.0", + "normalize-package-data": "^7.0.0", + "proc-log": "^5.0.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/npm/node_modules/@npmcli/ci-detect": { - "version": "2.0.0", + "node_modules/npm/node_modules/@npmcli/promise-spawn": { + "version": "8.0.1", "inBundle": true, "license": "ISC", + "dependencies": { + "which": "^5.0.0" + }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/npm/node_modules/@npmcli/config": { - "version": "4.2.2", + "node_modules/npm/node_modules/@npmcli/query": { + "version": "4.0.0", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/map-workspaces": "^2.0.2", - "ini": "^3.0.0", - "mkdirp-infer-owner": "^2.0.0", - "nopt": "^6.0.0", - "proc-log": "^2.0.0", - "read-package-json-fast": "^2.0.3", - "semver": "^7.3.5", - "walk-up-path": "^1.0.0" + "postcss-selector-parser": "^6.1.2" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/npm/node_modules/@npmcli/disparity-colors": { - "version": "2.0.0", + "node_modules/npm/node_modules/@npmcli/redact": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/run-script": { + "version": "9.0.1", "inBundle": true, "license": "ISC", "dependencies": { - "ansi-styles": "^4.3.0" + "@npmcli/node-gyp": "^4.0.0", + "@npmcli/package-json": "^6.0.0", + "@npmcli/promise-spawn": "^8.0.0", + "node-gyp": "^10.0.0", + "proc-log": "^5.0.0", + "which": "^5.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/npm/node_modules/@npmcli/fs": { - "version": "2.1.2", + "node_modules/npm/node_modules/@pkgjs/parseargs": { + "version": "0.11.0", "inBundle": true, - "license": "ISC", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/npm/node_modules/@sigstore/bundle": { + "version": "2.3.2", + "inBundle": true, + "license": "Apache-2.0", "dependencies": { - "@gar/promisify": "^1.1.3", - "semver": "^7.3.5" + "@sigstore/protobuf-specs": "^0.3.2" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/@npmcli/git": { - "version": "3.0.2", + "node_modules/npm/node_modules/@sigstore/core": { + "version": "1.1.0", "inBundle": true, - "license": "ISC", + "license": "Apache-2.0", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@sigstore/protobuf-specs": { + "version": "0.3.2", + "inBundle": true, + "license": "Apache-2.0", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@sigstore/sign": { + "version": "2.3.2", + "inBundle": true, + "license": "Apache-2.0", "dependencies": { - "@npmcli/promise-spawn": "^3.0.0", - "lru-cache": "^7.4.4", - "mkdirp": "^1.0.4", - "npm-pick-manifest": "^7.0.0", - "proc-log": "^2.0.0", - "promise-inflight": "^1.0.1", - "promise-retry": "^2.0.1", - "semver": "^7.3.5", - "which": "^2.0.2" + "@sigstore/bundle": "^2.3.2", + "@sigstore/core": "^1.0.0", + "@sigstore/protobuf-specs": "^0.3.2", + "make-fetch-happen": "^13.0.1", + "proc-log": "^4.2.0", + "promise-retry": "^2.0.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/@npmcli/installed-package-contents": { - "version": "1.0.7", + "node_modules/npm/node_modules/@sigstore/sign/node_modules/@npmcli/agent": { + "version": "2.2.2", "inBundle": true, "license": "ISC", "dependencies": { - "npm-bundled": "^1.1.1", - "npm-normalize-package-bin": "^1.0.1" - }, - "bin": { - "installed-package-contents": "index.js" + "agent-base": "^7.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "lru-cache": "^10.0.1", + "socks-proxy-agent": "^8.0.3" }, "engines": { - "node": ">= 10" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/@npmcli/installed-package-contents/node_modules/npm-bundled": { - "version": "1.1.2", + "node_modules/npm/node_modules/@sigstore/sign/node_modules/@npmcli/fs": { + "version": "3.1.1", "inBundle": true, "license": "ISC", "dependencies": { - "npm-normalize-package-bin": "^1.0.1" + "semver": "^7.3.5" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/@npmcli/map-workspaces": { - "version": "2.0.4", + "node_modules/npm/node_modules/@sigstore/sign/node_modules/cacache": { + "version": "18.0.4", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/name-from-folder": "^1.0.1", - "glob": "^8.0.1", - "minimatch": "^5.0.1", - "read-package-json-fast": "^2.0.3" + "@npmcli/fs": "^3.1.0", + "fs-minipass": "^3.0.0", + "glob": "^10.2.2", + "lru-cache": "^10.0.1", + "minipass": "^7.0.3", + "minipass-collect": "^2.0.1", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^4.0.0", + "ssri": "^10.0.0", + "tar": "^6.1.11", + "unique-filename": "^3.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/@npmcli/metavuln-calculator": { - "version": "3.1.1", + "node_modules/npm/node_modules/@sigstore/sign/node_modules/make-fetch-happen": { + "version": "13.0.1", "inBundle": true, "license": "ISC", "dependencies": { - "cacache": "^16.0.0", - "json-parse-even-better-errors": "^2.3.1", - "pacote": "^13.0.3", - "semver": "^7.3.5" + "@npmcli/agent": "^2.0.0", + "cacache": "^18.0.0", + "http-cache-semantics": "^4.1.1", + "is-lambda": "^1.0.1", + "minipass": "^7.0.2", + "minipass-fetch": "^3.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "proc-log": "^4.2.0", + "promise-retry": "^2.0.1", + "ssri": "^10.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/@npmcli/move-file": { - "version": "2.0.1", + "node_modules/npm/node_modules/@sigstore/sign/node_modules/minipass-fetch": { + "version": "3.0.5", "inBundle": true, "license": "MIT", "dependencies": { - "mkdirp": "^1.0.4", - "rimraf": "^3.0.2" + "minipass": "^7.0.3", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" } }, - "node_modules/npm/node_modules/@npmcli/name-from-folder": { - "version": "1.0.1", - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/@npmcli/node-gyp": { - "version": "2.0.0", + "node_modules/npm/node_modules/@sigstore/sign/node_modules/proc-log": { + "version": "4.2.0", "inBundle": true, "license": "ISC", "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/@npmcli/package-json": { - "version": "2.0.0", + "node_modules/npm/node_modules/@sigstore/sign/node_modules/ssri": { + "version": "10.0.6", "inBundle": true, "license": "ISC", "dependencies": { - "json-parse-even-better-errors": "^2.3.1" + "minipass": "^7.0.3" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/@npmcli/promise-spawn": { + "node_modules/npm/node_modules/@sigstore/sign/node_modules/unique-filename": { "version": "3.0.0", "inBundle": true, "license": "ISC", "dependencies": { - "infer-owner": "^1.0.4" + "unique-slug": "^4.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/@npmcli/query": { - "version": "1.2.0", + "node_modules/npm/node_modules/@sigstore/sign/node_modules/unique-slug": { + "version": "4.0.0", "inBundle": true, "license": "ISC", "dependencies": { - "npm-package-arg": "^9.1.0", - "postcss-selector-parser": "^6.0.10", - "semver": "^7.3.7" + "imurmurhash": "^0.1.4" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/@npmcli/run-script": { - "version": "4.2.1", + "node_modules/npm/node_modules/@sigstore/tuf": { + "version": "2.3.4", "inBundle": true, - "license": "ISC", + "license": "Apache-2.0", "dependencies": { - "@npmcli/node-gyp": "^2.0.0", - "@npmcli/promise-spawn": "^3.0.0", - "node-gyp": "^9.0.0", - "read-package-json-fast": "^2.0.3", - "which": "^2.0.2" + "@sigstore/protobuf-specs": "^0.3.2", + "tuf-js": "^2.2.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/@tootallnate/once": { - "version": "2.0.0", + "node_modules/npm/node_modules/@sigstore/verify": { + "version": "1.2.1", "inBundle": true, - "license": "MIT", + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^2.3.2", + "@sigstore/core": "^1.1.0", + "@sigstore/protobuf-specs": "^0.3.2" + }, "engines": { - "node": ">= 10" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/abbrev": { - "version": "1.1.1", + "node_modules/npm/node_modules/@tufjs/canonical-json": { + "version": "2.0.0", "inBundle": true, - "license": "ISC" + "license": "MIT", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } }, - "node_modules/npm/node_modules/agent-base": { - "version": "6.0.2", + "node_modules/npm/node_modules/@tufjs/models": { + "version": "2.0.1", "inBundle": true, "license": "MIT", "dependencies": { - "debug": "4" + "@tufjs/canonical-json": "2.0.0", + "minimatch": "^9.0.4" }, "engines": { - "node": ">= 6.0.0" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/agentkeepalive": { - "version": "4.2.1", + "node_modules/npm/node_modules/abbrev": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/agent-base": { + "version": "7.1.1", "inBundle": true, "license": "MIT", "dependencies": { - "debug": "^4.1.0", - "depd": "^1.1.2", - "humanize-ms": "^1.2.1" + "debug": "^4.3.4" }, "engines": { - "node": ">= 8.0.0" + "node": ">= 14" } }, "node_modules/npm/node_modules/aggregate-error": { @@ -19656,14 +22489,11 @@ } }, "node_modules/npm/node_modules/ansi-styles": { - "version": "4.3.0", + "version": "6.2.1", "inBundle": true, "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, "engines": { - "node": ">=8" + "node": ">=12" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" @@ -19679,114 +22509,142 @@ "inBundle": true, "license": "MIT" }, - "node_modules/npm/node_modules/are-we-there-yet": { - "version": "3.0.1", + "node_modules/npm/node_modules/balanced-match": { + "version": "1.0.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/bin-links": { + "version": "5.0.0", "inBundle": true, "license": "ISC", "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" + "cmd-shim": "^7.0.0", + "npm-normalize-package-bin": "^4.0.0", + "proc-log": "^5.0.0", + "read-cmd-shim": "^5.0.0", + "write-file-atomic": "^6.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/npm/node_modules/asap": { - "version": "2.0.6", + "node_modules/npm/node_modules/binary-extensions": { + "version": "2.3.0", "inBundle": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "node_modules/npm/node_modules/balanced-match": { - "version": "1.0.2", + "node_modules/npm/node_modules/brace-expansion": { + "version": "2.0.1", "inBundle": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } }, - "node_modules/npm/node_modules/bin-links": { - "version": "3.0.3", + "node_modules/npm/node_modules/cacache": { + "version": "19.0.1", "inBundle": true, "license": "ISC", "dependencies": { - "cmd-shim": "^5.0.0", - "mkdirp-infer-owner": "^2.0.0", - "npm-normalize-package-bin": "^2.0.0", - "read-cmd-shim": "^3.0.0", - "rimraf": "^3.0.0", - "write-file-atomic": "^4.0.0" + "@npmcli/fs": "^4.0.0", + "fs-minipass": "^3.0.0", + "glob": "^10.2.2", + "lru-cache": "^10.0.1", + "minipass": "^7.0.3", + "minipass-collect": "^2.0.1", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^7.0.2", + "ssri": "^12.0.0", + "tar": "^7.4.3", + "unique-filename": "^4.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/npm/node_modules/bin-links/node_modules/npm-normalize-package-bin": { - "version": "2.0.0", + "node_modules/npm/node_modules/cacache/node_modules/chownr": { + "version": "3.0.0", "inBundle": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=18" } }, - "node_modules/npm/node_modules/binary-extensions": { - "version": "2.2.0", + "node_modules/npm/node_modules/cacache/node_modules/minizlib": { + "version": "3.0.1", "inBundle": true, "license": "MIT", + "dependencies": { + "minipass": "^7.0.4", + "rimraf": "^5.0.5" + }, "engines": { - "node": ">=8" + "node": ">= 18" } }, - "node_modules/npm/node_modules/brace-expansion": { - "version": "2.0.1", + "node_modules/npm/node_modules/cacache/node_modules/mkdirp": { + "version": "3.0.1", "inBundle": true, "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/npm/node_modules/builtins": { - "version": "5.0.1", + "node_modules/npm/node_modules/cacache/node_modules/p-map": { + "version": "7.0.2", "inBundle": true, "license": "MIT", - "dependencies": { - "semver": "^7.0.0" + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/npm/node_modules/cacache": { - "version": "16.1.3", + "node_modules/npm/node_modules/cacache/node_modules/tar": { + "version": "7.4.3", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/fs": "^2.1.0", - "@npmcli/move-file": "^2.0.0", - "chownr": "^2.0.0", - "fs-minipass": "^2.1.0", - "glob": "^8.0.1", - "infer-owner": "^1.0.4", - "lru-cache": "^7.7.1", - "minipass": "^3.1.6", - "minipass-collect": "^1.0.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "mkdirp": "^1.0.4", - "p-map": "^4.0.0", - "promise-inflight": "^1.0.1", - "rimraf": "^3.0.2", - "ssri": "^9.0.0", - "tar": "^6.1.11", - "unique-filename": "^2.0.0" + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=18" + } + }, + "node_modules/npm/node_modules/cacache/node_modules/yallist": { + "version": "5.0.0", + "inBundle": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" } }, "node_modules/npm/node_modules/chalk": { - "version": "4.1.2", + "version": "5.3.0", "inBundle": true, "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, "engines": { - "node": ">=10" + "node": "^12.17.0 || ^14.13 || >=16.0.0" }, "funding": { "url": "https://github.com/chalk/chalk?sponsor=1" @@ -19800,15 +22658,29 @@ "node": ">=10" } }, + "node_modules/npm/node_modules/ci-info": { + "version": "4.0.0", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/npm/node_modules/cidr-regex": { - "version": "3.1.1", + "version": "4.1.1", "inBundle": true, "license": "BSD-2-Clause", "dependencies": { - "ip-regex": "^4.1.0" + "ip-regex": "^5.0.0" }, "engines": { - "node": ">=10" + "node": ">=14" } }, "node_modules/npm/node_modules/clean-stack": { @@ -19831,37 +22703,12 @@ "node": ">= 10" } }, - "node_modules/npm/node_modules/cli-table3": { - "version": "0.6.2", - "inBundle": true, - "license": "MIT", - "dependencies": { - "string-width": "^4.2.0" - }, - "engines": { - "node": "10.* || >= 12.*" - }, - "optionalDependencies": { - "@colors/colors": "1.5.0" - } - }, - "node_modules/npm/node_modules/clone": { - "version": "1.0.4", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=0.8" - } - }, "node_modules/npm/node_modules/cmd-shim": { - "version": "5.0.0", + "version": "7.0.0", "inBundle": true, "license": "ISC", - "dependencies": { - "mkdirp-infer-owner": "^2.0.0" - }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm/node_modules/color-convert": { @@ -19880,40 +22727,37 @@ "inBundle": true, "license": "MIT" }, - "node_modules/npm/node_modules/color-support": { - "version": "1.1.3", + "node_modules/npm/node_modules/common-ancestor-path": { + "version": "1.0.1", "inBundle": true, - "license": "ISC", - "bin": { - "color-support": "bin.js" - } + "license": "ISC" }, - "node_modules/npm/node_modules/columnify": { - "version": "1.6.0", + "node_modules/npm/node_modules/cross-spawn": { + "version": "7.0.3", "inBundle": true, "license": "MIT", "dependencies": { - "strip-ansi": "^6.0.1", - "wcwidth": "^1.0.0" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" }, "engines": { - "node": ">=8.0.0" + "node": ">= 8" } }, - "node_modules/npm/node_modules/common-ancestor-path": { - "version": "1.0.1", - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/concat-map": { - "version": "0.0.1", - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/console-control-strings": { - "version": "1.1.0", + "node_modules/npm/node_modules/cross-spawn/node_modules/which": { + "version": "2.0.2", "inBundle": true, - "license": "ISC" + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } }, "node_modules/npm/node_modules/cssesc": { "version": "3.0.0", @@ -19927,7 +22771,7 @@ } }, "node_modules/npm/node_modules/debug": { - "version": "4.3.4", + "version": "4.3.6", "inBundle": true, "license": "MIT", "dependencies": { @@ -19947,52 +22791,19 @@ "inBundle": true, "license": "MIT" }, - "node_modules/npm/node_modules/debuglog": { - "version": "1.0.1", - "inBundle": true, - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/npm/node_modules/defaults": { - "version": "1.0.3", - "inBundle": true, - "license": "MIT", - "dependencies": { - "clone": "^1.0.2" - } - }, - "node_modules/npm/node_modules/delegates": { - "version": "1.0.0", - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/depd": { - "version": "1.1.2", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/npm/node_modules/dezalgo": { - "version": "1.0.4", - "inBundle": true, - "license": "ISC", - "dependencies": { - "asap": "^2.0.0", - "wrappy": "1" - } - }, "node_modules/npm/node_modules/diff": { - "version": "5.1.0", + "version": "5.2.0", "inBundle": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" } }, + "node_modules/npm/node_modules/eastasianwidth": { + "version": "0.2.0", + "inBundle": true, + "license": "MIT" + }, "node_modules/npm/node_modules/emoji-regex": { "version": "8.0.0", "inBundle": true, @@ -20020,106 +22831,78 @@ "inBundle": true, "license": "MIT" }, - "node_modules/npm/node_modules/fastest-levenshtein": { - "version": "1.0.12", - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/fs-minipass": { - "version": "2.1.0", - "inBundle": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/npm/node_modules/fs.realpath": { - "version": "1.0.0", - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/function-bind": { - "version": "1.1.1", + "node_modules/npm/node_modules/exponential-backoff": { + "version": "3.1.1", "inBundle": true, - "license": "MIT" + "license": "Apache-2.0" }, - "node_modules/npm/node_modules/gauge": { - "version": "4.0.4", + "node_modules/npm/node_modules/fastest-levenshtein": { + "version": "1.0.16", "inBundle": true, - "license": "ISC", - "dependencies": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.3", - "console-control-strings": "^1.1.0", - "has-unicode": "^2.0.1", - "signal-exit": "^3.0.7", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.5" - }, + "license": "MIT", "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">= 4.9.1" } }, - "node_modules/npm/node_modules/glob": { - "version": "8.0.3", + "node_modules/npm/node_modules/foreground-child": { + "version": "3.3.0", "inBundle": true, "license": "ISC", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" }, "engines": { - "node": ">=12" + "node": ">=14" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/npm/node_modules/graceful-fs": { - "version": "4.2.10", - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/has": { - "version": "1.0.3", + "node_modules/npm/node_modules/fs-minipass": { + "version": "3.0.3", "inBundle": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "function-bind": "^1.1.1" + "minipass": "^7.0.3" }, "engines": { - "node": ">= 0.4.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/has-flag": { - "version": "4.0.0", + "node_modules/npm/node_modules/glob": { + "version": "10.4.5", "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/npm/node_modules/has-unicode": { - "version": "2.0.1", + "node_modules/npm/node_modules/graceful-fs": { + "version": "4.2.11", "inBundle": true, "license": "ISC" }, "node_modules/npm/node_modules/hosted-git-info": { - "version": "5.2.1", + "version": "8.0.0", "inBundle": true, "license": "ISC", "dependencies": { - "lru-cache": "^7.5.1" + "lru-cache": "^10.0.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm/node_modules/http-cache-semantics": { @@ -20128,36 +22911,27 @@ "license": "BSD-2-Clause" }, "node_modules/npm/node_modules/http-proxy-agent": { - "version": "5.0.0", + "version": "7.0.2", "inBundle": true, "license": "MIT", "dependencies": { - "@tootallnate/once": "2", - "agent-base": "6", - "debug": "4" + "agent-base": "^7.1.0", + "debug": "^4.3.4" }, "engines": { - "node": ">= 6" + "node": ">= 14" } }, "node_modules/npm/node_modules/https-proxy-agent": { - "version": "5.0.1", + "version": "7.0.5", "inBundle": true, "license": "MIT", "dependencies": { - "agent-base": "6", + "agent-base": "^7.0.2", "debug": "4" }, "engines": { - "node": ">= 6" - } - }, - "node_modules/npm/node_modules/humanize-ms": { - "version": "1.2.1", - "inBundle": true, - "license": "MIT", - "dependencies": { - "ms": "^2.0.0" + "node": ">= 14" } }, "node_modules/npm/node_modules/iconv-lite": { @@ -20173,14 +22947,14 @@ } }, "node_modules/npm/node_modules/ignore-walk": { - "version": "5.0.1", + "version": "7.0.0", "inBundle": true, "license": "ISC", "dependencies": { - "minimatch": "^5.0.1" + "minimatch": "^9.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm/node_modules/imurmurhash": { @@ -20199,83 +22973,63 @@ "node": ">=8" } }, - "node_modules/npm/node_modules/infer-owner": { - "version": "1.0.4", - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/inflight": { - "version": "1.0.6", - "inBundle": true, - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/npm/node_modules/inherits": { - "version": "2.0.4", - "inBundle": true, - "license": "ISC" - }, "node_modules/npm/node_modules/ini": { - "version": "3.0.1", + "version": "5.0.0", "inBundle": true, "license": "ISC", "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm/node_modules/init-package-json": { - "version": "3.0.2", + "version": "7.0.1", "inBundle": true, "license": "ISC", "dependencies": { - "npm-package-arg": "^9.0.1", - "promzard": "^0.3.0", - "read": "^1.0.7", - "read-package-json": "^5.0.0", + "@npmcli/package-json": "^6.0.0", + "npm-package-arg": "^12.0.0", + "promzard": "^2.0.0", + "read": "^4.0.0", "semver": "^7.3.5", "validate-npm-package-license": "^3.0.4", - "validate-npm-package-name": "^4.0.0" + "validate-npm-package-name": "^6.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/npm/node_modules/ip": { - "version": "2.0.0", + "node_modules/npm/node_modules/ip-address": { + "version": "9.0.5", "inBundle": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } }, "node_modules/npm/node_modules/ip-regex": { - "version": "4.3.0", + "version": "5.0.0", "inBundle": true, "license": "MIT", "engines": { - "node": ">=8" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/npm/node_modules/is-cidr": { - "version": "4.0.2", + "version": "5.1.0", "inBundle": true, "license": "BSD-2-Clause", "dependencies": { - "cidr-regex": "^3.1.1" + "cidr-regex": "^4.1.1" }, "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/is-core-module": { - "version": "2.10.0", - "inBundle": true, - "license": "MIT", - "dependencies": { - "has": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=14" } }, "node_modules/npm/node_modules/is-fullwidth-code-point": { @@ -20296,11 +23050,33 @@ "inBundle": true, "license": "ISC" }, - "node_modules/npm/node_modules/json-parse-even-better-errors": { - "version": "2.3.1", + "node_modules/npm/node_modules/jackspeak": { + "version": "3.4.3", + "inBundle": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/npm/node_modules/jsbn": { + "version": "1.1.0", "inBundle": true, "license": "MIT" }, + "node_modules/npm/node_modules/json-parse-even-better-errors": { + "version": "4.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, "node_modules/npm/node_modules/json-stringify-nice": { "version": "1.1.4", "inBundle": true, @@ -20318,255 +23094,257 @@ "license": "MIT" }, "node_modules/npm/node_modules/just-diff": { - "version": "5.1.1", + "version": "6.0.2", "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/just-diff-apply": { - "version": "5.4.1", + "version": "5.5.0", "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/libnpmaccess": { - "version": "6.0.4", + "version": "9.0.0", "inBundle": true, "license": "ISC", "dependencies": { - "aproba": "^2.0.0", - "minipass": "^3.1.1", - "npm-package-arg": "^9.0.1", - "npm-registry-fetch": "^13.0.0" + "npm-package-arg": "^12.0.0", + "npm-registry-fetch": "^18.0.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm/node_modules/libnpmdiff": { - "version": "4.0.5", + "version": "7.0.0", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/disparity-colors": "^2.0.0", - "@npmcli/installed-package-contents": "^1.0.7", - "binary-extensions": "^2.2.0", + "@npmcli/arborist": "^8.0.0", + "@npmcli/installed-package-contents": "^3.0.0", + "binary-extensions": "^2.3.0", "diff": "^5.1.0", - "minimatch": "^5.0.1", - "npm-package-arg": "^9.0.1", - "pacote": "^13.6.1", - "tar": "^6.1.0" + "minimatch": "^9.0.4", + "npm-package-arg": "^12.0.0", + "pacote": "^19.0.0", + "tar": "^6.2.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm/node_modules/libnpmexec": { - "version": "4.0.14", + "version": "9.0.0", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/arborist": "^5.6.3", - "@npmcli/ci-detect": "^2.0.0", - "@npmcli/fs": "^2.1.1", - "@npmcli/run-script": "^4.2.0", - "chalk": "^4.1.0", - "mkdirp-infer-owner": "^2.0.0", - "npm-package-arg": "^9.0.1", - "npmlog": "^6.0.2", - "pacote": "^13.6.1", - "proc-log": "^2.0.0", - "read": "^1.0.7", - "read-package-json-fast": "^2.0.2", + "@npmcli/arborist": "^8.0.0", + "@npmcli/run-script": "^9.0.1", + "ci-info": "^4.0.0", + "npm-package-arg": "^12.0.0", + "pacote": "^19.0.0", + "proc-log": "^5.0.0", + "read": "^4.0.0", + "read-package-json-fast": "^4.0.0", "semver": "^7.3.7", - "walk-up-path": "^1.0.0" + "walk-up-path": "^3.0.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm/node_modules/libnpmfund": { - "version": "3.0.5", + "version": "6.0.0", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/arborist": "^5.6.3" + "@npmcli/arborist": "^8.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm/node_modules/libnpmhook": { - "version": "8.0.4", + "version": "11.0.0", "inBundle": true, "license": "ISC", "dependencies": { "aproba": "^2.0.0", - "npm-registry-fetch": "^13.0.0" + "npm-registry-fetch": "^18.0.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm/node_modules/libnpmorg": { - "version": "4.0.4", + "version": "7.0.0", "inBundle": true, "license": "ISC", "dependencies": { "aproba": "^2.0.0", - "npm-registry-fetch": "^13.0.0" + "npm-registry-fetch": "^18.0.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm/node_modules/libnpmpack": { - "version": "4.1.3", + "version": "8.0.0", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/run-script": "^4.1.3", - "npm-package-arg": "^9.0.1", - "pacote": "^13.6.1" + "@npmcli/arborist": "^8.0.0", + "@npmcli/run-script": "^9.0.1", + "npm-package-arg": "^12.0.0", + "pacote": "^19.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm/node_modules/libnpmpublish": { - "version": "6.0.5", + "version": "10.0.0", "inBundle": true, "license": "ISC", "dependencies": { - "normalize-package-data": "^4.0.0", - "npm-package-arg": "^9.0.1", - "npm-registry-fetch": "^13.0.0", + "ci-info": "^4.0.0", + "normalize-package-data": "^7.0.0", + "npm-package-arg": "^12.0.0", + "npm-registry-fetch": "^18.0.1", + "proc-log": "^5.0.0", "semver": "^7.3.7", - "ssri": "^9.0.0" + "sigstore": "^2.2.0", + "ssri": "^12.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm/node_modules/libnpmsearch": { - "version": "5.0.4", + "version": "8.0.0", "inBundle": true, "license": "ISC", "dependencies": { - "npm-registry-fetch": "^13.0.0" + "npm-registry-fetch": "^18.0.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm/node_modules/libnpmteam": { - "version": "4.0.4", + "version": "7.0.0", "inBundle": true, "license": "ISC", "dependencies": { "aproba": "^2.0.0", - "npm-registry-fetch": "^13.0.0" + "npm-registry-fetch": "^18.0.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm/node_modules/libnpmversion": { - "version": "3.0.7", + "version": "7.0.0", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/git": "^3.0.0", - "@npmcli/run-script": "^4.1.3", - "json-parse-even-better-errors": "^2.3.1", - "proc-log": "^2.0.0", + "@npmcli/git": "^6.0.1", + "@npmcli/run-script": "^9.0.1", + "json-parse-even-better-errors": "^4.0.0", + "proc-log": "^5.0.0", "semver": "^7.3.7" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm/node_modules/lru-cache": { - "version": "7.13.2", + "version": "10.4.3", "inBundle": true, - "license": "ISC", - "engines": { - "node": ">=12" - } + "license": "ISC" }, "node_modules/npm/node_modules/make-fetch-happen": { - "version": "10.2.1", + "version": "14.0.1", "inBundle": true, "license": "ISC", "dependencies": { - "agentkeepalive": "^4.2.1", - "cacache": "^16.1.0", - "http-cache-semantics": "^4.1.0", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.0", - "is-lambda": "^1.0.1", - "lru-cache": "^7.7.1", - "minipass": "^3.1.6", - "minipass-collect": "^1.0.2", - "minipass-fetch": "^2.0.3", + "@npmcli/agent": "^3.0.0", + "cacache": "^19.0.1", + "http-cache-semantics": "^4.1.1", + "minipass": "^7.0.2", + "minipass-fetch": "^4.0.0", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "negotiator": "^0.6.3", + "proc-log": "^5.0.0", "promise-retry": "^2.0.1", - "socks-proxy-agent": "^7.0.0", - "ssri": "^9.0.0" + "ssri": "^12.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm/node_modules/minimatch": { - "version": "5.1.0", + "version": "9.0.5", "inBundle": true, "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=10" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/npm/node_modules/minipass": { - "version": "3.3.4", + "version": "7.1.2", "inBundle": true, "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, "engines": { - "node": ">=8" + "node": ">=16 || 14 >=14.17" } }, "node_modules/npm/node_modules/minipass-collect": { - "version": "1.0.2", + "version": "2.0.1", "inBundle": true, "license": "ISC", "dependencies": { - "minipass": "^3.0.0" + "minipass": "^7.0.3" }, "engines": { - "node": ">= 8" + "node": ">=16 || 14 >=14.17" } }, "node_modules/npm/node_modules/minipass-fetch": { - "version": "2.1.1", + "version": "4.0.0", "inBundle": true, "license": "MIT", "dependencies": { - "minipass": "^3.1.6", + "minipass": "^7.0.3", "minipass-sized": "^1.0.3", - "minizlib": "^2.1.2" + "minizlib": "^3.0.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" }, "optionalDependencies": { "encoding": "^0.1.13" } }, + "node_modules/npm/node_modules/minipass-fetch/node_modules/minizlib": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.4", + "rimraf": "^5.0.5" + }, + "engines": { + "node": ">= 18" + } + }, "node_modules/npm/node_modules/minipass-flush": { "version": "1.0.5", "inBundle": true, @@ -20578,13 +23356,15 @@ "node": ">= 8" } }, - "node_modules/npm/node_modules/minipass-json-stream": { - "version": "1.0.1", + "node_modules/npm/node_modules/minipass-flush/node_modules/minipass": { + "version": "3.3.6", "inBundle": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "jsonparse": "^1.3.1", - "minipass": "^3.0.0" + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, "node_modules/npm/node_modules/minipass-pipeline": { @@ -20598,6 +23378,17 @@ "node": ">=8" } }, + "node_modules/npm/node_modules/minipass-pipeline/node_modules/minipass": { + "version": "3.3.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/npm/node_modules/minipass-sized": { "version": "1.0.3", "inBundle": true, @@ -20609,6 +23400,17 @@ "node": ">=8" } }, + "node_modules/npm/node_modules/minipass-sized/node_modules/minipass": { + "version": "3.3.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/npm/node_modules/minizlib": { "version": "2.1.2", "inBundle": true, @@ -20621,25 +23423,23 @@ "node": ">= 8" } }, - "node_modules/npm/node_modules/mkdirp": { - "version": "1.0.4", + "node_modules/npm/node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", "inBundle": true, - "license": "MIT", - "bin": { - "mkdirp": "bin/cmd.js" + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" }, "engines": { - "node": ">=10" + "node": ">=8" } }, - "node_modules/npm/node_modules/mkdirp-infer-owner": { - "version": "2.0.0", + "node_modules/npm/node_modules/mkdirp": { + "version": "1.0.4", "inBundle": true, - "license": "ISC", - "dependencies": { - "chownr": "^2.0.0", - "infer-owner": "^1.0.4", - "mkdirp": "^1.0.3" + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" }, "engines": { "node": ">=10" @@ -20651,9 +23451,12 @@ "license": "MIT" }, "node_modules/npm/node_modules/mute-stream": { - "version": "0.0.8", + "version": "2.0.0", "inBundle": true, - "license": "ISC" + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } }, "node_modules/npm/node_modules/negotiator": { "version": "0.6.3", @@ -20664,278 +23467,359 @@ } }, "node_modules/npm/node_modules/node-gyp": { - "version": "9.1.0", + "version": "10.2.0", "inBundle": true, "license": "MIT", "dependencies": { "env-paths": "^2.2.0", - "glob": "^7.1.4", + "exponential-backoff": "^3.1.1", + "glob": "^10.3.10", "graceful-fs": "^4.2.6", - "make-fetch-happen": "^10.0.3", - "nopt": "^5.0.0", - "npmlog": "^6.0.0", - "rimraf": "^3.0.2", + "make-fetch-happen": "^13.0.0", + "nopt": "^7.0.0", + "proc-log": "^4.1.0", "semver": "^7.3.5", - "tar": "^6.1.2", - "which": "^2.0.2" + "tar": "^6.2.1", + "which": "^4.0.0" }, "bin": { "node-gyp": "bin/node-gyp.js" }, "engines": { - "node": "^12.22 || ^14.13 || >=16" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/node-gyp/node_modules/brace-expansion": { - "version": "1.1.11", + "node_modules/npm/node_modules/node-gyp/node_modules/@npmcli/agent": { + "version": "2.2.2", "inBundle": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "agent-base": "^7.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "lru-cache": "^10.0.1", + "socks-proxy-agent": "^8.0.3" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/node-gyp/node_modules/glob": { - "version": "7.2.3", + "node_modules/npm/node_modules/node-gyp/node_modules/@npmcli/fs": { + "version": "3.1.1", "inBundle": true, "license": "ISC", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "semver": "^7.3.5" }, "engines": { - "node": "*" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/abbrev": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/cacache": { + "version": "18.0.4", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^3.1.0", + "fs-minipass": "^3.0.0", + "glob": "^10.2.2", + "lru-cache": "^10.0.1", + "minipass": "^7.0.3", + "minipass-collect": "^2.0.1", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^4.0.0", + "ssri": "^10.0.0", + "tar": "^6.1.11", + "unique-filename": "^3.0.0" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "engines": { + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/node-gyp/node_modules/minimatch": { - "version": "3.1.2", + "node_modules/npm/node_modules/node-gyp/node_modules/isexe": { + "version": "3.1.1", + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/make-fetch-happen": { + "version": "13.0.1", "inBundle": true, "license": "ISC", "dependencies": { - "brace-expansion": "^1.1.7" + "@npmcli/agent": "^2.0.0", + "cacache": "^18.0.0", + "http-cache-semantics": "^4.1.1", + "is-lambda": "^1.0.1", + "minipass": "^7.0.2", + "minipass-fetch": "^3.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "proc-log": "^4.2.0", + "promise-retry": "^2.0.1", + "ssri": "^10.0.0" }, "engines": { - "node": "*" + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/minipass-fetch": { + "version": "3.0.5", + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.3", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" } }, "node_modules/npm/node_modules/node-gyp/node_modules/nopt": { - "version": "5.0.0", + "version": "7.2.1", "inBundle": true, "license": "ISC", "dependencies": { - "abbrev": "1" + "abbrev": "^2.0.0" }, "bin": { "nopt": "bin/nopt.js" }, "engines": { - "node": ">=6" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/nopt": { - "version": "6.0.0", + "node_modules/npm/node_modules/node-gyp/node_modules/proc-log": { + "version": "4.2.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/ssri": { + "version": "10.0.6", "inBundle": true, "license": "ISC", "dependencies": { - "abbrev": "^1.0.0" + "minipass": "^7.0.3" }, - "bin": { - "nopt": "bin/nopt.js" + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/unique-filename": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "unique-slug": "^4.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/normalize-package-data": { - "version": "4.0.1", + "node_modules/npm/node_modules/node-gyp/node_modules/unique-slug": { + "version": "4.0.0", "inBundle": true, - "license": "BSD-2-Clause", + "license": "ISC", "dependencies": { - "hosted-git-info": "^5.0.0", - "is-core-module": "^2.8.1", - "semver": "^7.3.5", - "validate-npm-package-license": "^3.0.4" + "imurmurhash": "^0.1.4" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/npm-audit-report": { - "version": "3.0.0", + "node_modules/npm/node_modules/node-gyp/node_modules/which": { + "version": "4.0.0", "inBundle": true, "license": "ISC", "dependencies": { - "chalk": "^4.0.0" + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^16.13.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/npm-bundled": { - "version": "2.0.1", + "node_modules/npm/node_modules/nopt": { + "version": "8.0.0", "inBundle": true, "license": "ISC", "dependencies": { - "npm-normalize-package-bin": "^2.0.0" + "abbrev": "^2.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/npm/node_modules/npm-bundled/node_modules/npm-normalize-package-bin": { + "node_modules/npm/node_modules/nopt/node_modules/abbrev": { "version": "2.0.0", "inBundle": true, "license": "ISC", "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/npm-install-checks": { - "version": "5.0.0", + "node_modules/npm/node_modules/normalize-package-data": { + "version": "7.0.0", "inBundle": true, "license": "BSD-2-Clause", "dependencies": { - "semver": "^7.1.1" + "hosted-git-info": "^8.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/npm/node_modules/npm-normalize-package-bin": { - "version": "1.0.1", + "node_modules/npm/node_modules/npm-audit-report": { + "version": "6.0.0", "inBundle": true, - "license": "ISC" + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } }, - "node_modules/npm/node_modules/npm-package-arg": { - "version": "9.1.0", + "node_modules/npm/node_modules/npm-bundled": { + "version": "4.0.0", "inBundle": true, "license": "ISC", "dependencies": { - "hosted-git-info": "^5.0.0", - "proc-log": "^2.0.1", - "semver": "^7.3.5", - "validate-npm-package-name": "^4.0.0" + "npm-normalize-package-bin": "^4.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/npm/node_modules/npm-packlist": { - "version": "5.1.3", + "node_modules/npm/node_modules/npm-install-checks": { + "version": "7.1.0", "inBundle": true, - "license": "ISC", + "license": "BSD-2-Clause", "dependencies": { - "glob": "^8.0.1", - "ignore-walk": "^5.0.1", - "npm-bundled": "^2.0.0", - "npm-normalize-package-bin": "^2.0.0" - }, - "bin": { - "npm-packlist": "bin/index.js" + "semver": "^7.1.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/npm/node_modules/npm-packlist/node_modules/npm-normalize-package-bin": { - "version": "2.0.0", + "node_modules/npm/node_modules/npm-normalize-package-bin": { + "version": "4.0.0", "inBundle": true, "license": "ISC", "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/npm/node_modules/npm-pick-manifest": { - "version": "7.0.2", + "node_modules/npm/node_modules/npm-package-arg": { + "version": "12.0.0", "inBundle": true, "license": "ISC", "dependencies": { - "npm-install-checks": "^5.0.0", - "npm-normalize-package-bin": "^2.0.0", - "npm-package-arg": "^9.0.0", - "semver": "^7.3.5" + "hosted-git-info": "^8.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^6.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/npm/node_modules/npm-pick-manifest/node_modules/npm-normalize-package-bin": { - "version": "2.0.0", + "node_modules/npm/node_modules/npm-packlist": { + "version": "9.0.0", "inBundle": true, "license": "ISC", + "dependencies": { + "ignore-walk": "^7.0.0" + }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/npm/node_modules/npm-profile": { - "version": "6.2.1", + "node_modules/npm/node_modules/npm-pick-manifest": { + "version": "10.0.0", "inBundle": true, "license": "ISC", "dependencies": { - "npm-registry-fetch": "^13.0.1", - "proc-log": "^2.0.0" + "npm-install-checks": "^7.1.0", + "npm-normalize-package-bin": "^4.0.0", + "npm-package-arg": "^12.0.0", + "semver": "^7.3.5" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/npm/node_modules/npm-registry-fetch": { - "version": "13.3.1", + "node_modules/npm/node_modules/npm-profile": { + "version": "11.0.1", "inBundle": true, "license": "ISC", "dependencies": { - "make-fetch-happen": "^10.0.6", - "minipass": "^3.1.6", - "minipass-fetch": "^2.0.3", - "minipass-json-stream": "^1.0.1", - "minizlib": "^2.1.2", - "npm-package-arg": "^9.0.1", - "proc-log": "^2.0.0" + "npm-registry-fetch": "^18.0.0", + "proc-log": "^5.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/npm/node_modules/npm-user-validate": { - "version": "1.0.1", - "inBundle": true, - "license": "BSD-2-Clause" - }, - "node_modules/npm/node_modules/npmlog": { - "version": "6.0.2", + "node_modules/npm/node_modules/npm-registry-fetch": { + "version": "18.0.1", "inBundle": true, "license": "ISC", "dependencies": { - "are-we-there-yet": "^3.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^4.0.3", - "set-blocking": "^2.0.0" + "@npmcli/redact": "^3.0.0", + "jsonparse": "^1.3.1", + "make-fetch-happen": "^14.0.0", + "minipass": "^7.0.2", + "minipass-fetch": "^4.0.0", + "minizlib": "^3.0.1", + "npm-package-arg": "^12.0.0", + "proc-log": "^5.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/npm/node_modules/once": { - "version": "1.4.0", + "node_modules/npm/node_modules/npm-registry-fetch/node_modules/minizlib": { + "version": "3.0.1", "inBundle": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "wrappy": "1" + "minipass": "^7.0.4", + "rimraf": "^5.0.5" + }, + "engines": { + "node": ">= 18" } }, - "node_modules/npm/node_modules/opener": { - "version": "1.5.2", + "node_modules/npm/node_modules/npm-user-validate": { + "version": "3.0.0", "inBundle": true, - "license": "(WTFPL OR MIT)", - "bin": { - "opener": "bin/opener-bin.js" + "license": "BSD-2-Clause", + "engines": { + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm/node_modules/p-map": { @@ -20952,63 +23836,79 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/npm/node_modules/package-json-from-dist": { + "version": "1.0.0", + "inBundle": true, + "license": "BlueOak-1.0.0" + }, "node_modules/npm/node_modules/pacote": { - "version": "13.6.2", + "version": "19.0.0", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/git": "^3.0.0", - "@npmcli/installed-package-contents": "^1.0.7", - "@npmcli/promise-spawn": "^3.0.0", - "@npmcli/run-script": "^4.1.0", - "cacache": "^16.0.0", - "chownr": "^2.0.0", - "fs-minipass": "^2.1.0", - "infer-owner": "^1.0.4", - "minipass": "^3.1.6", - "mkdirp": "^1.0.4", - "npm-package-arg": "^9.0.0", - "npm-packlist": "^5.1.0", - "npm-pick-manifest": "^7.0.0", - "npm-registry-fetch": "^13.0.1", - "proc-log": "^2.0.0", + "@npmcli/git": "^6.0.0", + "@npmcli/installed-package-contents": "^3.0.0", + "@npmcli/package-json": "^6.0.0", + "@npmcli/promise-spawn": "^8.0.0", + "@npmcli/run-script": "^9.0.0", + "cacache": "^19.0.0", + "fs-minipass": "^3.0.0", + "minipass": "^7.0.2", + "npm-package-arg": "^12.0.0", + "npm-packlist": "^9.0.0", + "npm-pick-manifest": "^10.0.0", + "npm-registry-fetch": "^18.0.0", + "proc-log": "^5.0.0", "promise-retry": "^2.0.1", - "read-package-json": "^5.0.0", - "read-package-json-fast": "^2.0.3", - "rimraf": "^3.0.2", - "ssri": "^9.0.0", + "sigstore": "^2.2.0", + "ssri": "^12.0.0", "tar": "^6.1.11" }, "bin": { - "pacote": "lib/bin.js" + "pacote": "bin/index.js" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm/node_modules/parse-conflict-json": { - "version": "2.0.2", + "version": "4.0.0", "inBundle": true, "license": "ISC", "dependencies": { - "json-parse-even-better-errors": "^2.3.1", - "just-diff": "^5.0.1", + "json-parse-even-better-errors": "^4.0.0", + "just-diff": "^6.0.0", "just-diff-apply": "^5.2.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/npm/node_modules/path-is-absolute": { - "version": "1.0.1", + "node_modules/npm/node_modules/path-key": { + "version": "3.1.1", "inBundle": true, "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=8" + } + }, + "node_modules/npm/node_modules/path-scurry": { + "version": "1.11.1", + "inBundle": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/npm/node_modules/postcss-selector-parser": { - "version": "6.0.10", + "version": "6.1.2", "inBundle": true, "license": "MIT", "dependencies": { @@ -21020,11 +23920,19 @@ } }, "node_modules/npm/node_modules/proc-log": { - "version": "2.0.1", + "version": "5.0.0", "inBundle": true, "license": "ISC", "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/proggy": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm/node_modules/promise-all-reject-late": { @@ -21036,7 +23944,7 @@ } }, "node_modules/npm/node_modules/promise-call-limit": { - "version": "1.0.1", + "version": "3.0.1", "inBundle": true, "license": "ISC", "funding": { @@ -21061,11 +23969,14 @@ } }, "node_modules/npm/node_modules/promzard": { - "version": "0.3.0", + "version": "2.0.0", "inBundle": true, "license": "ISC", "dependencies": { - "read": "1" + "read": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm/node_modules/qrcode-terminal": { @@ -21076,161 +23987,57 @@ } }, "node_modules/npm/node_modules/read": { - "version": "1.0.7", + "version": "4.0.0", "inBundle": true, "license": "ISC", "dependencies": { - "mute-stream": "~0.0.4" + "mute-stream": "^2.0.0" }, "engines": { - "node": ">=0.8" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm/node_modules/read-cmd-shim": { - "version": "3.0.0", - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/read-package-json": { - "version": "5.0.2", + "version": "5.0.0", "inBundle": true, "license": "ISC", - "dependencies": { - "glob": "^8.0.1", - "json-parse-even-better-errors": "^2.3.1", - "normalize-package-data": "^4.0.0", - "npm-normalize-package-bin": "^2.0.0" - }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm/node_modules/read-package-json-fast": { - "version": "2.0.3", - "inBundle": true, - "license": "ISC", - "dependencies": { - "json-parse-even-better-errors": "^2.3.0", - "npm-normalize-package-bin": "^1.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/read-package-json/node_modules/npm-normalize-package-bin": { - "version": "2.0.0", + "version": "4.0.0", "inBundle": true, "license": "ISC", - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/readable-stream": { - "version": "3.6.0", - "inBundle": true, - "license": "MIT", "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "json-parse-even-better-errors": "^4.0.0", + "npm-normalize-package-bin": "^4.0.0" }, "engines": { - "node": ">= 6" - } - }, - "node_modules/npm/node_modules/readdir-scoped-modules": { - "version": "1.1.0", - "inBundle": true, - "license": "ISC", - "dependencies": { - "debuglog": "^1.0.1", - "dezalgo": "^1.0.0", - "graceful-fs": "^4.1.2", - "once": "^1.3.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm/node_modules/retry": { "version": "0.12.0", "inBundle": true, "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/npm/node_modules/rimraf": { - "version": "3.0.2", - "inBundle": true, - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/rimraf/node_modules/brace-expansion": { - "version": "1.1.11", - "inBundle": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/npm/node_modules/rimraf/node_modules/glob": { - "version": "7.2.3", - "inBundle": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/rimraf/node_modules/minimatch": { - "version": "3.1.2", - "inBundle": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/npm/node_modules/safe-buffer": { - "version": "5.2.1", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], + "engines": { + "node": ">= 4" + } + }, + "node_modules/npm/node_modules/rimraf": { + "version": "5.0.10", "inBundle": true, - "license": "MIT" + "license": "ISC", + "dependencies": { + "glob": "^10.3.7" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, "node_modules/npm/node_modules/safer-buffer": { "version": "2.1.2", @@ -21239,12 +24046,9 @@ "optional": true }, "node_modules/npm/node_modules/semver": { - "version": "7.3.7", + "version": "7.6.3", "inBundle": true, "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -21252,26 +24056,51 @@ "node": ">=10" } }, - "node_modules/npm/node_modules/semver/node_modules/lru-cache": { - "version": "6.0.0", + "node_modules/npm/node_modules/shebang-command": { + "version": "2.0.0", "inBundle": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "yallist": "^4.0.0" + "shebang-regex": "^3.0.0" }, "engines": { - "node": ">=10" + "node": ">=8" } }, - "node_modules/npm/node_modules/set-blocking": { - "version": "2.0.0", + "node_modules/npm/node_modules/shebang-regex": { + "version": "3.0.0", "inBundle": true, - "license": "ISC" + "license": "MIT", + "engines": { + "node": ">=8" + } }, "node_modules/npm/node_modules/signal-exit": { - "version": "3.0.7", + "version": "4.1.0", "inBundle": true, - "license": "ISC" + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/sigstore": { + "version": "2.3.1", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^2.3.2", + "@sigstore/core": "^1.0.0", + "@sigstore/protobuf-specs": "^0.3.2", + "@sigstore/sign": "^2.3.2", + "@sigstore/tuf": "^2.3.4", + "@sigstore/verify": "^1.2.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } }, "node_modules/npm/node_modules/smart-buffer": { "version": "4.2.0", @@ -21283,33 +24112,33 @@ } }, "node_modules/npm/node_modules/socks": { - "version": "2.7.0", + "version": "2.8.3", "inBundle": true, "license": "MIT", "dependencies": { - "ip": "^2.0.0", + "ip-address": "^9.0.5", "smart-buffer": "^4.2.0" }, "engines": { - "node": ">= 10.13.0", + "node": ">= 10.0.0", "npm": ">= 3.0.0" } }, "node_modules/npm/node_modules/socks-proxy-agent": { - "version": "7.0.0", + "version": "8.0.4", "inBundle": true, "license": "MIT", "dependencies": { - "agent-base": "^6.0.2", - "debug": "^4.3.3", - "socks": "^2.6.2" + "agent-base": "^7.1.1", + "debug": "^4.3.4", + "socks": "^2.8.3" }, "engines": { - "node": ">= 10" + "node": ">= 14" } }, "node_modules/npm/node_modules/spdx-correct": { - "version": "3.1.1", + "version": "3.2.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { @@ -21317,13 +24146,22 @@ "spdx-license-ids": "^3.0.0" } }, + "node_modules/npm/node_modules/spdx-correct/node_modules/spdx-expression-parse": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, "node_modules/npm/node_modules/spdx-exceptions": { - "version": "2.3.0", + "version": "2.5.0", "inBundle": true, "license": "CC-BY-3.0" }, "node_modules/npm/node_modules/spdx-expression-parse": { - "version": "3.0.1", + "version": "4.0.0", "inBundle": true, "license": "MIT", "dependencies": { @@ -21332,30 +24170,41 @@ } }, "node_modules/npm/node_modules/spdx-license-ids": { - "version": "3.0.11", + "version": "3.0.18", "inBundle": true, "license": "CC0-1.0" }, + "node_modules/npm/node_modules/sprintf-js": { + "version": "1.1.3", + "inBundle": true, + "license": "BSD-3-Clause" + }, "node_modules/npm/node_modules/ssri": { - "version": "9.0.1", + "version": "12.0.0", "inBundle": true, "license": "ISC", "dependencies": { - "minipass": "^3.1.1" + "minipass": "^7.0.3" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/npm/node_modules/string_decoder": { - "version": "1.3.0", + "node_modules/npm/node_modules/string-width": { + "version": "4.2.3", "inBundle": true, "license": "MIT", "dependencies": { - "safe-buffer": "~5.2.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" } }, - "node_modules/npm/node_modules/string-width": { + "node_modules/npm/node_modules/string-width-cjs": { + "name": "string-width", "version": "4.2.3", "inBundle": true, "license": "MIT", @@ -21376,74 +24225,256 @@ "ansi-regex": "^5.0.1" }, "engines": { - "node": ">=8" + "node": ">=8" + } + }, + "node_modules/npm/node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/supports-color": { + "version": "9.4.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/npm/node_modules/tar": { + "version": "6.2.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/tar/node_modules/fs-minipass": { + "version": "2.1.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/tar/node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/text-table": { + "version": "0.2.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/tiny-relative-date": { + "version": "1.3.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/treeverse": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/tuf-js": { + "version": "2.2.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "@tufjs/models": "2.0.1", + "debug": "^4.3.4", + "make-fetch-happen": "^13.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/tuf-js/node_modules/@npmcli/agent": { + "version": "2.2.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "agent-base": "^7.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "lru-cache": "^10.0.1", + "socks-proxy-agent": "^8.0.3" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/tuf-js/node_modules/@npmcli/fs": { + "version": "3.1.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/tuf-js/node_modules/cacache": { + "version": "18.0.4", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^3.1.0", + "fs-minipass": "^3.0.0", + "glob": "^10.2.2", + "lru-cache": "^10.0.1", + "minipass": "^7.0.3", + "minipass-collect": "^2.0.1", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^4.0.0", + "ssri": "^10.0.0", + "tar": "^6.1.11", + "unique-filename": "^3.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/tuf-js/node_modules/make-fetch-happen": { + "version": "13.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/agent": "^2.0.0", + "cacache": "^18.0.0", + "http-cache-semantics": "^4.1.1", + "is-lambda": "^1.0.1", + "minipass": "^7.0.2", + "minipass-fetch": "^3.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "proc-log": "^4.2.0", + "promise-retry": "^2.0.1", + "ssri": "^10.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/tuf-js/node_modules/minipass-fetch": { + "version": "3.0.5", + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.3", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/npm/node_modules/tuf-js/node_modules/proc-log": { + "version": "4.2.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/supports-color": { - "version": "7.2.0", + "node_modules/npm/node_modules/tuf-js/node_modules/ssri": { + "version": "10.0.6", "inBundle": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "has-flag": "^4.0.0" + "minipass": "^7.0.3" }, "engines": { - "node": ">=8" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/tar": { - "version": "6.1.11", + "node_modules/npm/node_modules/tuf-js/node_modules/unique-filename": { + "version": "3.0.0", "inBundle": true, "license": "ISC", "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^3.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" + "unique-slug": "^4.0.0" }, "engines": { - "node": ">= 10" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/text-table": { - "version": "0.2.0", - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/tiny-relative-date": { - "version": "1.3.0", - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/treeverse": { - "version": "2.0.0", + "node_modules/npm/node_modules/tuf-js/node_modules/unique-slug": { + "version": "4.0.0", "inBundle": true, "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4" + }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/unique-filename": { - "version": "2.0.1", + "version": "4.0.0", "inBundle": true, "license": "ISC", "dependencies": { - "unique-slug": "^3.0.0" + "unique-slug": "^5.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm/node_modules/unique-slug": { - "version": "3.0.0", + "version": "5.0.0", "inBundle": true, "license": "ISC", "dependencies": { "imurmurhash": "^0.1.4" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm/node_modules/util-deprecate": { @@ -21460,67 +24491,153 @@ "spdx-expression-parse": "^3.0.0" } }, + "node_modules/npm/node_modules/validate-npm-package-license/node_modules/spdx-expression-parse": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, "node_modules/npm/node_modules/validate-npm-package-name": { - "version": "4.0.0", + "version": "6.0.0", "inBundle": true, "license": "ISC", - "dependencies": { - "builtins": "^5.0.0" - }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm/node_modules/walk-up-path": { - "version": "1.0.0", + "version": "3.0.1", "inBundle": true, "license": "ISC" }, - "node_modules/npm/node_modules/wcwidth": { - "version": "1.0.1", + "node_modules/npm/node_modules/which": { + "version": "5.0.0", "inBundle": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "defaults": "^1.0.3" + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/npm/node_modules/which": { - "version": "2.0.2", + "node_modules/npm/node_modules/which/node_modules/isexe": { + "version": "3.1.1", "inBundle": true, "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/npm/node_modules/wrap-ansi": { + "version": "8.1.0", + "inBundle": true, + "license": "MIT", "dependencies": { - "isexe": "^2.0.0" + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" }, - "bin": { - "node-which": "bin/node-which" + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, "engines": { - "node": ">= 8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/npm/node_modules/wide-align": { - "version": "1.1.5", + "node_modules/npm/node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", "inBundle": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "string-width": "^1.0.2 || 2 || 3 || 4" + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/npm/node_modules/wrappy": { - "version": "1.0.2", + "node_modules/npm/node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.0.1", "inBundle": true, - "license": "ISC" + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/emoji-regex": { + "version": "9.2.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/string-width": { + "version": "5.1.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.1.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } }, "node_modules/npm/node_modules/write-file-atomic": { - "version": "4.0.2", + "version": "6.0.0", "inBundle": true, "license": "ISC", "dependencies": { "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" + "signal-exit": "^4.0.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm/node_modules/yallist": { @@ -21533,7 +24650,6 @@ "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", "deprecated": "This package is no longer supported.", - "optional": true, "dependencies": { "are-we-there-yet": "^3.0.0", "console-control-strings": "^1.1.0", @@ -21726,6 +24842,23 @@ "node": ">=6" } }, + "node_modules/oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "engines": { + "node": "*" + } + }, + "node_modules/oauth4webapi": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/oauth4webapi/-/oauth4webapi-3.1.2.tgz", + "integrity": "sha512-KQZkNU+xn02lWrFu5Vjqg9E81yPtDSxUZorRHlLWVoojD+H/0GFbH59kcnz5Thdjj7c4/mYMBPj/mhvGe/kKXA==", + "optional": true, + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/object-assign": { "version": "4.1.1", "license": "MIT", @@ -21909,6 +25042,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/openid-client": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-6.1.3.tgz", + "integrity": "sha512-74sc0bR4ptfwCwMheLPaJHTQnds+97Yu6O8eQgoO3MRcd53xkfKyl3gNAsRsYSYoO+AVG3eCgnRMjRkZ6n2RYw==", + "optional": true, + "dependencies": { + "jose": "^5.9.6", + "oauth4webapi": "^3.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/optionator": { "version": "0.9.3", "license": "MIT", @@ -22847,6 +25993,11 @@ "version": "1.2.0", "license": "MIT" }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" + }, "node_modules/pico-signals": { "version": "1.0.0", "license": "MIT" @@ -23557,14 +26708,12 @@ "node_modules/promise-inflight": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", - "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", - "optional": true + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==" }, "node_modules/promise-retry": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", - "optional": true, "dependencies": { "err-code": "^2.0.2", "retry": "^0.12.0" @@ -23576,14 +26725,12 @@ "node_modules/promise-retry/node_modules/err-code": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", - "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", - "optional": true + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==" }, "node_modules/promise-retry/node_modules/retry": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", - "optional": true, "engines": { "node": ">= 4" } @@ -23811,6 +26958,11 @@ "version": "1.0.2", "license": "ISC" }, + "node_modules/psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" + }, "node_modules/pump": { "version": "3.0.0", "license": "MIT", @@ -25829,6 +28981,79 @@ "node": ">=0.10.0" } }, + "node_modules/request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", + "dependencies": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/request/node_modules/form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/request/node_modules/qs": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/request/node_modules/tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dependencies": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/request/node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "bin": { + "uuid": "bin/uuid" + } + }, "node_modules/require-directory": { "version": "2.1.1", "license": "MIT", @@ -26054,6 +29279,11 @@ "node": ">= 0.4.0" } }, + "node_modules/rfc4648": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/rfc4648/-/rfc4648-1.5.3.tgz", + "integrity": "sha512-MjOWxM065+WswwnmNONOT+bD1nXzY9Km6u3kzvnx8F8/HXGZdz3T6e6vZJ8Q/RIMUSp/nxqjH3GwvJDy8ijeQQ==" + }, "node_modules/rimraf": { "version": "3.0.2", "license": "ISC", @@ -27517,7 +30747,6 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", - "devOptional": true, "engines": { "node": ">= 6.0.0", "npm": ">= 3.0.0" @@ -27527,7 +30756,6 @@ "version": "2.8.3", "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", - "devOptional": true, "dependencies": { "ip-address": "^9.0.5", "smart-buffer": "^4.2.0" @@ -27541,7 +30769,6 @@ "version": "6.2.1", "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz", "integrity": "sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==", - "optional": true, "dependencies": { "agent-base": "^6.0.2", "debug": "^4.3.3", @@ -27664,7 +30891,6 @@ }, "node_modules/sprintf-js": { "version": "1.1.3", - "devOptional": true, "license": "BSD-3-Clause" }, "node_modules/sqlite3": { @@ -27743,11 +30969,47 @@ "license": "MIT", "optional": true }, + "node_modules/sshpk": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", + "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", + "dependencies": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "bin": { + "sshpk-conv": "bin/sshpk-conv", + "sshpk-sign": "bin/sshpk-sign", + "sshpk-verify": "bin/sshpk-verify" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sshpk/node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/sshpk/node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==" + }, "node_modules/ssri": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", - "optional": true, "dependencies": { "minipass": "^3.1.1" }, @@ -27991,6 +31253,14 @@ "node": ">= 6" } }, + "node_modules/stream-buffers": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-3.0.3.tgz", + "integrity": "sha512-pqMqwQCso0PBJt2PQmDO0cFj0lyqmiwOMiMSkVtRokl7e+ZTRYgDHKnuZNbqjiJXgsg4nuqtD/zxuo9KqTp0Yw==", + "engines": { + "node": ">= 0.10.0" + } + }, "node_modules/stream-chunks": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/stream-chunks/-/stream-chunks-1.0.0.tgz", @@ -29466,7 +32736,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", - "optional": true, "dependencies": { "unique-slug": "^2.0.0" } @@ -29475,7 +32744,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", - "optional": true, "dependencies": { "imurmurhash": "^0.1.4" } @@ -30011,7 +33279,6 @@ "version": "1.1.5", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", - "optional": true, "dependencies": { "string-width": "^1.0.2 || 2 || 3 || 4" } @@ -30381,9 +33648,9 @@ } }, "node_modules/ws": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", - "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", "engines": { "node": ">=10.0.0" }, diff --git a/package.json b/package.json index 0e6ca92e2..af4278a34 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,7 @@ "@chainsafe/libp2p-noise": "^15.1.0", "@chainsafe/libp2p-yamux": "^6.0.2", "@elastic/elasticsearch": "^8.14.0", + "@kubernetes/client-node": "^0.22.2", "@libp2p/autonat": "^1.1.1", "@libp2p/bootstrap": "^10.1.1", "@libp2p/circuit-relay-v2": "^1.1.1", @@ -97,6 +98,7 @@ "ethers": "^6.8.1", "express": "^4.21.1", "hyperdiff": "^2.0.16", + "i": "^0.3.7", "ip": "^2.0.1", "it-pipe": "^3.0.1", "libp2p": "^1.8.0", @@ -104,6 +106,7 @@ "lzma-purejs-requirejs": "^1.0.0", "n3": "^1.17.2", "node-cron": "^3.0.3", + "npm": "^10.9.0", "private-ip": "^3.0.2", "rdf-utils-fs": "^3.0.0", "rdf-validate-shacl": "^0.5.5", diff --git a/resources/c2d_k8_yaml/algo-job-template.yaml b/resources/c2d_k8_yaml/algo-job-template.yaml new file mode 100644 index 000000000..72a991892 --- /dev/null +++ b/resources/c2d_k8_yaml/algo-job-template.yaml @@ -0,0 +1,31 @@ +apiVersion: batch/v1 +kind: Job +metadata: + labels: + app: "" + workflow: "" + name: "" + namespace: "" +spec: + template: + metadata: + labels: + workflow: "" + spec: + containers: + - args: [] + command: [] + image: "" + imagePullPolicy: Always + name: "workflow-executor" + resources: {} + env: [] + securityContext: + allowPrivilegeEscalation: false + restartPolicy: OnFailure + schedulerName: default-scheduler + securityContext: {} + serviceAccount: default + serviceAccountName: default + automountServiceAccountToken: false +status: {} diff --git a/resources/c2d_k8_yaml/configmap-template.yaml b/resources/c2d_k8_yaml/configmap-template.yaml new file mode 100644 index 000000000..45eba316e --- /dev/null +++ b/resources/c2d_k8_yaml/configmap-template.yaml @@ -0,0 +1,6 @@ +kind: ConfigMap +apiVersion: v1 +metadata: + name: "" + namespace: "" +data: {} diff --git a/resources/c2d_k8_yaml/configure-job-template.yaml b/resources/c2d_k8_yaml/configure-job-template.yaml new file mode 100644 index 000000000..72a991892 --- /dev/null +++ b/resources/c2d_k8_yaml/configure-job-template.yaml @@ -0,0 +1,31 @@ +apiVersion: batch/v1 +kind: Job +metadata: + labels: + app: "" + workflow: "" + name: "" + namespace: "" +spec: + template: + metadata: + labels: + workflow: "" + spec: + containers: + - args: [] + command: [] + image: "" + imagePullPolicy: Always + name: "workflow-executor" + resources: {} + env: [] + securityContext: + allowPrivilegeEscalation: false + restartPolicy: OnFailure + schedulerName: default-scheduler + securityContext: {} + serviceAccount: default + serviceAccountName: default + automountServiceAccountToken: false +status: {} diff --git a/resources/c2d_k8_yaml/publish-job-template.yaml b/resources/c2d_k8_yaml/publish-job-template.yaml new file mode 100644 index 000000000..72a991892 --- /dev/null +++ b/resources/c2d_k8_yaml/publish-job-template.yaml @@ -0,0 +1,31 @@ +apiVersion: batch/v1 +kind: Job +metadata: + labels: + app: "" + workflow: "" + name: "" + namespace: "" +spec: + template: + metadata: + labels: + workflow: "" + spec: + containers: + - args: [] + command: [] + image: "" + imagePullPolicy: Always + name: "workflow-executor" + resources: {} + env: [] + securityContext: + allowPrivilegeEscalation: false + restartPolicy: OnFailure + schedulerName: default-scheduler + securityContext: {} + serviceAccount: default + serviceAccountName: default + automountServiceAccountToken: false +status: {} diff --git a/resources/c2d_k8_yaml/pvc.yaml b/resources/c2d_k8_yaml/pvc.yaml new file mode 100644 index 000000000..e02a9f1ed --- /dev/null +++ b/resources/c2d_k8_yaml/pvc.yaml @@ -0,0 +1,12 @@ +kind: PersistentVolumeClaim +apiVersion: v1 +metadata: + name: REPLACE_PVC_NAME + namespace: REPLACE_NAMESPACE +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 'REPLACE_STORAGE_SIZE' + storageClassName: 'REPLACE_STORAGE_CLASS_NAME' diff --git a/src/@types/C2D/C2D.ts b/src/@types/C2D/C2D.ts index 6b66c51fa..82cde2091 100644 --- a/src/@types/C2D/C2D.ts +++ b/src/@types/C2D/C2D.ts @@ -6,7 +6,9 @@ export enum C2DClusterType { // eslint-disable-next-line no-unused-vars NODE_LOCAL = 1, // eslint-disable-next-line no-unused-vars - DOCKER = 2 + DOCKER = 2, + // eslint-disable-next-line no-unused-vars + K8 = 3 } export interface C2DClusterInfo { @@ -53,6 +55,21 @@ export interface C2DDockerConfig { freeComputeOptions?: ComputeEnvironment } +export interface C2DK8User { + name: string + certData?: string + keyData?: string +} +export interface C2DK8ClusterConfig { + name?: string + server: string + context: string + skipTLSVerify?: boolean + user: C2DK8User + environments: ComputeEnvironment[] + freeComputeOptions?: ComputeEnvironment +} + export interface ComputeEnvByChain { [chainId: number]: ComputeEnvironment[] } diff --git a/src/components/c2d/compute_engine_k8.ts b/src/components/c2d/compute_engine_k8.ts new file mode 100644 index 000000000..e840abec6 --- /dev/null +++ b/src/components/c2d/compute_engine_k8.ts @@ -0,0 +1,441 @@ +import { Readable } from 'stream' +import { C2DClusterType, C2DStatusNumber, C2DStatusText } from '../../@types/C2D/C2D.js' +import type { + C2DClusterInfo, + ComputeEnvironment, + ComputeAlgorithm, + ComputeAsset, + ComputeJob, + ComputeOutput, + DBComputeJob, + ComputeResult +} from '../../@types/C2D/C2D.js' +import { ZeroAddress } from 'ethers' +import fs from 'fs' +// import { getProviderFeeToken } from '../../components/core/utils/feesHandler.js' +import { getConfiguration } from '../../utils/config.js' +import { C2DEngine } from './compute_engine_base.js' +import { C2DDatabase } from '../database/C2DDatabase.js' +import { create256Hash } from '../../utils/crypt.js' +import { Storage } from '../storage/index.js' +import * as k8s from '@kubernetes/client-node' +import type { ContainerCreateOptions, VolumeCreateOptions } from 'dockerode' +import * as tar from 'tar' +import { + createWriteStream, + existsSync, + mkdirSync, + rmSync, + writeFileSync, + statSync, + createReadStream +} from 'fs' +import { pipeline } from 'node:stream/promises' +import { CORE_LOGGER } from '../../utils/logging/common.js' +import { generateUniqueID } from '../database/sqliteCompute.js' + +export class C2DEngineK8 extends C2DEngine { + private envs: ComputeEnvironment[] = [] + protected db: C2DDatabase + public k8s: any + private k8sApi: any + public yamlFolder: string = './resources/c2d_k8_yaml/' + private cronTimer: any + private cronTime: number = 2000 + public constructor(clusterConfig: C2DClusterInfo, db: C2DDatabase) { + super(clusterConfig) + this.db = db + this.k8s = null + this.k8sApi = null + if (clusterConfig.connection) { + // Alex config + clusterConfig.connection = { + name: 'my-server', + server: 'https://172.26.53.25:6443', + user: { + name: 'kubernetes-admin', + certData: + 'LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURLVENDQWhHZ0F3SUJBZ0lJSVRkQmVDeHBiand3RFFZSktvWklodmNOQVFFTEJRQXdGVEVUTUJFR0ExVUUKQXhNS2EzVmlaWEp1WlhSbGN6QWVGdzB5TkRFeE1EY3dOekEwTVRKYUZ3MHlOVEV4TURjd056QTVNVFZhTUR3eApIekFkQmdOVkJBb1RGbXQxWW1WaFpHMDZZMngxYzNSbGNpMWhaRzFwYm5NeEdUQVhCZ05WQkFNVEVHdDFZbVZ5CmJtVjBaWE10WVdSdGFXNHdnZ0VpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElCRHdBd2dnRUtBb0lCQVFDMklkT2EKeEkveEVnM0EvQXcyZjQ3UVNpMmxQbUViMGFBUU0rN2l1Rmd6aTkvRmd3RHF1K01kRWxPRTNUam8yM1BJSWE5OQprM25ES0VZc0ZXN21rWERZZVdzM1ZncW12U2tYVXFNLzJJdXZHcEpOZXY1dnJnUG9NSzFYSjFNdzBIcEhjZFhjCm1SR1Z4RVF3UVBqbGsrY0xCdk1LYS9GaVl3R2hCeXEyRmVhNngvbHFJMnZkSnhWM2ZrL2JGOVVvbXZYTzZPdXUKMGlsQkNHYmlBRFlMTCtRZjZOQnVUSDBZb1d6bTNkNnZ3R3RHRGtQWDlpdFllZW1YaGVCQWhTdVVwT3BtM283VgpQbktiQ3dyS1VMRmZIYXFvU3NYcE93R2JPODBGNUtKQ2l5ektTMmlNak4rbTd6RXNWWmpGSjROM3F3MHhaWU1MCnhOU0pHYWRTS2FDN3EzKzFBZ01CQUFHalZqQlVNQTRHQTFVZER3RUIvd1FFQXdJRm9EQVRCZ05WSFNVRUREQUsKQmdnckJnRUZCUWNEQWpBTUJnTlZIUk1CQWY4RUFqQUFNQjhHQTFVZEl3UVlNQmFBRkdpNklzVDZ3UURJeHBhbgo2RitzVnFmemRYUi9NQTBHQ1NxR1NJYjNEUUVCQ3dVQUE0SUJBUURCUFE0RTR3NDZocjFRc01MaWxTeGNwNzJkCnBNUUhwbTFzdTJFWlk2QmJFWjBncmlpVFlhc0VCN1RUWHpJR3h1cTRDemdIUDBWbFE4QmlkSHJIOVZwcm03aEgKWEVjeWFVcHFubG85cWpTWU16dW5XaTYrUUE5SzFPNUhSRDd6MzBHeDkzZzhhTC9ibE1meEpHUjNCTE1SZ1JvUApiamtxVjVVc2gyUnVnKy9EVlRTNG5sQlRMTWlML1BDQkEvRXoxdFZYR1lHSDhjdTdyVnVYMHhWSmxQTVpjYk9ECmZQWGdhamp6dXkxQXF5OGZrLzAxZUtRZGZHbmZHK214WW0xVlR0bk12NmZ5cVQzZ2ZHV1JsNWliYmltamR3d3YKRlZLSDBvY3RlSlVpWmdnMDRVNVhFNE9sb0tYdm90Q1JFR2toRUFkSzR5dk8yT0ovTENzZTNWSXM3UTEvCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K', + keyData: + 'LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb3dJQkFBS0NBUUVBdGlIVG1zU1A4UklOd1B3TU5uK08wRW90cFQ1aEc5R2dFRFB1NHJoWU00dmZ4WU1BCjZydmpIUkpUaE4wNDZOdHp5Q0d2ZlpONXd5aEdMQlZ1NXBGdzJIbHJOMVlLcHIwcEYxS2pQOWlMcnhxU1RYcisKYjY0RDZEQ3RWeWRUTU5CNlIzSFYzSmtSbGNSRU1FRDQ1WlBuQ3diekNtdnhZbU1Cb1FjcXRoWG11c2Y1YWlOcgozU2NWZDM1UDJ4ZlZLSnIxenVqcnJ0SXBRUWhtNGdBMkN5L2tIK2pRYmt4OUdLRnM1dDNlcjhCclJnNUQxL1lyCldIbnBsNFhnUUlVcmxLVHFadDZPMVQ1eW13c0t5bEN4WHgycXFFckY2VHNCbXp2TkJlU2lRb3NzeWt0b2pJemYKcHU4eExGV1l4U2VEZDZzTk1XV0RDOFRVaVJtblVpbWd1NnQvdFFJREFRQUJBb0lCQURQbU1ia3hkOTczQ1FwTQpDR0xqT0Z2c04xT1dFZS91YlJFUTYycVpvekNWRkIvaE03cXY3WWpVTnc1dVI1QTdNS1AvelZVWVdDTWZiOWVTCkIvY1Z6TFV5N0RWcGhFRjlONTlZd2dJb2Y2MVhBZ2VvRzZiUlRIVzJvVDVyaTA0bXFpRi9zN1JYdmVZU2RtZlYKcTljbnJUZThORGR0Q096RFQ2eUdNVXFQdFI3VjhBNzUvdkt4RzY3WEw1dll5YXNqUTRCWTR1R0V1ZkRkVUdtYgpPU3YvODlzQlZkbVRWV2hmNEI2S0FhNHd1WUJjbDR0SzkzYTZxcDd5N2x4cmoxdXVCa0NZcytTUGpRM0JJcnJ2Cml4ZjZ4NmliZ1pBUkhhWDkzY2tXNjd5Z05VQk5KclRwbDdOeVgyUHo5T2ZxY3NQdXVoK0lhWEFmdTlKcmcrOVIKOFFUcmRHRUNnWUVBMnRZaWQzU1d2SVdLN1FxY2lyOTdkUVgzMUxlSTVqejhCZGpNUnEwdXNUbmhFa1dvOXZkWQo4REVZNjFqK2h6a25SYTY1YTh3aDNtWXh1ZmlNcXc4NnIvN25oak92WHZ3SVYvdXAzZEo1MG5DWFdvOEEyYm9GCmxmeFVDUUZaRy9IdXJYSG9BZzVTWFpvcU1LcE9Oa1ZwRWJ2NmZrS0ttVWNWNlZYbGd0T2RmYmtDZ1lFQTFRLzYKeVg0U09paERwNlNEbXIzVHBhTkh0Vjg2Rko2Sk1DZGFKSW5DYXRzYUt5ZFIvTzNSbmV0VFRvemlxRlloUzRFTwordUdlNGZvOTRoc0pVekxiOERXQmZrSGJLWWxMVVZQV0lnSDcwSnF2YlkwMlZjbHl6dUloWm4vaVBxMmtYcUMvCk5FeThwVFNiUit0N2RiWmlUaXVRbDlxR1dvbXRJUXI2QlFBcEw5MENnWUVBaEtQa01qbDFvQURsaXZXeW1wcWoKVHZQMkduWEFRYVZYTUlnT2tRd3BUL3lBQWw0OG9xeWJ1TUpaazFUV3VjbVhsekhuYTRKSVNRL3lOZ0dENmE4SwphR0I5bnFjM05jQlhvbFNFeWxIbnl2aTVsSWMzQWNFeDM0NFl0WGlldFVSMzRhTTM5LzhNUjNYSStzUlBNYS95CmxuQTB6VkN4eDJRQjBQTmljR0NwaGVrQ2dZQXpNMHpjQU56V2R2aVRIN2kwaGV1SGdXNXBDb0pGbERkNWgrdGsKbm1wSERYSURic2FJRm9wcC9iUWVTMExvbXhJVE4rZG1xTE5xc0owUWFkamEwbjBDQTRtajBxV2RISzRwMUJEQQpTV08vSkgwRndZcU9JUVBpN1hxRFREWU5RK01kRGxvRWNuQmU3djVsMFJQeEhLd1JCdTBQWU9jcWVLMVBKSGtpCm5JQkpUUUtCZ0JGYW0rUi9iaWYzOE9PZnluWHM1aFBoVFZEcUpOQmsrd01Da0l6R2NyVjlwRjRkSXRlanNHbGIKRHlaM0VCdCt4czNtYTE3U3MzTVM5SFRSYlY0OE9STEhqVE9vK2NUTzBHTHN2MkRxRHBVWElqLzdjaDV5OFhNeAprWmpxTDhNbFhJRmhvUHQ5R1RIRUUzNldZTFZ1Q1VuS250elZVQzF1SFNrc2F4dUhVOHoyCi0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg==' + }, + context: 'kubernetes-admin@kubernetes' + } + const cluster = { + name: clusterConfig.connection.name, + server: clusterConfig.connection.server, + skipTLSVerify: true + } + const user = { + name: clusterConfig.connection.user.name, + certData: clusterConfig.connection.user.certData, + keyData: clusterConfig.connection.user.keyData + } + const context = { + name: clusterConfig.connection.context, + cluster: clusterConfig.connection.name, + user: clusterConfig.connection.user.name + } + try { + const kc = new k8s.KubeConfig() + kc.loadFromOptions({ + clusters: [cluster], + users: [user], + contexts: [context], + currentContext: context.name + }) + + this.k8sApi = kc.makeApiClient(k8s.CoreV1Api) + } catch (e) { + console.error(e) + } + } + } + + // eslint-disable-next-line require-await + public override async getComputeEnvironments( + chainId?: number + ): Promise { + /** + * Returns all cluster's compute environments for a specific chainId. Env's id already contains the cluster hash + */ + if (!this.k8sApi) return [] + return this.envs + } + + // eslint-disable-next-line require-await + public override async startComputeJob( + assets: ComputeAsset[], + algorithm: ComputeAlgorithm, + output: ComputeOutput, + environment: string, + owner?: string, + validUntil?: number, + chainId?: number, + agreementId?: string + ): Promise { + if (!this.k8sApi) return [] + + const jobId = generateUniqueID() + // NOTE: this does not generate a unique ID... + // if i send 2 times the same startComputeJob parameters i get the same ID twice + // const jobId = create256Hash( + // JSON.stringify({ + // assets, + // algorithm, + // output, + // environment, + // owner, + // validUntil, + // chainId, + // agreementId + // }) + // ) + // TO DO C2D - Check image, check arhitecture, etc + let { image } = algorithm.meta.container + if (algorithm.meta.container.checksum) + image = image + '@' + algorithm.meta.container.checksum + else if (algorithm.meta.container.tag) + image = image + ':' + algorithm.meta.container.checksum + else image = image + ':latest' + console.log('Using image: ' + image) + + const job: DBComputeJob = { + clusterHash: this.getC2DConfig().hash, + containerImage: image, + owner, + jobId, + dateCreated: String(Date.now() / 1000), + dateFinished: null, + status: C2DStatusNumber.JobStarted, + statusText: C2DStatusText.JobStarted, + results: [], + algorithm, + assets, + agreementId, + expireTimestamp: Date.now() / 1000 + validUntil, + environment, + configlogURL: null, + publishlogURL: null, + algologURL: null, + outputsURL: null, + stopRequested: false, + isRunning: true, + isStarted: false + } + // make sure we actually were able to insert on DB + const addedId = await this.db.newJob(job) + if (!addedId) { + return [] + } + + // only now set the timer + if (!this.cronTimer) { + this.setNewTimer() + } + const cjob: ComputeJob = JSON.parse(JSON.stringify(job)) as ComputeJob + // we add cluster hash to user output + // cjob.jobId = this.getC2DConfig().hash + '-' + cjob.jobId + cjob.jobId = jobId + return [cjob] + } + + // eslint-disable-next-line require-await + public override async stopComputeJob( + jobId: string, + owner: string, + agreementId?: string + ): Promise { + return null + } + + // eslint-disable-next-line require-await + protected async getResults(jobId: string): Promise { + const job = await this.db.getJob(jobId) + return job.results + } + + // eslint-disable-next-line require-await + public override async getComputeJobStatus( + consumerAddress?: string, + agreementId?: string, + jobId?: string + ): Promise { + const job = await this.db.getJob(jobId) + if (!job) { + return [] + } + const res: ComputeJob = job as ComputeJob + // add results for algoLogs + res.results = await this.getResults(job.jobId) + return [res] + } + + // eslint-disable-next-line require-await + public override async getComputeJobResult( + consumerAddress: string, + jobId: string, + index: number + ): Promise { + const job = await this.db.getJob(jobId) + if (!job || job.owner !== consumerAddress) { + return null + } + // TO DO + return null + } + + // TO DO + // eslint-disable-next-line require-await + // public override async getStreamableLogs(jobId: string): Promise { + // } + + private async setNewTimer() { + // don't set the cron if we don't have compute environments + if ((await this.getComputeEnvironments()).length > 0) + this.cronTimer = setInterval(this.InternalLoop.bind(this), this.cronTime) + } + + private async InternalLoop() { + // this is the internal loop of k8 cluter + // gets list of all running jobs and process them one by one + clearInterval(this.cronTimer) + this.cronTimer = null + // get all running jobs + const jobs = await this.db.getRunningJobs(this.getC2DConfig().hash) + console.log('Got jobs for engine ' + this.getC2DConfig().hash) + console.log(jobs) + const promises: any = [] + for (const job of jobs) { + promises.push(this.processJob(job)) + } + // wait for all promises, there is no return + await Promise.all(promises) + // set the cron again + this.setNewTimer() + } + + // eslint-disable-next-line require-await + private async processJob(job: DBComputeJob) { + console.log('Process job started') + console.log(job) + // has to : + // - monitor running containers and stop them if over limits + // - monitor disc space and clean up + /* steps: + - create pvc & pv + + */ + if (job.status === C2DStatusNumber.JobStarted) { + // pull docker image + job.status = C2DStatusNumber.ConfiguringVolumes + job.statusText = C2DStatusText.ConfiguringVolumes + } + + if (job.status === C2DStatusNumber.ConfiguringVolumes) { + try { + job.status = C2DStatusNumber.Provisioning + job.statusText = C2DStatusText.Provisioning + await this.db.updateJob(job) + } catch (e) { + job.status = C2DStatusNumber.ContainerCreationFailed + job.statusText = C2DStatusText.ContainerCreationFailed + job.isRunning = false + await this.db.updateJob(job) + await this.cleanupJob(job) + } + return + } + if (job.status === C2DStatusNumber.Provisioning) { + try { + job.status = C2DStatusNumber.RunningAlgorithm + job.statusText = C2DStatusText.RunningAlgorithm + await this.db.updateJob(job) + } catch (e) { + job.status = C2DStatusNumber.ContainerCreationFailed + job.statusText = C2DStatusText.ContainerCreationFailed + // C2DStatusNumber.AlgorithmProvisioningFailed + // C2DStatusText.AlgorithmProvisioningFailed + // C2DStatusNumber.DataProvisioningFailed + // C2DStatusText.DataProvisioningFailed + job.isRunning = false + await this.db.updateJob(job) + await this.cleanupJob(job) + } + return + } + if (job.status === C2DStatusNumber.RunningAlgorithm) { + job.status = C2DStatusNumber.PublishingResults + job.statusText = C2DStatusText.PublishingResults + await this.db.updateJob(job) + } + if (job.status === C2DStatusNumber.PublishingResults) { + // get output + job.status = C2DStatusNumber.JobFinished + job.statusText = C2DStatusText.JobFinished + job.isRunning = false + await this.db.updateJob(job) + await this.cleanupJob(job) + } + } + + // eslint-disable-next-line require-await + private async cleanupJob(job: DBComputeJob) { + // cleaning up + } + + // clean up temporary files + // eslint-disable-next-line require-await + public override async cleanupExpiredStorage(job: DBComputeJob): Promise { + if (!job) return false + CORE_LOGGER.info('Cleaning up C2D storage for Job: ' + job.jobId) + } + + + + //k8 apis + private async createpvc(name:string,namespace:string,storage:string,storageClass:string){ + const yamlString=await fs.readFileSync(this.yamlFolder+"/pvc.yaml").toString() + + const yamlNamespace = k8s.loadYaml(yamlString); + try { + const createNamespaceRes = await this.k8sApi.createNamespace(yamlNamespace); + console.log('Created namespace: ', createNamespaceRes.body); + + const namespaceRes = await this.k8sApi.readNamespace("test"); + console.log('Namespace: ', namespaceRes.body); + + //await k8sApi.deleteNamespace(yamlNamespace.metadata.name, {}); + } catch (err) { + console.error(err); + } + + +} + +// this uses the docker engine, but exposes only one env, the free one +export class C2DEngineK8Free extends C2DEngineK8 { + public constructor(clusterConfig: C2DClusterInfo, db: C2DDatabase) { + // we remove envs, cause we have our own + const hash = create256Hash('free' + clusterConfig.hash) + const owerwrite = { + type: C2DClusterType.K8, + hash, + connection: clusterConfig.connection, + tempFolder: './c2d_storage/' + hash + } + super(owerwrite, db) + } + + // eslint-disable-next-line require-await + public override async getComputeEnvironments( + chainId?: number + ): Promise { + /** + * Returns all cluster's compute environments for a specific chainId. Env's id already contains the cluster hash + */ + // TO DO C2D - fill consts below + if (!this.k8s) return [] + const cpuType = '' + const currentJobs = 0 + const consumerAddress = '' + const envs: ComputeEnvironment[] = [ + { + id: `${this.getC2DConfig().hash}-free`, + cpuNumber: 1, + cpuType, + gpuNumber: 0, + ramGB: 1, + diskGB: 1, + priceMin: 0, + desc: 'Free', + currentJobs, + maxJobs: 1, + consumerAddress, + storageExpiry: 600, + maxJobDuration: 30, + feeToken: ZeroAddress, + free: true + } + ] + return envs + } + + public override async startComputeJob( + assets: ComputeAsset[], + algorithm: ComputeAlgorithm, + output: ComputeOutput, + environment: string, + owner?: string, + validUntil?: number, + chainId?: number, + agreementId?: string + ): Promise { + // since it's a free job, we need to mangle some params + agreementId = create256Hash( + JSON.stringify({ + owner, + assets, + algorithm, + time: process.hrtime.bigint().toString() + }) + ) + chainId = 0 + const envs = await this.getComputeEnvironments() + if (envs.length < 1) { + // no free env ?? + throw new Error('No free env found') + } + validUntil = envs[0].maxJobDuration + return await super.startComputeJob( + assets, + algorithm, + output, + environment, + owner, + validUntil, + chainId, + agreementId + ) + } +} diff --git a/src/components/c2d/compute_engines.ts b/src/components/c2d/compute_engines.ts index a119adaa3..a923e7134 100644 --- a/src/components/c2d/compute_engines.ts +++ b/src/components/c2d/compute_engines.ts @@ -2,6 +2,7 @@ import { C2DClusterType, ComputeEnvironment } from '../../@types/C2D/C2D.js' import { C2DEngine } from './compute_engine_base.js' import { C2DEngineOPFK8 } from './compute_engine_opf_k8.js' import { C2DEngineDocker, C2DEngineDockerFree } from './compute_engine_docker.js' +import { C2DEngineK8, C2DEngineK8Free } from './compute_engine_k8.js' import { OceanNodeConfig } from '../../@types/OceanNode.js' import { C2DDatabase } from '../database/C2DDatabase.js' export class C2DEngines { @@ -24,6 +25,13 @@ export class C2DEngines { haveFree = true } } + if (cluster.type === C2DClusterType.K8) { + this.engines.push(new C2DEngineK8(cluster, db)) + if (!haveFree) { + this.engines.push(new C2DEngineK8Free(cluster, db)) + haveFree = true + } + } } } } diff --git a/src/utils/config.ts b/src/utils/config.ts index 881d9eac6..71943557d 100644 --- a/src/utils/config.ts +++ b/src/utils/config.ts @@ -2,7 +2,8 @@ import type { DenyList, OceanNodeConfig, OceanNodeKeys } from '../@types/OceanNo import type { C2DClusterInfo, ComputeEnvironment, - C2DDockerConfig + C2DDockerConfig, + C2DK8ClusterConfig } from '../@types/C2D/C2D.js' import { C2DClusterType } from '../@types/C2D/C2D.js' import { createFromPrivKey } from '@libp2p/peer-id-factory' @@ -352,11 +353,58 @@ function getC2DClusterEnvironment(isStartup?: boolean): C2DClusterInfo[] { tempFolder: './c2d_storage/' + hash }) } - + // K* clusters + const k8Clusters = JSON.parse(getEnvValue(process.env.K8_CLUSTERS, null)) + if (k8Clusters) { + for (const cluster of k8Clusters) { + const k8Config: C2DK8ClusterConfig = { + name: cluster.name, + server: cluster.server, + user: { + name: cluster.username + }, + context: cluster.context, + environments: null + } + const hash = create256Hash(JSON.stringify(k8Config)) + k8Config.environments = getK8ComputeEnvironments(cluster.name, hash, isStartup) + clusters.push({ + connection: k8Config, + hash, + type: C2DClusterType.DOCKER, + tempFolder: './c2d_storage/' + hash + }) + } + } return clusters } // TODO C2D v2.0 +function getK8ComputeEnvironments( + clusterName: string, + clusterHash: string, + isStartup?: boolean +): ComputeEnvironment[] { + // TO DO = get env definitions for clusterName.. ignore the rest + const defaultOptions: ComputeEnvironment = { + id: `${clusterHash}-env`, + cpuNumber: 1, + cpuType: '', + gpuNumber: 0, + ramGB: 1, + diskGB: 1, + priceMin: 0, + desc: 'Free', + currentJobs: 0, + maxJobs: 1, + consumerAddress: '', + storageExpiry: 600, + maxJobDuration: 30, + feeToken: ZeroAddress, + free: true + } + return [defaultOptions] +} // eslint-disable-next-line no-unused-vars function getDockerFreeComputeOptions( clusterHash: string,