diff --git a/.github/actions/replace-version/action.yml b/.github/actions/replace-version/action.yml index 8edc8e6f9..2ba82a7f3 100644 --- a/.github/actions/replace-version/action.yml +++ b/.github/actions/replace-version/action.yml @@ -7,5 +7,5 @@ inputs: type: string runs: - using: "node16" + using: "node20" main: "dist/index.js" diff --git a/.github/renovate.json b/.github/renovate.json index cefdc1dca..b2eb80c83 100644 --- a/.github/renovate.json +++ b/.github/renovate.json @@ -2,14 +2,6 @@ "extends": ["github>fluencelabs/renovate", "github>fluencelabs/renovate:npm"], "enabledManagers": ["npm", "regex"], "regexManagers": [ - { - "fileMatch": ["^packages/cli/package/src/versions\\.json$"], - "matchStrings": [ - "\"nox\": \"(?[^:]+):(?.*)\",\n" - ], - "datasourceTemplate": "docker", - "depNameTemplate": "nox" - }, { "fileMatch": ["^packages/cli/package/src/versions\\.json$"], "matchStrings": [ @@ -27,14 +19,6 @@ ], "matchStringsStrategy": "any", "datasourceTemplate": "npm" - }, - { - "fileMatch": ["^packages/cli/package/src/versions\\.json$"], - "matchStrings": [ - "\"(?[marine|mrepl|marine\\-rs\\-sdk|marine\\-rs\\-sdk\\-test]+)\": \"(?[^\"\n]+)\"" - ], - "matchStringsStrategy": "any", - "datasourceTemplate": "crate" } ], "packageRules": [ diff --git a/.github/workflows/pack.yml b/.github/workflows/pack.yml index ce68e027f..5ebdc0398 100644 --- a/.github/workflows/pack.yml +++ b/.github/workflows/pack.yml @@ -23,30 +23,6 @@ on: description: "Promote version to channel" type: string default: "null" - marine-version: - description: "marine version" - type: string - default: "null" - mrepl-version: - description: "mrepl version" - type: string - default: "null" - marine-rs-sdk-version: - description: "marine-rs-sdk version" - type: string - default: "null" - marine-rs-sdk-test-version: - description: "marine-rs-sdk-test version" - type: string - default: "null" - js-client-snapshots: - description: "js-client snapshots" - type: string - default: "null" - aqua-snapshots: - description: "aqua snapshots" - type: string - default: "null" node-version: description: "Node version" type: string @@ -91,13 +67,9 @@ jobs: exportToken: false secrets: | kv/npm-registry/basicauth/ci token | NODE_AUTH_TOKEN; - kv/cargo-registry/users/ci token | CARGO_REGISTRIES_FLUENCE_TOKEN; kv/ci/fcli-binaries id | AWS_ACCESS_KEY_ID ; kv/ci/fcli-binaries secret | AWS_SECRET_ACCESS_KEY - - name: Setup Rust toolchain - uses: actions-rust-lang/setup-rust-toolchain@v1 - - name: Setup node with self-hosted npm registry uses: actions/setup-node@v4 with: @@ -107,40 +79,6 @@ jobs: - run: yarn install - - name: Set js-client version - if: inputs.js-client-snapshots != 'null' - uses: fluencelabs/github-actions/npm-set-dependency@main - with: - package: "@fluencelabs/js-client" - version: ${{ fromJson(inputs.js-client-snapshots)['js-client'] }} - package-manager: yarn - working-directory: packages/cli/package - - - name: Set aqua-api version - if: inputs.aqua-snapshots != 'null' - uses: fluencelabs/github-actions/npm-set-dependency@main - with: - package: "@fluencelabs/aqua-api" - version: "${{ fromJson(inputs.aqua-snapshots)['aqua-api'] }}" - package-manager: yarn - working-directory: packages/cli/package - - - name: Update versions.json - uses: ./.github/actions/replace-version - with: - versions: | - { - "npm": { - "@fluencelabs/js-client": "${{ inputs.js-client-snapshots == 'null' && 'null' || fromJson(inputs.js-client-snapshots)['js-client'] }}" - }, - "cargo": { - "marine": "${{ inputs.marine-version }}", - "mrepl": "${{ inputs.mrepl-version }}", - "marine-rs-sdk": "${{ inputs.marine-rs-sdk-version }}", - "marine-rs-sdk-test": "${{ inputs.marine-rs-sdk-test-version }}" - } - } - - name: Generate snapshot version if: inputs.upload-to-s3 != true id: version @@ -156,7 +94,7 @@ jobs: - name: Cache node binaries id: cache-node-binaries - uses: actions/cache@v3 + uses: actions/cache@v4 env: cache-name: cache-node-binaries with: @@ -194,7 +132,7 @@ jobs: - name: Upload archive to release if: inputs.tag != 'null' && inputs.platform != 'win32-x64' - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 with: files: fluence-cli-${{ inputs.platform }}.tar.gz tag_name: ${{ inputs.tag }} @@ -212,7 +150,7 @@ jobs: - name: Upload windows installer to release if: inputs.tag != 'null' && inputs.platform == 'win32-x64' - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 with: files: fluence-cli-${{ inputs.platform }}.exe tag_name: ${{ inputs.tag }} diff --git a/.github/workflows/snapshot.yml b/.github/workflows/snapshot.yml index 9bb11df6a..b77012ba2 100644 --- a/.github/workflows/snapshot.yml +++ b/.github/workflows/snapshot.yml @@ -7,38 +7,6 @@ on: description: "Git ref to checkout to" type: string default: "main" - marine-version: - description: "marine version" - type: string - default: "null" - mrepl-version: - description: "mrepl version" - type: string - default: "null" - marine-rs-sdk-version: - description: "marine-rs-sdk version" - type: string - default: "null" - marine-rs-sdk-test-version: - description: "marine-rs-sdk-test version" - type: string - default: "null" - js-client-snapshots: - description: "js-client snapshots" - type: string - default: "null" - aqua-snapshots: - description: "aqua snapshots" - type: string - default: "null" - spell-version: - description: "@fluencelabs/spell version" - type: string - default: "null" - installation-spell-version: - description: "@fluencelabs/installation-spell version" - type: string - default: "null" node-version: description: "Node version" type: string @@ -81,13 +49,9 @@ jobs: exportToken: false secrets: | kv/npm-registry/basicauth/ci token | NODE_AUTH_TOKEN; - kv/cargo-registry/users/ci token | CARGO_REGISTRIES_FLUENCE_TOKEN; kv/ci/fcli-binaries id | AWS_ACCESS_KEY_ID ; kv/ci/fcli-binaries secret | AWS_SECRET_ACCESS_KEY - - name: Setup Rust toolchain - uses: actions-rust-lang/setup-rust-toolchain@v1 - - name: Setup node with self-hosted npm registry uses: actions/setup-node@v4 with: @@ -103,50 +67,6 @@ jobs: - run: yarn install - - name: Set js-client version - if: inputs.js-client-snapshots != 'null' - uses: fluencelabs/github-actions/npm-set-dependency@main - with: - package: "@fluencelabs/js-client" - version: ${{ fromJson(inputs.js-client-snapshots)['js-client'] }} - package-manager: yarn - working-directory: packages/cli/package - - - name: Set aqua-api version - if: inputs.aqua-snapshots != 'null' - uses: fluencelabs/github-actions/npm-set-dependency@main - with: - package: "@fluencelabs/aqua-api" - version: "${{ fromJson(inputs.aqua-snapshots)['aqua-api'] }}" - package-manager: yarn - working-directory: packages/cli/package - - - name: Set installation-spell version - if: inputs.installation-spell-version != 'null' - uses: fluencelabs/github-actions/npm-set-dependency@main - with: - package: "@fluencelabs/installation-spell" - version: "${{ inputs.installation-spell-version }}" - package-manager: yarn - flags: "--dev" - working-directory: packages/cli/package - - - name: Update versions.json - uses: ./.github/actions/replace-version - with: - versions: | - { - "npm": { - "@fluencelabs/spell": "${{ inputs.spell-version == 'null' && 'null' || inputs.spell-version }}" - }, - "cargo": { - "marine": "${{ inputs.marine-version }}", - "mrepl": "${{ inputs.mrepl-version }}", - "marine-rs-sdk": "${{ inputs.marine-rs-sdk-version }}", - "marine-rs-sdk-test": "${{ inputs.marine-rs-sdk-test-version }}" - } - } - - name: Generate snapshot version id: version uses: fluencelabs/github-actions/generate-snapshot-id@main @@ -161,7 +81,7 @@ jobs: - name: Cache node binaries linux-x64 id: cache-node-binaries-linux-x64 - uses: actions/cache@v3 + uses: actions/cache@v4 env: cache-name: cache-node-binaries with: @@ -170,7 +90,7 @@ jobs: - name: Cache node binaries darwin-arm64 id: cache-node-binaries-darwin-arm64 - uses: actions/cache@v3 + uses: actions/cache@v4 env: cache-name: cache-node-binaries with: diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 88453f3cc..05ce0a70c 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -11,10 +11,6 @@ on: description: "Fluence environment to run tests against" type: string default: "local" - nox-image: - description: "nox image tag" - type: string - default: "null" chain-rpc-image: description: "chain-rpc image tag" type: string @@ -27,42 +23,10 @@ on: description: "subgraph-deploy-script image tag" type: string default: "null" - marine-version: - description: "marine version" - type: string - default: "null" - mrepl-version: - description: "mrepl version" - type: string - default: "null" - marine-rs-sdk-version: - description: "marine-rs-sdk version" - type: string - default: "null" - marine-rs-sdk-test-version: - description: "marine-rs-sdk-test version" - type: string - default: "null" - js-client-snapshots: - description: "js-client snapshots" - type: string - default: "null" - aqua-snapshots: - description: "aqua snapshots" - type: string - default: "null" deal-ts-clients-version: description: "deal-ts-clients version" type: string default: "null" - spell-version: - description: "@fluencelabs/spell version" - type: string - default: "null" - installation-spell-version: - description: "@fluencelabs/installation-spell version" - type: string - default: "null" node-version: description: "Node version" type: string @@ -78,7 +42,6 @@ env: DEBUG: "fcli:*,deal-ts-clients:*" FLUENCE_ENV: "${{ inputs.fluence-env }}" FLUENCE_USER_DIR: "${{ github.workspace }}/tmp/.fluence" - CARGO_REGISTRIES_FLUENCE_INDEX: "git://crates.fluence.dev/index" NPM_CONFIG_REGISTRY: "https://npm.fluence.dev" FLUENCE_LOG_DISPLAY_SPAN_LIST: true @@ -120,24 +83,6 @@ jobs: kv/docker-registry/basicauth/ci username | DOCKER_USERNAME ; kv/docker-registry/basicauth/ci password | DOCKER_PASSWORD ; kv/npm-registry/basicauth/ci token | NODE_AUTH_TOKEN; - kv/cargo-registry/users/ci token | CARGO_REGISTRIES_FLUENCE_TOKEN - - - name: Set nox image - run: | - # set nox image - case ${{ inputs.nox-image }} in - # if nox image is not passed from e2e read from versions.json - 'null') - nox="$(jq .nox packages/cli/package/src/versions.json -r)" - ;; - # else set nox image to snapshot passed from e2e - *) - nox=${{ inputs.nox-image }} - ;; - esac - - echo "nox image used for tests is $nox" - echo "NOX_IMAGE=${nox}" >> $GITHUB_ENV - name: Set deal images run: | @@ -178,9 +123,6 @@ jobs: username: ${{ env.DOCKER_USERNAME }} password: ${{ env.DOCKER_PASSWORD }} - - name: Setup Rust toolchain - uses: dsherret/rust-toolchain-file@v1 - - name: Setup node with self-hosted npm registry uses: actions/setup-node@v4 with: @@ -196,24 +138,6 @@ jobs: - run: yarn install - - name: Set js-client version - if: inputs.js-client-snapshots != 'null' - uses: fluencelabs/github-actions/npm-set-dependency@main - with: - package: "@fluencelabs/js-client" - version: ${{ fromJson(inputs.js-client-snapshots)['js-client'] }} - package-manager: yarn - working-directory: packages/cli/package - - - name: Set aqua-api version - if: inputs.aqua-snapshots != 'null' - uses: fluencelabs/github-actions/npm-set-dependency@main - with: - package: "@fluencelabs/aqua-api" - version: "${{ fromJson(inputs.aqua-snapshots)['aqua-api'] }}" - package-manager: yarn - working-directory: packages/cli/package - - name: Set deal-ts-clients version if: inputs.deal-ts-clients-version != 'null' uses: fluencelabs/github-actions/npm-set-dependency@main @@ -223,34 +147,14 @@ jobs: package-manager: yarn working-directory: packages/cli/package - - name: Set installation-spell version - if: inputs.installation-spell-version != 'null' - uses: fluencelabs/github-actions/npm-set-dependency@main - with: - package: "@fluencelabs/installation-spell" - version: "${{ inputs.installation-spell-version }}" - package-manager: yarn - flags: "--dev" - working-directory: packages/cli/package - - name: Update versions.json uses: ./.github/actions/replace-version with: versions: | { - "nox": "${{ env.NOX_IMAGE }}", "chain-rpc": "${{ env.CHAIN_RPC_IMAGE }}", "chain-deploy-script": "${{ env.CHAIN_DEPLOY_SCRIPT_IMAGE }}", - "subgraph-deploy-script": "${{ env.SUBGRAPH_DEPLOY_SCRIPT_IMAGE }}", - "npm": { - "@fluencelabs/spell": "${{ inputs.spell-version == 'null' && 'null' || inputs.spell-version }}" - }, - "cargo": { - "marine": "${{ inputs.marine-version }}", - "mrepl": "${{ inputs.mrepl-version }}", - "marine-rs-sdk": "${{ inputs.marine-rs-sdk-version }}", - "marine-rs-sdk-test": "${{ inputs.marine-rs-sdk-test-version }}" - } + "subgraph-deploy-script": "${{ env.SUBGRAPH_DEPLOY_SCRIPT_IMAGE }}" } - name: Cache turbo build setup @@ -263,7 +167,7 @@ jobs: - name: Cache node binaries id: cache-node-binaries - uses: actions/cache@v3 + uses: actions/cache@v4 env: cache-name: cache-node-binaries with: @@ -273,9 +177,6 @@ jobs: - name: Pack Fluence CLI for Linux run: yarn pack-${{ inputs.platform }} - - name: Download Marine and Mrepl - run: yarn download-marine-and-mrepl - - name: Generate templates run: yarn generate-templates @@ -286,18 +187,6 @@ jobs: - name: Run provider tests run: yarn vitest-provider - - name: Run deal deploy tests - run: yarn vitest-deal-deploy - - - name: Run deal update tests - run: yarn vitest-deal-update - - - name: Run smoke tests - run: yarn vitest-smoke - - - name: Run js-to-aqua tests - run: yarn vitest-js-to-aqua - - name: Show Runner Load Average run: uptime if: always() diff --git a/.prettierignore b/.prettierignore index 7b076479d..d7efc8a06 100644 --- a/.prettierignore +++ b/.prettierignore @@ -3,7 +3,6 @@ docs node_modules tmp .fluence -compiled-aqua gqlSchema.json gqlGenerated.ts CHANGELOG.md diff --git a/.vscode/extensions.json b/.vscode/extensions.json index cb5cc5ca1..ea02c44a4 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -4,7 +4,6 @@ "esbenp.prettier-vscode", "editorconfig.editorconfig", "streetsidesoftware.code-spell-checker", - "redhat.vscode-yaml", - "FluenceLabs.aqua" + "redhat.vscode-yaml" ] } diff --git a/cli/docs/commands/README.md b/cli/docs/commands/README.md deleted file mode 100644 index 1607e1ad8..000000000 --- a/cli/docs/commands/README.md +++ /dev/null @@ -1,2066 +0,0 @@ -# Commands - -* [`fluence air beautify [PATH]`](#fluence-air-beautify-path) -* [`fluence aqua`](#fluence-aqua) -* [`fluence aqua imports`](#fluence-aqua-imports) -* [`fluence aqua json [INPUT] [OUTPUT]`](#fluence-aqua-json-input-output) -* [`fluence aqua yml [INPUT] [OUTPUT]`](#fluence-aqua-yml-input-output) -* [`fluence autocomplete [SHELL]`](#fluence-autocomplete-shell) -* [`fluence build`](#fluence-build) -* [`fluence chain info`](#fluence-chain-info) -* [`fluence deal change-app [DEAL-ADDRESS] [NEW-APP-CID]`](#fluence-deal-change-app-deal-address-new-app-cid) -* [`fluence deal create`](#fluence-deal-create) -* [`fluence deal deposit [AMOUNT] [DEPLOYMENT-NAMES]`](#fluence-deal-deposit-amount-deployment-names) -* [`fluence deal info [DEPLOYMENT-NAMES]`](#fluence-deal-info-deployment-names) -* [`fluence deal logs [DEPLOYMENT-NAMES]`](#fluence-deal-logs-deployment-names) -* [`fluence deal stop [DEPLOYMENT-NAMES]`](#fluence-deal-stop-deployment-names) -* [`fluence deal withdraw [AMOUNT] [DEPLOYMENT-NAMES]`](#fluence-deal-withdraw-amount-deployment-names) -* [`fluence deal workers-add [DEPLOYMENT-NAMES]`](#fluence-deal-workers-add-deployment-names) -* [`fluence deal workers-remove [WORKER-IDS]`](#fluence-deal-workers-remove-worker-ids) -* [`fluence default env [ENV]`](#fluence-default-env-env) -* [`fluence default peers [ENV]`](#fluence-default-peers-env) -* [`fluence delegator collateral-add [IDS]`](#fluence-delegator-collateral-add-ids) -* [`fluence delegator collateral-withdraw [IDS]`](#fluence-delegator-collateral-withdraw-ids) -* [`fluence delegator reward-withdraw [IDS]`](#fluence-delegator-reward-withdraw-ids) -* [`fluence dep install [PACKAGE-NAME | PACKAGE-NAME@VERSION]`](#fluence-dep-install-package-name--package-nameversion) -* [`fluence dep reset`](#fluence-dep-reset) -* [`fluence dep uninstall PACKAGE-NAME`](#fluence-dep-uninstall-package-name) -* [`fluence dep versions`](#fluence-dep-versions) -* [`fluence deploy [DEPLOYMENT-NAMES]`](#fluence-deploy-deployment-names) -* [`fluence help [COMMAND]`](#fluence-help-command) -* [`fluence init [PATH]`](#fluence-init-path) -* [`fluence key default [NAME]`](#fluence-key-default-name) -* [`fluence key new [NAME]`](#fluence-key-new-name) -* [`fluence key remove [NAME]`](#fluence-key-remove-name) -* [`fluence local down`](#fluence-local-down) -* [`fluence local init`](#fluence-local-init) -* [`fluence local logs`](#fluence-local-logs) -* [`fluence local ps`](#fluence-local-ps) -* [`fluence local up`](#fluence-local-up) -* [`fluence module add [PATH | URL]`](#fluence-module-add-path--url) -* [`fluence module build [PATH]`](#fluence-module-build-path) -* [`fluence module new [NAME]`](#fluence-module-new-name) -* [`fluence module pack [PATH]`](#fluence-module-pack-path) -* [`fluence module remove [NAME | PATH | URL]`](#fluence-module-remove-name--path--url) -* [`fluence provider cc-activate`](#fluence-provider-cc-activate) -* [`fluence provider cc-create`](#fluence-provider-cc-create) -* [`fluence provider cc-finish`](#fluence-provider-cc-finish) -* [`fluence provider cc-info`](#fluence-provider-cc-info) -* [`fluence provider cc-remove`](#fluence-provider-cc-remove) -* [`fluence provider cc-rewards-withdraw`](#fluence-provider-cc-rewards-withdraw) -* [`fluence provider deal-exit`](#fluence-provider-deal-exit) -* [`fluence provider deal-list`](#fluence-provider-deal-list) -* [`fluence provider deal-rewards-info [DEAL-ADDRESS] [ON-CHAIN-WORKER-ID]`](#fluence-provider-deal-rewards-info-deal-address-on-chain-worker-id) -* [`fluence provider deal-rewards-withdraw`](#fluence-provider-deal-rewards-withdraw) -* [`fluence provider gen`](#fluence-provider-gen) -* [`fluence provider info`](#fluence-provider-info) -* [`fluence provider init`](#fluence-provider-init) -* [`fluence provider offer-create`](#fluence-provider-offer-create) -* [`fluence provider offer-info`](#fluence-provider-offer-info) -* [`fluence provider offer-remove`](#fluence-provider-offer-remove) -* [`fluence provider offer-update`](#fluence-provider-offer-update) -* [`fluence provider register`](#fluence-provider-register) -* [`fluence provider tokens-distribute`](#fluence-provider-tokens-distribute) -* [`fluence provider tokens-withdraw`](#fluence-provider-tokens-withdraw) -* [`fluence provider update`](#fluence-provider-update) -* [`fluence run`](#fluence-run) -* [`fluence service add [PATH | URL]`](#fluence-service-add-path--url) -* [`fluence service new [NAME]`](#fluence-service-new-name) -* [`fluence service remove [NAME | PATH | URL]`](#fluence-service-remove-name--path--url) -* [`fluence service repl [NAME | PATH | URL]`](#fluence-service-repl-name--path--url) -* [`fluence spell build [SPELL-NAMES]`](#fluence-spell-build-spell-names) -* [`fluence spell new [NAME]`](#fluence-spell-new-name) -* [`fluence update [CHANNEL]`](#fluence-update-channel) - -## `fluence air beautify [PATH]` - -Prints AIR script in human-readable Python-like representation. This representation cannot be executed and is intended to be read by mere mortals. - -``` -USAGE - $ fluence air beautify [PATH] [--no-input] - -ARGUMENTS - PATH Path to an AIR file. Must be relative to the current working directory or absolute - -FLAGS - --no-input Don't interactively ask for any input from the user - -DESCRIPTION - Prints AIR script in human-readable Python-like representation. This representation cannot be executed and is intended - to be read by mere mortals. - -ALIASES - $ fluence air b -``` - -_See code: [src/commands/air/beautify.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/air/beautify.ts)_ - -## `fluence aqua` - -Compile aqua defined in 'compileAqua' property of fluence.yaml. If --input flag is used - then content of 'compileAqua' property in fluence.yaml will be ignored - -``` -USAGE - $ fluence aqua [-n ] [--no-input] [-w] [-o ] [--air | --js] [--import ...] [-i - ] [--const ...] [--log-level-compiler ] [--no-relay] [--no-xor] [--tracing] - [--no-empty-response] [--dry] - -FLAGS - -i, --input= Path to an aqua file or a directory that contains your aqua files - -n, --names= Comma-separated names of the configs from 'compileAqua' property of fluence.yaml to - compile. If not specified, all configs will be compiled - -o, --output= Path to the output directory. Must be relative to the current working directory or - absolute. Will be created if it doesn't exists - -w, --watch Watch aqua file or folder for changes and recompile - --air Generate .air file instead of .ts - --const=... Constants to be passed to the compiler - --dry Checks if compilation succeeded, without output - --import=... Path to a directory to import aqua files from. May be used several times - --js Generate .js file instead of .ts - --log-level-compiler= Set log level for the compiler. Must be one of: all, trace, debug, info, warn, - error, off - --no-empty-response Do not generate response call if there are no returned values - --no-input Don't interactively ask for any input from the user - --no-relay Do not generate a pass through the relay node - --no-xor Do not generate a wrapper that catches and displays errors - --tracing Compile aqua in tracing mode (for debugging purposes) - -DESCRIPTION - Compile aqua defined in 'compileAqua' property of fluence.yaml. If --input flag is used - then content of - 'compileAqua' property in fluence.yaml will be ignored - -EXAMPLES - $ fluence aqua -``` - -_See code: [src/commands/aqua.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/aqua.ts)_ - -## `fluence aqua imports` - -Returns a list of aqua imports that CLI produces - -``` -USAGE - $ fluence aqua imports [--no-input] - -FLAGS - --no-input Don't interactively ask for any input from the user - -DESCRIPTION - Returns a list of aqua imports that CLI produces -``` - -_See code: [src/commands/aqua/imports.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/aqua/imports.ts)_ - -## `fluence aqua json [INPUT] [OUTPUT]` - -Infers aqua types for an arbitrary json file, generates valid aqua code with a function call that returns an aqua object literal with the same structure as the json file. For valid generation please refer to aqua documentation https://fluence.dev/docs/aqua-book/language/ to learn about what kind of structures are valid in aqua language and what they translate into - -``` -USAGE - $ fluence aqua json [INPUT] [OUTPUT] [--no-input] [--f64] [--types ] - -ARGUMENTS - INPUT Path to json file - OUTPUT Path to the output dir - -FLAGS - --f64 Convert all numbers to f64. Useful for arrays objects that contain numbers of different types in them. - Without this flag, numbers will be converted to u64, i64 or f64 depending on their value - --no-input Don't interactively ask for any input from the user - --types= Experimental! Path to a file with custom types. Must be a list with objects that have 'name' and - 'properties'. 'properties' must be a list of all custom type properties - -DESCRIPTION - Infers aqua types for an arbitrary json file, generates valid aqua code with a function call that returns an aqua - object literal with the same structure as the json file. For valid generation please refer to aqua documentation - https://fluence.dev/docs/aqua-book/language/ to learn about what kind of structures are valid in aqua language and - what they translate into -``` - -_See code: [src/commands/aqua/json.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/aqua/json.ts)_ - -## `fluence aqua yml [INPUT] [OUTPUT]` - -Infers aqua types for an arbitrary yaml file, generates valid aqua code with a function call that returns an aqua object literal with the same structure as the yaml file. For valid generation please refer to aqua documentation https://fluence.dev/docs/aqua-book/language/ to learn about what kind of structures are valid in aqua language and what they translate into - -``` -USAGE - $ fluence aqua yml [INPUT] [OUTPUT] [--no-input] [--f64] [--types ] - -ARGUMENTS - INPUT Path to yaml file - OUTPUT Path to the output dir - -FLAGS - --f64 Convert all numbers to f64. Useful for arrays objects that contain numbers of different types in them. - Without this flag, numbers will be converted to u64, i64 or f64 depending on their value - --no-input Don't interactively ask for any input from the user - --types= Experimental! Path to a file with custom types. Must be a list with objects that have 'name' and - 'properties'. 'properties' must be a list of all custom type properties - -DESCRIPTION - Infers aqua types for an arbitrary yaml file, generates valid aqua code with a function call that returns an aqua - object literal with the same structure as the yaml file. For valid generation please refer to aqua documentation - https://fluence.dev/docs/aqua-book/language/ to learn about what kind of structures are valid in aqua language and - what they translate into - -ALIASES - $ fluence aqua yaml -``` - -_See code: [src/commands/aqua/yml.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/aqua/yml.ts)_ - -## `fluence autocomplete [SHELL]` - -Display autocomplete installation instructions. - -``` -USAGE - $ fluence autocomplete [SHELL] [-r] - -ARGUMENTS - SHELL (zsh|bash|powershell) Shell type - -FLAGS - -r, --refresh-cache Refresh cache (ignores displaying instructions) - -DESCRIPTION - Display autocomplete installation instructions. - -EXAMPLES - $ fluence autocomplete - - $ fluence autocomplete bash - - $ fluence autocomplete zsh - - $ fluence autocomplete powershell - - $ fluence autocomplete --refresh-cache -``` - -_See code: [@oclif/plugin-autocomplete](https://github.com/oclif/plugin-autocomplete/blob/v3.0.17/src/commands/autocomplete/index.ts)_ - -## `fluence build` - -Build all application services, described in fluence.yaml and generate aqua interfaces for them - -``` -USAGE - $ fluence build [--no-input] [--marine-build-args <--flag arg>] [--import ...] [--env ] - -FLAGS - --env= Fluence Environment to use when running the command - --import=... Path to a directory to import aqua files from. May be used several times - --marine-build-args=<--flag arg> Space separated `cargo build` flags and args to pass to marine build. - Overrides 'marineBuildArgs' property in fluence.yaml. Default: --release - --no-input Don't interactively ask for any input from the user - -DESCRIPTION - Build all application services, described in fluence.yaml and generate aqua interfaces for them - -EXAMPLES - $ fluence build -``` - -_See code: [src/commands/build.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/build.ts)_ - -## `fluence chain info` - -Show contract addresses for the fluence environment and accounts for the local environment - -``` -USAGE - $ fluence chain info [--no-input] [--env ] [--priv-key ] - -FLAGS - --env= Fluence Environment to use when running the command - --no-input Don't interactively ask for any input from the user - --priv-key= !WARNING! for debug purposes only. Passing private keys through flags is - unsecure. On local env - 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is used - by default when CLI is used in non-interactive mode - -DESCRIPTION - Show contract addresses for the fluence environment and accounts for the local environment -``` - -_See code: [src/commands/chain/info.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/chain/info.ts)_ - -## `fluence deal change-app [DEAL-ADDRESS] [NEW-APP-CID]` - -Change app id in the deal - -``` -USAGE - $ fluence deal change-app [DEAL-ADDRESS] [NEW-APP-CID] [--no-input] [--env ] - [--priv-key ] - -ARGUMENTS - DEAL-ADDRESS Deal address - NEW-APP-CID New app CID for the deal - -FLAGS - --env= Fluence Environment to use when running the command - --no-input Don't interactively ask for any input from the user - --priv-key= !WARNING! for debug purposes only. Passing private keys through flags is - unsecure. On local env - 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is used - by default when CLI is used in non-interactive mode - -DESCRIPTION - Change app id in the deal -``` - -_See code: [src/commands/deal/change-app.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/deal/change-app.ts)_ - -## `fluence deal create` - -Create your deal with the specified parameters - -``` -USAGE - $ fluence deal create --app-cid --collateral-per-worker --min-workers --target-workers - --max-workers-per-provider --price-per-cu-per-epoch --cu-count-per-worker - [--no-input] [--initial-balance ] [--effectors ] [--whitelist | --blacklist ] - [--protocol-version ] [--env ] [--priv-key ] - -FLAGS - --app-cid= (required) CID of the application that will be deployed - --blacklist= Comma-separated list of blacklisted providers - --collateral-per-worker= (required) Collateral per worker - --cu-count-per-worker= (required) Compute unit count per worker - --effectors= Comma-separated list of effector to be used in the deal - --env= Fluence Environment to use when running the command - --initial-balance= Initial balance - --max-workers-per-provider= (required) Max workers per provider - --min-workers= (required) Required workers to activate the deal - --no-input Don't interactively ask for any input from the user - --price-per-cu-per-epoch= (required) Price per CU per epoch - --priv-key= !WARNING! for debug purposes only. Passing private keys through flags is - unsecure. On local env - 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is used - by default when CLI is used in non-interactive mode - --protocol-version= Protocol version - --target-workers= (required) Max workers in the deal - --whitelist= Comma-separated list of whitelisted providers - -DESCRIPTION - Create your deal with the specified parameters -``` - -_See code: [src/commands/deal/create.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/deal/create.ts)_ - -## `fluence deal deposit [AMOUNT] [DEPLOYMENT-NAMES]` - -Deposit do the deal - -``` -USAGE - $ fluence deal deposit [AMOUNT] [DEPLOYMENT-NAMES] [--no-input] [--env ] - [--priv-key ] [--deal-ids ] - -ARGUMENTS - AMOUNT Amount of USDC tokens to deposit - DEPLOYMENT-NAMES Comma separated names of deployments. Can't be used together with --deal-ids flag - -FLAGS - --deal-ids= Comma-separated deal ids - --env= Fluence Environment to use when running the command - --no-input Don't interactively ask for any input from the user - --priv-key= !WARNING! for debug purposes only. Passing private keys through flags is - unsecure. On local env - 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is used - by default when CLI is used in non-interactive mode - -DESCRIPTION - Deposit do the deal -``` - -_See code: [src/commands/deal/deposit.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/deal/deposit.ts)_ - -## `fluence deal info [DEPLOYMENT-NAMES]` - -Get info about the deal - -``` -USAGE - $ fluence deal info [DEPLOYMENT-NAMES] [--no-input] [--env ] [--priv-key - ] [--deal-ids ] - -ARGUMENTS - DEPLOYMENT-NAMES Comma separated names of deployments. Can't be used together with --deal-ids flag - -FLAGS - --deal-ids= Comma-separated deal ids - --env= Fluence Environment to use when running the command - --no-input Don't interactively ask for any input from the user - --priv-key= !WARNING! for debug purposes only. Passing private keys through flags is - unsecure. On local env - 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is used - by default when CLI is used in non-interactive mode - -DESCRIPTION - Get info about the deal -``` - -_See code: [src/commands/deal/info.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/deal/info.ts)_ - -## `fluence deal logs [DEPLOYMENT-NAMES]` - -Get logs from deployed workers for deals listed in workers.yaml - -``` -USAGE - $ fluence deal logs [DEPLOYMENT-NAMES] [--no-input] [-k ] [--relay ] [--ttl - ] [--dial-timeout ] [--particle-id] [--env ] - [--off-aqua-logs] [--tracing] [--deal-ids ] [--spell ] - -ARGUMENTS - DEPLOYMENT-NAMES Comma separated names of deployments. Can't be used together with --deal-ids flag - -FLAGS - -k, --sk= Name of the secret key for js-client inside CLI to use. If not - specified, will use the default key for the project. If there is no - fluence project or there is no default key, will use user's default key - --deal-ids= Comma-separated deal ids - --dial-timeout= [default: 15000] Timeout for Fluence js-client to connect to relay peer - --env= Fluence Environment to use when running the command - --no-input Don't interactively ask for any input from the user - --off-aqua-logs Turns off logs from Console.print in aqua and from IPFS service - --particle-id Print particle ids when running Fluence js-client - --relay= Relay for Fluence js-client to connect to - --spell= [default: worker-spell] Spell name to get logs for - --tracing Compile aqua in tracing mode (for debugging purposes) - --ttl= [default: 15000] Particle Time To Live since 'now'. After that, - particle is expired and not processed. - -DESCRIPTION - Get logs from deployed workers for deals listed in workers.yaml - -EXAMPLES - $ fluence deal logs -``` - -_See code: [src/commands/deal/logs.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/deal/logs.ts)_ - -## `fluence deal stop [DEPLOYMENT-NAMES]` - -Stop the deal - -``` -USAGE - $ fluence deal stop [DEPLOYMENT-NAMES] [--no-input] [--env ] [--priv-key - ] [--deal-ids ] - -ARGUMENTS - DEPLOYMENT-NAMES Comma separated names of deployments. Can't be used together with --deal-ids flag - -FLAGS - --deal-ids= Comma-separated deal ids - --env= Fluence Environment to use when running the command - --no-input Don't interactively ask for any input from the user - --priv-key= !WARNING! for debug purposes only. Passing private keys through flags is - unsecure. On local env - 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is used - by default when CLI is used in non-interactive mode - -DESCRIPTION - Stop the deal -``` - -_See code: [src/commands/deal/stop.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/deal/stop.ts)_ - -## `fluence deal withdraw [AMOUNT] [DEPLOYMENT-NAMES]` - -Withdraw tokens from the deal - -``` -USAGE - $ fluence deal withdraw [AMOUNT] [DEPLOYMENT-NAMES] [--no-input] [--env ] - [--priv-key ] [--deal-ids ] - -ARGUMENTS - AMOUNT Amount of USDC tokens to withdraw - DEPLOYMENT-NAMES Comma separated names of deployments. Can't be used together with --deal-ids flag - -FLAGS - --deal-ids= Comma-separated deal ids - --env= Fluence Environment to use when running the command - --no-input Don't interactively ask for any input from the user - --priv-key= !WARNING! for debug purposes only. Passing private keys through flags is - unsecure. On local env - 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is used - by default when CLI is used in non-interactive mode - -DESCRIPTION - Withdraw tokens from the deal -``` - -_See code: [src/commands/deal/withdraw.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/deal/withdraw.ts)_ - -## `fluence deal workers-add [DEPLOYMENT-NAMES]` - -Add missing workers to the deal - -``` -USAGE - $ fluence deal workers-add [DEPLOYMENT-NAMES] [--no-input] [--env ] [--priv-key - ] [--deal-ids ] - -ARGUMENTS - DEPLOYMENT-NAMES Comma separated names of deployments. Can't be used together with --deal-ids flag - -FLAGS - --deal-ids= Comma-separated deal ids - --env= Fluence Environment to use when running the command - --no-input Don't interactively ask for any input from the user - --priv-key= !WARNING! for debug purposes only. Passing private keys through flags is - unsecure. On local env - 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is used - by default when CLI is used in non-interactive mode - -DESCRIPTION - Add missing workers to the deal - -ALIASES - $ fluence deal wa -``` - -_See code: [src/commands/deal/workers-add.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/deal/workers-add.ts)_ - -## `fluence deal workers-remove [WORKER-IDS]` - -Remove unit from the deal - -``` -USAGE - $ fluence deal workers-remove [WORKER-IDS] [--no-input] [--env ] [--priv-key - ] [--deal-id ] [--name ] - -ARGUMENTS - WORKER-IDS Comma-separated compute unit ids. You can get them using 'fluence deal info' command - -FLAGS - --deal-id= Deal id. You can get it using 'fluence deal info' command - --env= Fluence Environment to use when running the command - --name= Name of the deployment from workers.yaml - --no-input Don't interactively ask for any input from the user - --priv-key= !WARNING! for debug purposes only. Passing private keys through flags is - unsecure. On local env - 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is used - by default when CLI is used in non-interactive mode - -DESCRIPTION - Remove unit from the deal - -ALIASES - $ fluence deal wr -``` - -_See code: [src/commands/deal/workers-remove.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/deal/workers-remove.ts)_ - -## `fluence default env [ENV]` - -Switch default Fluence Environment - -``` -USAGE - $ fluence default env [ENV] [--no-input] - -ARGUMENTS - ENV Fluence Environment to use when running the command - -FLAGS - --no-input Don't interactively ask for any input from the user - -DESCRIPTION - Switch default Fluence Environment - -EXAMPLES - $ fluence default env -``` - -_See code: [src/commands/default/env.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/default/env.ts)_ - -## `fluence default peers [ENV]` - -Print default Fluence network peer addresses - -``` -USAGE - $ fluence default peers [ENV] [--no-input] - -ARGUMENTS - ENV Fluence Environment to use when running the command - -FLAGS - --no-input Don't interactively ask for any input from the user - -DESCRIPTION - Print default Fluence network peer addresses - -EXAMPLES - $ fluence default peers -``` - -_See code: [src/commands/default/peers.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/default/peers.ts)_ - -## `fluence delegator collateral-add [IDS]` - -Add FLT collateral to capacity commitment - -``` -USAGE - $ fluence delegator collateral-add [IDS] [--no-input] [--env ] [--priv-key - ] - -ARGUMENTS - IDS Comma separated capacity commitment IDs - -FLAGS - --env= Fluence Environment to use when running the command - --no-input Don't interactively ask for any input from the user - --priv-key= !WARNING! for debug purposes only. Passing private keys through flags is - unsecure. On local env - 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is used - by default when CLI is used in non-interactive mode - -DESCRIPTION - Add FLT collateral to capacity commitment - -ALIASES - $ fluence delegator ca -``` - -_See code: [src/commands/delegator/collateral-add.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/delegator/collateral-add.ts)_ - -## `fluence delegator collateral-withdraw [IDS]` - -Withdraw FLT collateral from capacity commitment - -``` -USAGE - $ fluence delegator collateral-withdraw [IDS] [--no-input] [--env ] [--priv-key ] - [--finish] - -ARGUMENTS - IDS Comma separated capacity commitment IDs - -FLAGS - --env= Fluence Environment to use when running the command - --finish Finish capacity commitment after collateral withdrawal - --no-input Don't interactively ask for any input from the user - --priv-key= !WARNING! for debug purposes only. Passing private keys through flags is - unsecure. On local env - 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is used - by default when CLI is used in non-interactive mode - -DESCRIPTION - Withdraw FLT collateral from capacity commitment - -ALIASES - $ fluence delegator cw -``` - -_See code: [src/commands/delegator/collateral-withdraw.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/delegator/collateral-withdraw.ts)_ - -## `fluence delegator reward-withdraw [IDS]` - -Withdraw FLT rewards from capacity commitment - -``` -USAGE - $ fluence delegator reward-withdraw [IDS] [--no-input] [--env ] [--priv-key - ] - -ARGUMENTS - IDS Comma separated capacity commitment IDs - -FLAGS - --env= Fluence Environment to use when running the command - --no-input Don't interactively ask for any input from the user - --priv-key= !WARNING! for debug purposes only. Passing private keys through flags is - unsecure. On local env - 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is used - by default when CLI is used in non-interactive mode - -DESCRIPTION - Withdraw FLT rewards from capacity commitment - -ALIASES - $ fluence delegator rw -``` - -_See code: [src/commands/delegator/reward-withdraw.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/delegator/reward-withdraw.ts)_ - -## `fluence dep install [PACKAGE-NAME | PACKAGE-NAME@VERSION]` - -Install aqua project dependencies (currently npm is used under the hood for managing aqua dependencies) - -``` -USAGE - $ fluence dep install [PACKAGE-NAME | PACKAGE-NAME@VERSION] [--no-input] - -ARGUMENTS - PACKAGE-NAME | PACKAGE-NAME@VERSION Valid argument for npm install command. If this argument is omitted all project - aqua dependencies will be installed and command will also make sure marine and - mrepl are installed - -FLAGS - --no-input Don't interactively ask for any input from the user - -DESCRIPTION - Install aqua project dependencies (currently npm is used under the hood for managing aqua dependencies) - -ALIASES - $ fluence dep i - -EXAMPLES - $ fluence dep install -``` - -_See code: [src/commands/dep/install.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/dep/install.ts)_ - -## `fluence dep reset` - -Reset all project dependencies to recommended versions - -``` -USAGE - $ fluence dep reset [--no-input] - -FLAGS - --no-input Don't interactively ask for any input from the user - -DESCRIPTION - Reset all project dependencies to recommended versions - -ALIASES - $ fluence dep r - -EXAMPLES - $ fluence dep reset -``` - -_See code: [src/commands/dep/reset.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/dep/reset.ts)_ - -## `fluence dep uninstall PACKAGE-NAME` - -Uninstall aqua project dependencies (currently npm is used under the hood for managing aqua dependencies) - -``` -USAGE - $ fluence dep uninstall PACKAGE-NAME [--no-input] - -ARGUMENTS - PACKAGE-NAME Aqua dependency name - -FLAGS - --no-input Don't interactively ask for any input from the user - -DESCRIPTION - Uninstall aqua project dependencies (currently npm is used under the hood for managing aqua dependencies) - -ALIASES - $ fluence dep un - -EXAMPLES - $ fluence dep uninstall -``` - -_See code: [src/commands/dep/uninstall.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/dep/uninstall.ts)_ - -## `fluence dep versions` - -Get versions of all cli dependencies, including aqua, marine, mrepl and internal - -``` -USAGE - $ fluence dep versions [--no-input] [--default] [--json] - -FLAGS - --default Display default npm and cargo dependencies and their versions for current CLI version. Default npm - dependencies are always available to be imported in Aqua - --json Output JSON - --no-input Don't interactively ask for any input from the user - -DESCRIPTION - Get versions of all cli dependencies, including aqua, marine, mrepl and internal - -ALIASES - $ fluence dep v - -EXAMPLES - $ fluence dep versions -``` - -_See code: [src/commands/dep/versions.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/dep/versions.ts)_ - -## `fluence deploy [DEPLOYMENT-NAMES]` - -Deploy according to 'deployments' property in fluence.yaml - -``` -USAGE - $ fluence deploy [DEPLOYMENT-NAMES] [--no-input] [--env ] [--priv-key - ] [--import ...] [--no-build] [--marine-build-args <--flag arg>] [-u] [--peer-ids ] - -ARGUMENTS - DEPLOYMENT-NAMES Comma separated names of deployments. Can't be used together with --deal-ids flag - -FLAGS - -u, --update Update your previous deployment - --env= Fluence Environment to use when running the command - --import=... Path to a directory to import aqua files from. May be used several - times - --marine-build-args=<--flag arg> Space separated `cargo build` flags and args to pass to marine build. - Overrides 'marineBuildArgs' property in fluence.yaml. Default: - --release - --no-build Don't build the project before running the command - --no-input Don't interactively ask for any input from the user - --peer-ids= Comma separated list of peer ids to deploy to. Creates 1 worker for - each peer with 'cuCountPerWorker' number of compute units - --priv-key= !WARNING! for debug purposes only. Passing private keys through flags - is unsecure. On local env - 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is - used by default when CLI is used in non-interactive mode - -DESCRIPTION - Deploy according to 'deployments' property in fluence.yaml - -EXAMPLES - $ fluence deploy -``` - -_See code: [src/commands/deploy.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/deploy.ts)_ - -## `fluence help [COMMAND]` - -Display help for fluence. - -``` -USAGE - $ fluence help [COMMAND...] [-n] - -ARGUMENTS - COMMAND... Command to show help for. - -FLAGS - -n, --nested-commands Include all nested commands in the output. - -DESCRIPTION - Display help for fluence. -``` - -_See code: [@oclif/plugin-help](https://github.com/oclif/plugin-help/blob/v6.0.21/src/commands/help.ts)_ - -## `fluence init [PATH]` - -Initialize fluence project - -``` -USAGE - $ fluence init [PATH] [--no-input] [-t ] [--env ] [--noxes - ] - -ARGUMENTS - PATH Project path - -FLAGS - -t, --template= Template to use for the project. One of: quickstart, minimal, ts, js - --env= Fluence Environment to use when running the command - --no-input Don't interactively ask for any input from the user - --noxes= Number of Compute Peers to generate when a new provider.yaml is created - -DESCRIPTION - Initialize fluence project - -EXAMPLES - $ fluence init -``` - -_See code: [src/commands/init.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/init.ts)_ - -## `fluence key default [NAME]` - -Set default key-pair for user or project - -``` -USAGE - $ fluence key default [NAME] [--no-input] [--user] - -ARGUMENTS - NAME Key-pair name - -FLAGS - --no-input Don't interactively ask for any input from the user - --user Set default key-pair for current user instead of current project - -DESCRIPTION - Set default key-pair for user or project - -EXAMPLES - $ fluence key default -``` - -_See code: [src/commands/key/default.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/key/default.ts)_ - -## `fluence key new [NAME]` - -Generate key-pair and store it in user-secrets.yaml or project-secrets.yaml - -``` -USAGE - $ fluence key new [NAME] [--no-input] [--user] [--default] - -ARGUMENTS - NAME Key-pair name - -FLAGS - --default Set new key-pair as default for current project or user - --no-input Don't interactively ask for any input from the user - --user Generate key-pair for current user instead of generating key-pair for current project - -DESCRIPTION - Generate key-pair and store it in user-secrets.yaml or project-secrets.yaml - -EXAMPLES - $ fluence key new -``` - -_See code: [src/commands/key/new.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/key/new.ts)_ - -## `fluence key remove [NAME]` - -Remove key-pair from user-secrets.yaml or project-secrets.yaml - -``` -USAGE - $ fluence key remove [NAME] [--no-input] [--user] - -ARGUMENTS - NAME Key-pair name - -FLAGS - --no-input Don't interactively ask for any input from the user - --user Remove key-pair from current user instead of removing key-pair from current project - -DESCRIPTION - Remove key-pair from user-secrets.yaml or project-secrets.yaml - -EXAMPLES - $ fluence key remove -``` - -_See code: [src/commands/key/remove.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/key/remove.ts)_ - -## `fluence local down` - -Stop and remove currently running docker-compose.yaml using docker compose - -``` -USAGE - $ fluence local down [--no-input] [-v] [--flags <--flag arg>] - -FLAGS - -v, --volumes Remove named volumes declared in the "volumes" section of the Compose file and anonymous - volumes attached to containers - --flags=<--flag arg> Space separated flags to pass to `docker compose` - --no-input Don't interactively ask for any input from the user - -DESCRIPTION - Stop and remove currently running docker-compose.yaml using docker compose - -EXAMPLES - $ fluence local down -``` - -_See code: [src/commands/local/down.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/local/down.ts)_ - -## `fluence local init` - -Init docker-compose.yaml according to provider.yaml - -``` -USAGE - $ fluence local init [--no-input] [--env ] [--priv-key ] - -FLAGS - --env= Fluence Environment to use when running the command - --no-input Don't interactively ask for any input from the user - --priv-key= !WARNING! for debug purposes only. Passing private keys through flags is - unsecure. On local env - 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is used - by default when CLI is used in non-interactive mode - -DESCRIPTION - Init docker-compose.yaml according to provider.yaml - -EXAMPLES - $ fluence local init -``` - -_See code: [src/commands/local/init.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/local/init.ts)_ - -## `fluence local logs` - -Display docker-compose.yaml logs - -``` -USAGE - $ fluence local logs [--no-input] [--flags <--flag arg>] - -FLAGS - --flags=<--flag arg> Space separated flags to pass to `docker compose` - --no-input Don't interactively ask for any input from the user - -DESCRIPTION - Display docker-compose.yaml logs - -EXAMPLES - $ fluence local logs -``` - -_See code: [src/commands/local/logs.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/local/logs.ts)_ - -## `fluence local ps` - -List containers using docker compose - -``` -USAGE - $ fluence local ps [--no-input] [--flags <--flag arg>] - -FLAGS - --flags=<--flag arg> Space separated flags to pass to `docker compose` - --no-input Don't interactively ask for any input from the user - -DESCRIPTION - List containers using docker compose - -EXAMPLES - $ fluence local ps -``` - -_See code: [src/commands/local/ps.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/local/ps.ts)_ - -## `fluence local up` - -Run docker-compose.yaml using docker compose and set up provider using all the offers from the 'offers' section in provider.yaml config using default wallet key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 - -``` -USAGE - $ fluence local up [--no-input] [--noxes ] [--timeout ] [--priv-key ] - [--quiet-pull] [-d] [--build] [--flags <--flag arg>] [-r] [--no-wait] [--no-set-up] - -FLAGS - -d, --detach Detached mode: Run containers in the background - -r, --no-reset Don't reset docker-compose.yaml to default, don't remove volumes and previous local - deployments - --build Build images before starting containers - --flags=<--flag arg> Space separated flags to pass to `docker compose` - --no-input Don't interactively ask for any input from the user - --no-set-up Don't set up provider, offer, commitments and deposit collateral, so there will be no - active offer on the network after command is finished - --no-wait Don't wait for services to be running|healthy - --noxes= Number of Compute Peers to generate when a new provider.yaml is created - --priv-key= !WARNING! for debug purposes only. Passing private keys through flags is unsecure. On - local env 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is used by - default when CLI is used in non-interactive mode - --quiet-pull Pull without printing progress information - --timeout= [default: 120] Timeout in seconds for attempting to register local network on local - peers - -DESCRIPTION - Run docker-compose.yaml using docker compose and set up provider using all the offers from the 'offers' section in - provider.yaml config using default wallet key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 - -EXAMPLES - $ fluence local up -``` - -_See code: [src/commands/local/up.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/local/up.ts)_ - -## `fluence module add [PATH | URL]` - -Add module to service.yaml - -``` -USAGE - $ fluence module add [PATH | URL] [--no-input] [--name ] [--service ] - -ARGUMENTS - PATH | URL Path to a module or url to .tar.gz archive - -FLAGS - --name= Override module name - --no-input Don't interactively ask for any input from the user - --service= Service name from fluence.yaml or path to the service config or directory that contains - service.yaml - -DESCRIPTION - Add module to service.yaml - -EXAMPLES - $ fluence module add -``` - -_See code: [src/commands/module/add.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/module/add.ts)_ - -## `fluence module build [PATH]` - -Build module - -``` -USAGE - $ fluence module build [PATH] [--no-input] [--marine-build-args <--flag arg>] - -ARGUMENTS - PATH Path to a module - -FLAGS - --marine-build-args=<--flag arg> Space separated `cargo build` flags and args to pass to marine build. Overrides - 'marineBuildArgs' property in fluence.yaml. Default: --release - --no-input Don't interactively ask for any input from the user - -DESCRIPTION - Build module - -EXAMPLES - $ fluence module build -``` - -_See code: [src/commands/module/build.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/module/build.ts)_ - -## `fluence module new [NAME]` - -Create new marine module template - -``` -USAGE - $ fluence module new [NAME] [--no-input] [--path ] [--service ] - -ARGUMENTS - NAME Module name - -FLAGS - --no-input Don't interactively ask for any input from the user - --path= Path to module dir (default: src/modules) - --service= Name or relative path to the service to add the created module to - -DESCRIPTION - Create new marine module template - -EXAMPLES - $ fluence module new -``` - -_See code: [src/commands/module/new.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/module/new.ts)_ - -## `fluence module pack [PATH]` - -Pack module into tar.gz archive - -``` -USAGE - $ fluence module pack [PATH] [--no-input] [--marine-build-args <--flag arg>] [-d ] [-b ] - -ARGUMENTS - PATH Path to a module - -FLAGS - -b, --binding-crate= Path to a directory with rust binding crate - -d, --destination= Path to a directory where you want archive to be saved. Default: current - directory - --marine-build-args=<--flag arg> Space separated `cargo build` flags and args to pass to marine build. Overrides - 'marineBuildArgs' property in fluence.yaml. Default: --release - --no-input Don't interactively ask for any input from the user - -DESCRIPTION - Pack module into tar.gz archive - -EXAMPLES - $ fluence module pack -``` - -_See code: [src/commands/module/pack.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/module/pack.ts)_ - -## `fluence module remove [NAME | PATH | URL]` - -Remove module from service.yaml - -``` -USAGE - $ fluence module remove [NAME | PATH | URL] [--no-input] [--service ] - -ARGUMENTS - NAME | PATH | URL Module name from service.yaml, path to a module or url to .tar.gz archive - -FLAGS - --no-input Don't interactively ask for any input from the user - --service= Service name from fluence.yaml or path to the service directory - -DESCRIPTION - Remove module from service.yaml - -EXAMPLES - $ fluence module remove -``` - -_See code: [src/commands/module/remove.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/module/remove.ts)_ - -## `fluence provider cc-activate` - -Add FLT collateral to capacity commitment to activate it - -``` -USAGE - $ fluence provider cc-activate [--no-input] [--env ] [--priv-key ] - [--nox-names | --cc-ids ] [--offers ] - -FLAGS - --cc-ids= Comma separated capacity commitment IDs - --env= Fluence Environment to use when running the command - --no-input Don't interactively ask for any input from the user - --nox-names= Comma-separated names of noxes from provider.yaml. To use all of your - noxes: --nox-names all - --offers= Comma-separated list of offer names. To use all of your offers: --offers - all - --priv-key= !WARNING! for debug purposes only. Passing private keys through flags is - unsecure. On local env - 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is used - by default when CLI is used in non-interactive mode - -DESCRIPTION - Add FLT collateral to capacity commitment to activate it - -ALIASES - $ fluence provider ca -``` - -_See code: [src/commands/provider/cc-activate.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/provider/cc-activate.ts)_ - -## `fluence provider cc-create` - -Create Capacity commitment - -``` -USAGE - $ fluence provider cc-create [--no-input] [--env ] [--priv-key ] - [--nox-names ] [--offers ] - -FLAGS - --env= Fluence Environment to use when running the command - --no-input Don't interactively ask for any input from the user - --nox-names= Comma-separated names of noxes from provider.yaml. To use all of your - noxes: --nox-names all - --offers= Comma-separated list of offer names. To use all of your offers: --offers - all - --priv-key= !WARNING! for debug purposes only. Passing private keys through flags is - unsecure. On local env - 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is used - by default when CLI is used in non-interactive mode - -DESCRIPTION - Create Capacity commitment - -ALIASES - $ fluence provider cc -``` - -_See code: [src/commands/provider/cc-create.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/provider/cc-create.ts)_ - -## `fluence provider cc-finish` - -Move resources from deals, withdraw FLT collateral from capacity commitments, remove compute units from capacity commitments and finish capacity commitments - -``` -USAGE - $ fluence provider cc-finish [--no-input] [--nox-names | --cc-ids ] [--offers ] - [--env ] [--priv-key ] - -FLAGS - --cc-ids= Comma separated capacity commitment IDs - --env= Fluence Environment to use when running the command - --no-input Don't interactively ask for any input from the user - --nox-names= Comma-separated names of noxes from provider.yaml. To use all of your - noxes: --nox-names all - --offers= Comma-separated list of offer names. To use all of your offers: --offers - all - --priv-key= !WARNING! for debug purposes only. Passing private keys through flags is - unsecure. On local env - 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is used - by default when CLI is used in non-interactive mode - -DESCRIPTION - Move resources from deals, withdraw FLT collateral from capacity commitments, remove compute units from capacity - commitments and finish capacity commitments - -ALIASES - $ fluence provider ccf -``` - -_See code: [src/commands/provider/cc-finish.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/provider/cc-finish.ts)_ - -## `fluence provider cc-info` - -Get info about capacity commitments - -``` -USAGE - $ fluence provider cc-info [--no-input] [--env ] [--priv-key ] - [--nox-names | --cc-ids ] [--offers ] [--json] - -FLAGS - --cc-ids= Comma separated capacity commitment IDs - --env= Fluence Environment to use when running the command - --json Output JSON - --no-input Don't interactively ask for any input from the user - --nox-names= Comma-separated names of noxes from provider.yaml. To use all of your - noxes: --nox-names all - --offers= Comma-separated list of offer names. To use all of your offers: --offers - all - --priv-key= !WARNING! for debug purposes only. Passing private keys through flags is - unsecure. On local env - 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is used - by default when CLI is used in non-interactive mode - -DESCRIPTION - Get info about capacity commitments - -ALIASES - $ fluence provider ci -``` - -_See code: [src/commands/provider/cc-info.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/provider/cc-info.ts)_ - -## `fluence provider cc-remove` - -Remove Capacity commitment. You can remove it only BEFORE you activated it by depositing collateral - -``` -USAGE - $ fluence provider cc-remove [--no-input] [--env ] [--priv-key ] - [--nox-names | --cc-ids ] [--offers ] - -FLAGS - --cc-ids= Comma separated capacity commitment IDs - --env= Fluence Environment to use when running the command - --no-input Don't interactively ask for any input from the user - --nox-names= Comma-separated names of noxes from provider.yaml. To use all of your - noxes: --nox-names all - --offers= Comma-separated list of offer names. To use all of your offers: --offers - all - --priv-key= !WARNING! for debug purposes only. Passing private keys through flags is - unsecure. On local env - 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is used - by default when CLI is used in non-interactive mode - -DESCRIPTION - Remove Capacity commitment. You can remove it only BEFORE you activated it by depositing collateral - -ALIASES - $ fluence provider cr -``` - -_See code: [src/commands/provider/cc-remove.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/provider/cc-remove.ts)_ - -## `fluence provider cc-rewards-withdraw` - -Withdraw FLT rewards from capacity commitments - -``` -USAGE - $ fluence provider cc-rewards-withdraw [--no-input] [--nox-names | --cc-ids ] [--offers ] - [--env ] [--priv-key ] - -FLAGS - --cc-ids= Comma separated capacity commitment IDs - --env= Fluence Environment to use when running the command - --no-input Don't interactively ask for any input from the user - --nox-names= Comma-separated names of noxes from provider.yaml. To use all of your - noxes: --nox-names all - --offers= Comma-separated list of offer names. To use all of your offers: --offers - all - --priv-key= !WARNING! for debug purposes only. Passing private keys through flags is - unsecure. On local env - 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is used - by default when CLI is used in non-interactive mode - -DESCRIPTION - Withdraw FLT rewards from capacity commitments - -ALIASES - $ fluence provider crw -``` - -_See code: [src/commands/provider/cc-rewards-withdraw.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/provider/cc-rewards-withdraw.ts)_ - -## `fluence provider deal-exit` - -Exit from deal - -``` -USAGE - $ fluence provider deal-exit [--no-input] [--env ] [--priv-key ] - [--deal-ids ] [--all] - -FLAGS - --all To use all deal ids that indexer is aware of for your provider address - --deal-ids= Comma-separated deal ids - --env= Fluence Environment to use when running the command - --no-input Don't interactively ask for any input from the user - --priv-key= !WARNING! for debug purposes only. Passing private keys through flags is - unsecure. On local env - 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is used - by default when CLI is used in non-interactive mode - -DESCRIPTION - Exit from deal - -ALIASES - $ fluence provider de -``` - -_See code: [src/commands/provider/deal-exit.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/provider/deal-exit.ts)_ - -## `fluence provider deal-list` - -List all deals - -``` -USAGE - $ fluence provider deal-list [--no-input] [--env ] [--priv-key ] - -FLAGS - --env= Fluence Environment to use when running the command - --no-input Don't interactively ask for any input from the user - --priv-key= !WARNING! for debug purposes only. Passing private keys through flags is - unsecure. On local env - 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is used - by default when CLI is used in non-interactive mode - -DESCRIPTION - List all deals - -ALIASES - $ fluence provider dl -``` - -_See code: [src/commands/provider/deal-list.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/provider/deal-list.ts)_ - -## `fluence provider deal-rewards-info [DEAL-ADDRESS] [ON-CHAIN-WORKER-ID]` - -Deal rewards info - -``` -USAGE - $ fluence provider deal-rewards-info [DEAL-ADDRESS] [ON-CHAIN-WORKER-ID] [--no-input] [--env ] [--priv-key ] - -ARGUMENTS - DEAL-ADDRESS Deal address - ON-CHAIN-WORKER-ID On-chain worker id - -FLAGS - --env= Fluence Environment to use when running the command - --no-input Don't interactively ask for any input from the user - --priv-key= !WARNING! for debug purposes only. Passing private keys through flags is - unsecure. On local env - 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is used - by default when CLI is used in non-interactive mode - -DESCRIPTION - Deal rewards info - -ALIASES - $ fluence provider dri -``` - -_See code: [src/commands/provider/deal-rewards-info.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/provider/deal-rewards-info.ts)_ - -## `fluence provider deal-rewards-withdraw` - -Withdraw USDC rewards from deals - -``` -USAGE - $ fluence provider deal-rewards-withdraw [--no-input] [--env ] [--priv-key ] - [--deal-ids ] - -FLAGS - --deal-ids= Comma-separated deal ids - --env= Fluence Environment to use when running the command - --no-input Don't interactively ask for any input from the user - --priv-key= !WARNING! for debug purposes only. Passing private keys through flags is - unsecure. On local env - 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is used - by default when CLI is used in non-interactive mode - -DESCRIPTION - Withdraw USDC rewards from deals - -ALIASES - $ fluence provider drw -``` - -_See code: [src/commands/provider/deal-rewards-withdraw.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/provider/deal-rewards-withdraw.ts)_ - -## `fluence provider gen` - -Generate Config.toml files according to provider.yaml and secrets according to provider-secrets.yaml - -``` -USAGE - $ fluence provider gen [--no-input] [--env ] [--priv-key ] - [--reset-nox-secrets] [--no-withdraw] - -FLAGS - --env= Fluence Environment to use when running the command - --no-input Don't interactively ask for any input from the user - --no-withdraw Is used only when --reset-nox-secrets flag is present. Will not withdraw - tokens from noxes (if you don't need it or it fails for some reason) - --priv-key= !WARNING! for debug purposes only. Passing private keys through flags is - unsecure. On local env - 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is used - by default when CLI is used in non-interactive mode - --reset-nox-secrets Withdraw remaining tokens from your noxes, backup nox secrets from - .fluence/provider-secrets.yaml and .fluence/secrets (if they exist) to - .fluence/backups and generate new ones - -DESCRIPTION - Generate Config.toml files according to provider.yaml and secrets according to provider-secrets.yaml - -EXAMPLES - $ fluence provider gen -``` - -_See code: [src/commands/provider/gen.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/provider/gen.ts)_ - -## `fluence provider info` - -Print nox signing wallets and peer ids - -``` -USAGE - $ fluence provider info [--no-input] [--env ] [--priv-key ] - [--nox-names ] [--json] [--address
] - -FLAGS - --address=
Provider address - --env= Fluence Environment to use when running the command - --json Output JSON - --no-input Don't interactively ask for any input from the user - --nox-names= Comma-separated names of noxes from provider.yaml. To use all of your - noxes: --nox-names all - --priv-key= !WARNING! for debug purposes only. Passing private keys through flags is - unsecure. On local env - 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is used - by default when CLI is used in non-interactive mode - -DESCRIPTION - Print nox signing wallets and peer ids - -ALIASES - $ fluence provider i -``` - -_See code: [src/commands/provider/info.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/provider/info.ts)_ - -## `fluence provider init` - -Init provider config. Creates a provider.yaml file - -``` -USAGE - $ fluence provider init [--no-input] [--noxes ] [--env ] [--priv-key - ] [--no-vm] - -FLAGS - --env= Fluence Environment to use when running the command - --no-input Don't interactively ask for any input from the user - --no-vm Generate provider.yaml without vm configuration - --noxes= Number of Compute Peers to generate when a new provider.yaml is created - --priv-key= !WARNING! for debug purposes only. Passing private keys through flags is - unsecure. On local env - 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is used - by default when CLI is used in non-interactive mode - -DESCRIPTION - Init provider config. Creates a provider.yaml file -``` - -_See code: [src/commands/provider/init.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/provider/init.ts)_ - -## `fluence provider offer-create` - -Create offers. You have to be registered as a provider to do that - -``` -USAGE - $ fluence provider offer-create [--no-input] [--env ] [--priv-key ] - [--offers ] - -FLAGS - --env= Fluence Environment to use when running the command - --no-input Don't interactively ask for any input from the user - --offers= Comma-separated list of offer names. To use all of your offers: --offers - all - --priv-key= !WARNING! for debug purposes only. Passing private keys through flags is - unsecure. On local env - 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is used - by default when CLI is used in non-interactive mode - -DESCRIPTION - Create offers. You have to be registered as a provider to do that - -ALIASES - $ fluence provider oc -``` - -_See code: [src/commands/provider/offer-create.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/provider/offer-create.ts)_ - -## `fluence provider offer-info` - -Get info about offers - -``` -USAGE - $ fluence provider offer-info [--no-input] [--offers | --offer-ids ] [--env ] [--priv-key ] - -FLAGS - --env= Fluence Environment to use when running the command - --no-input Don't interactively ask for any input from the user - --offer-ids= Comma-separated list of offer ids. Can't be used together with --offers - flag - --offers= Comma-separated list of offer names. To use all of your offers: --offers - all - --priv-key= !WARNING! for debug purposes only. Passing private keys through flags is - unsecure. On local env - 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is used - by default when CLI is used in non-interactive mode - -DESCRIPTION - Get info about offers - -ALIASES - $ fluence provider oi -``` - -_See code: [src/commands/provider/offer-info.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/provider/offer-info.ts)_ - -## `fluence provider offer-remove` - -Remove offers - -``` -USAGE - $ fluence provider offer-remove [--no-input] [--offers | --offer-ids ] [--env ] [--priv-key ] - -FLAGS - --env= Fluence Environment to use when running the command - --no-input Don't interactively ask for any input from the user - --offer-ids= Comma-separated list of offer ids. Can't be used together with --offers - flag - --offers= Comma-separated list of offer names. To use all of your offers: --offers - all - --priv-key= !WARNING! for debug purposes only. Passing private keys through flags is - unsecure. On local env - 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is used - by default when CLI is used in non-interactive mode - -DESCRIPTION - Remove offers - -ALIASES - $ fluence provider or -``` - -_See code: [src/commands/provider/offer-remove.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/provider/offer-remove.ts)_ - -## `fluence provider offer-update` - -Update offers - -``` -USAGE - $ fluence provider offer-update [--no-input] [--offers ] [--env ] - [--priv-key ] - -FLAGS - --env= Fluence Environment to use when running the command - --no-input Don't interactively ask for any input from the user - --offers= Comma-separated list of offer names. To use all of your offers: --offers - all - --priv-key= !WARNING! for debug purposes only. Passing private keys through flags is - unsecure. On local env - 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is used - by default when CLI is used in non-interactive mode - -DESCRIPTION - Update offers - -ALIASES - $ fluence provider ou -``` - -_See code: [src/commands/provider/offer-update.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/provider/offer-update.ts)_ - -## `fluence provider register` - -Register as a provider - -``` -USAGE - $ fluence provider register [--no-input] [--env ] [--priv-key ] - -FLAGS - --env= Fluence Environment to use when running the command - --no-input Don't interactively ask for any input from the user - --priv-key= !WARNING! for debug purposes only. Passing private keys through flags is - unsecure. On local env - 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is used - by default when CLI is used in non-interactive mode - -DESCRIPTION - Register as a provider - -ALIASES - $ fluence provider r -``` - -_See code: [src/commands/provider/register.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/provider/register.ts)_ - -## `fluence provider tokens-distribute` - -Distribute FLT tokens to noxes - -``` -USAGE - $ fluence provider tokens-distribute [--no-input] [--env ] [--priv-key ] - [--nox-names ] [--offers ] [--amount ] - -FLAGS - --amount= Amount of FLT tokens to distribute to noxes - --env= Fluence Environment to use when running the command - --no-input Don't interactively ask for any input from the user - --nox-names= Comma-separated names of noxes from provider.yaml. To use all of your - noxes: --nox-names all - --offers= Comma-separated list of offer names. To use all of your offers: --offers - all - --priv-key= !WARNING! for debug purposes only. Passing private keys through flags is - unsecure. On local env - 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is used - by default when CLI is used in non-interactive mode - -DESCRIPTION - Distribute FLT tokens to noxes - -ALIASES - $ fluence provider td -``` - -_See code: [src/commands/provider/tokens-distribute.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/provider/tokens-distribute.ts)_ - -## `fluence provider tokens-withdraw` - -Withdraw FLT tokens from noxes - -``` -USAGE - $ fluence provider tokens-withdraw [--no-input] [--env ] [--priv-key ] - [--nox-names ] [--amount ] - -FLAGS - --amount= Amount of FLT tokens to withdraw from noxes. Use --amount max to withdraw - maximum possible amount - --env= Fluence Environment to use when running the command - --no-input Don't interactively ask for any input from the user - --nox-names= Comma-separated names of noxes from provider.yaml. To use all of your - noxes: --nox-names all - --priv-key= !WARNING! for debug purposes only. Passing private keys through flags is - unsecure. On local env - 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is used - by default when CLI is used in non-interactive mode - -DESCRIPTION - Withdraw FLT tokens from noxes - -ALIASES - $ fluence provider tw -``` - -_See code: [src/commands/provider/tokens-withdraw.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/provider/tokens-withdraw.ts)_ - -## `fluence provider update` - -Update provider info - -``` -USAGE - $ fluence provider update [--no-input] [--env ] [--priv-key ] - -FLAGS - --env= Fluence Environment to use when running the command - --no-input Don't interactively ask for any input from the user - --priv-key= !WARNING! for debug purposes only. Passing private keys through flags is - unsecure. On local env - 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is used - by default when CLI is used in non-interactive mode - -DESCRIPTION - Update provider info - -ALIASES - $ fluence provider u -``` - -_See code: [src/commands/provider/update.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/provider/update.ts)_ - -## `fluence run` - -Run the first aqua function CLI is able to find and compile among all aqua files specified in 'compileAqua' property of fluence.yaml file. If --input flag is used - then content of 'compileAqua' property in fluence.yaml will be ignored - -``` -USAGE - $ fluence run [--no-input] [--data ] [--data-path ] [--quiet] [-f ] - [--print-air | -b] [--off-aqua-logs] [-k ] [--relay ] [--ttl ] [--dial-timeout - ] [--particle-id] [--env ] [--import ...] [-i ] - [--const ...] [--log-level-compiler ] [--no-relay] [--no-xor] [--tracing] [--no-empty-response] - -FLAGS - -b, --print-beautified-air Prints beautified AIR code instead of function execution - -f, --func= Function call. Example: funcName("stringArg") - -i, --input= Path to an aqua file or a directory that contains your aqua files - -k, --sk= Name of the secret key for js-client inside CLI to use. If not - specified, will use the default key for the project. If there is no - fluence project or there is no default key, will use user's default key - --const=... Constants to be passed to the compiler - --data= JSON in { [argumentName]: argumentValue } format. You can call a - function using these argument names like this: -f - 'myFunc(argumentName)'. Arguments in this flag override arguments in - the --data-path flag - --data-path= Path to a JSON file in { [argumentName]: argumentValue } format. You - can call a function using these argument names like this: -f - 'myFunc(argumentName)'. Arguments in this flag can be overridden using - --data flag - --dial-timeout= [default: 15000] Timeout for Fluence js-client to connect to relay peer - --env= Fluence Environment to use when running the command - --import=... Path to a directory to import aqua files from. May be used several - times - --log-level-compiler= Set log level for the compiler. Must be one of: all, trace, debug, - info, warn, error, off - --no-empty-response Do not generate response call if there are no returned values - --no-input Don't interactively ask for any input from the user - --no-relay Do not generate a pass through the relay node - --no-xor Do not generate a wrapper that catches and displays errors - --off-aqua-logs Turns off logs from Console.print in aqua and from IPFS service - --particle-id Print particle ids when running Fluence js-client - --print-air Prints generated AIR code instead of function execution - --quiet Print only execution result. Overrides all --log-level-* flags - --relay= Relay for Fluence js-client to connect to - --tracing Compile aqua in tracing mode (for debugging purposes) - --ttl= [default: 15000] Particle Time To Live since 'now'. After that, - particle is expired and not processed. - -DESCRIPTION - Run the first aqua function CLI is able to find and compile among all aqua files specified in 'compileAqua' property - of fluence.yaml file. If --input flag is used - then content of 'compileAqua' property in fluence.yaml will be ignored - -EXAMPLES - $ fluence run -f 'funcName("stringArg")' -``` - -_See code: [src/commands/run.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/run.ts)_ - -## `fluence service add [PATH | URL]` - -Add service to fluence.yaml - -``` -USAGE - $ fluence service add [PATH | URL] [--no-input] [--name ] [--marine-build-args <--flag arg>] - -ARGUMENTS - PATH | URL Path to a service or url to .tar.gz archive - -FLAGS - --marine-build-args=<--flag arg> Space separated `cargo build` flags and args to pass to marine build. Overrides - 'marineBuildArgs' property in fluence.yaml. Default: --release - --name= Override service name (must start with a lowercase letter and contain only letters, - numbers, and underscores) - --no-input Don't interactively ask for any input from the user - -DESCRIPTION - Add service to fluence.yaml - -EXAMPLES - $ fluence service add -``` - -_See code: [src/commands/service/add.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/service/add.ts)_ - -## `fluence service new [NAME]` - -Create new marine service template - -``` -USAGE - $ fluence service new [NAME] [--no-input] [--path ] - -ARGUMENTS - NAME Unique service name (must start with a lowercase letter and contain only letters, numbers, and underscores) - -FLAGS - --no-input Don't interactively ask for any input from the user - --path= Path to services dir (default: src/services) - -DESCRIPTION - Create new marine service template - -EXAMPLES - $ fluence service new -``` - -_See code: [src/commands/service/new.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/service/new.ts)_ - -## `fluence service remove [NAME | PATH | URL]` - -Remove service from fluence.yaml services property and from all of the workers - -``` -USAGE - $ fluence service remove [NAME | PATH | URL] [--no-input] - -ARGUMENTS - NAME | PATH | URL Service name from fluence.yaml, path to a service or url to .tar.gz archive - -FLAGS - --no-input Don't interactively ask for any input from the user - -DESCRIPTION - Remove service from fluence.yaml services property and from all of the workers - -EXAMPLES - $ fluence service remove -``` - -_See code: [src/commands/service/remove.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/service/remove.ts)_ - -## `fluence service repl [NAME | PATH | URL]` - -Open service inside repl (downloads and builds modules if necessary) - -``` -USAGE - $ fluence service repl [NAME | PATH | URL] [--no-input] [--marine-build-args <--flag arg>] - -ARGUMENTS - NAME | PATH | URL Service name from fluence.yaml, path to a service or url to .tar.gz archive - -FLAGS - --marine-build-args=<--flag arg> Space separated `cargo build` flags and args to pass to marine build. Overrides - 'marineBuildArgs' property in fluence.yaml. Default: --release - --no-input Don't interactively ask for any input from the user - -DESCRIPTION - Open service inside repl (downloads and builds modules if necessary) - -EXAMPLES - $ fluence service repl -``` - -_See code: [src/commands/service/repl.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/service/repl.ts)_ - -## `fluence spell build [SPELL-NAMES]` - -Check spells aqua is able to compile without any errors - -``` -USAGE - $ fluence spell build [SPELL-NAMES] [--no-input] [--import ...] - -ARGUMENTS - SPELL-NAMES Comma separated names of spells to build. Example: "spell1,spell2" (by default all spells from 'spells' - property in fluence.yaml will be built) - -FLAGS - --import=... Path to a directory to import aqua files from. May be used several times - --no-input Don't interactively ask for any input from the user - -DESCRIPTION - Check spells aqua is able to compile without any errors - -EXAMPLES - $ fluence spell build -``` - -_See code: [src/commands/spell/build.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/spell/build.ts)_ - -## `fluence spell new [NAME]` - -Create a new spell template - -``` -USAGE - $ fluence spell new [NAME] [--no-input] [--path ] - -ARGUMENTS - NAME Spell name - -FLAGS - --no-input Don't interactively ask for any input from the user - --path= Path to spells dir (default: src/spells) - -DESCRIPTION - Create a new spell template - -EXAMPLES - $ fluence spell new -``` - -_See code: [src/commands/spell/new.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/spell/new.ts)_ - -## `fluence update [CHANNEL]` - -update the fluence CLI - -``` -USAGE - $ fluence update [CHANNEL] [-a] [--force] [-i | -v ] - -FLAGS - -a, --available See available versions. - -i, --interactive Interactively select version to install. This is ignored if a channel is provided. - -v, --version= Install a specific version. - --force Force a re-download of the requested version. - -DESCRIPTION - update the fluence CLI - -EXAMPLES - Update to the stable channel: - - $ fluence update stable - - Update to a specific version: - - $ fluence update --version 1.0.0 - - Interactively select version: - - $ fluence update --interactive - - See available versions: - - $ fluence update --available -``` - -_See code: [@oclif/plugin-update](https://github.com/oclif/plugin-update/blob/v4.2.11/src/commands/update.ts)_ - diff --git a/cli/docs/configs/README.md b/cli/docs/configs/README.md deleted file mode 100644 index fc7c06008..000000000 --- a/cli/docs/configs/README.md +++ /dev/null @@ -1,45 +0,0 @@ -# Fluence CLI Configs - -## [fluence.yaml](./fluence.md) - -Defines Fluence Project, most importantly - what exactly you want to deploy and how. You can use `fluence init` command to generate a template for new Fluence project - -## [provider.yaml](./provider.md) - -Defines config used for provider set up - -## [provider-secrets.yaml](./provider-secrets.md) - -Defines secrets config used for provider set up - -## [module.yaml](./module.md) - -Defines Marine Module. You can use `fluence module new` command to generate a template for new module - -## [service.yaml](./service.md) - -Defines a [Marine service](https://fluence.dev/docs/build/concepts/#services), most importantly the modules that the service consists of. You can use `fluence service new` command to generate a template for new service - -## [spell.yaml](./spell.md) - -Defines a spell. You can use `fluence spell new` command to generate a template for new spell - -## [workers.yaml](./workers.md) - -A result of app deployment. This file is created automatically after successful deployment using `fluence workers deploy` command - -## [config.yaml](./config.md) - -Defines global config for Fluence CLI - -## [env.yaml](./env.md) - -Defines user project preferences - -## [docker-compose.yaml](./docker-compose.md) - -The Compose file is a YAML file defining a multi-containers based application. - -## [provider-artifacts.yaml](./provider-artifacts.md) - -Defines artifacts created by the provider \ No newline at end of file diff --git a/cli/docs/configs/config.md b/cli/docs/configs/config.md deleted file mode 100644 index 22cd2be61..000000000 --- a/cli/docs/configs/config.md +++ /dev/null @@ -1,14 +0,0 @@ -# config.yaml - -Defines global config for Fluence CLI - -## Properties - -| Property | Type | Required | Description | -|------------------------|---------|----------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `countlyConsent` | boolean | **Yes** | Weather you consent to send usage data to Countly | -| `defaultSecretKeyName` | string | **Yes** | Secret key with this name will be used by default by js-client inside CLI to run Aqua code | -| `version` | integer | **Yes** | | -| `docsInConfigs` | boolean | No | Whether to include commented-out documented config examples in the configs generated with the CLI | -| `lastCheckForUpdates` | string | No | DEPRECATED. It's currently advised to install CLI without using npm (See README.md: https://github.com/fluencelabs/cli?tab=readme-ov-file#installation-and-usage). Last time when Fluence CLI checked for updates. Updates are checked daily unless this field is set to 'disabled' | - diff --git a/cli/docs/configs/docker-compose.md b/cli/docs/configs/docker-compose.md deleted file mode 100644 index 7e58743cf..000000000 --- a/cli/docs/configs/docker-compose.md +++ /dev/null @@ -1,42 +0,0 @@ -# Compose Specification - -The Compose file is a YAML file defining a multi-containers based application. - -## Properties - -| Property | Type | Required | Description | -|------------|---------------------|----------|---------------------------------------------------------------------| -| `configs` | [object](#configs) | No | | -| `include` | | No | compose sub-projects to be included. | -| `name` | string | No | define the Compose project name, until user defines one explicitly. | -| `networks` | [object](#networks) | No | | -| `secrets` | [object](#secrets) | No | | -| `services` | [object](#services) | No | | -| `version` | string | No | declared for backward compatibility, ignored. | -| `volumes` | [object](#volumes) | No | | - -## configs - -| Property | Type | Required | Description | -|----------|------|----------|-------------| - -## networks - -| Property | Type | Required | Description | -|----------|------|----------|-------------| - -## secrets - -| Property | Type | Required | Description | -|----------|------|----------|-------------| - -## services - -| Property | Type | Required | Description | -|----------|------|----------|-------------| - -## volumes - -| Property | Type | Required | Description | -|----------|------|----------|-------------| - diff --git a/cli/docs/configs/env.md b/cli/docs/configs/env.md deleted file mode 100644 index 2335d30e7..000000000 --- a/cli/docs/configs/env.md +++ /dev/null @@ -1,29 +0,0 @@ -# env.yaml - -Defines user project preferences - -## Properties - -| Property | Type | Required | Description | -|-----------------|-----------------------|----------|------------------------------------------------------------------------------------------------| -| `version` | integer | **Yes** | | -| `blockScoutUrl` | string | No | BlockScout URL to use | -| `chainId` | number | No | Chain ID to use | -| `deployment` | [object](#deployment) | No | Deployed contract address overrides | -| `fluenceEnv` | string | No | Fluence environment to connect to Possible values are: `testnet`, `mainnet`, `stage`, `local`. | -| `relays` | string[] | No | List of custom relay multiaddresses to use when connecting to Fluence network | -| `rpcUrl` | string | No | RPC URL to use | -| `subgraphUrl` | string | No | Subgraph URL to use | - -## deployment - -Deployed contract address overrides - -### Properties - -| Property | Type | Required | Description | -|--------------|--------|----------|-------------| -| `diamond` | string | No | | -| `multicall3` | string | No | | -| `usdc` | string | No | | - diff --git a/cli/docs/configs/fluence.md b/cli/docs/configs/fluence.md deleted file mode 100644 index d9af60f7b..000000000 --- a/cli/docs/configs/fluence.md +++ /dev/null @@ -1,257 +0,0 @@ -# fluence.yaml - -Defines Fluence Project, most importantly - what exactly you want to deploy and how. You can use `fluence init` command to generate a template for new Fluence project - -## Properties - -| Property | Type | Required | Description | -|------------------------|-----------------------------|----------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `aquaDependencies` | [object](#aquadependencies) | **Yes** | A map of npm aqua dependency versions | -| `version` | integer | **Yes** | | -| `aquaImports` | string[] | No | A list of path to be considered by aqua compiler to be used as imports. First dependency in the list has the highest priority. Priority of imports is considered in the following order: imports from --import flags, imports from aquaImports property in fluence.yaml, project's .fluence/aqua dir, npm dependencies from fluence.yaml, npm dependencies from user's .fluence/config.yaml, npm dependencies recommended by fluence | -| `cliVersion` | string | No | The version of the Fluence CLI that is compatible with this project. Set this to enforce a particular set of versions of all fluence components | -| `compileAqua` | [object](#compileaqua) | No | A map of aqua files to compile | -| `customFluenceEnv` | [object](#customfluenceenv) | No | Custom Fluence environment to use when connecting to Fluence network | -| `defaultSecretKeyName` | string | No | Secret key with this name will be used by default by js-client inside CLI to run Aqua code | -| `deployments` | [object](#deployments) | No | A map with deployment names as keys and deployments as values | -| `hosts` | [object](#hosts) | No | A map of objects with worker names as keys, each object defines a list of peer IDs to host the worker on. Intended to be used by providers to deploy directly without using the blockchain | -| `ipfsAddr` | string | No | IPFS multiaddress to use when uploading workers with 'fluence deploy'. Default: /dns4/ipfs.fluence.dev/tcp/5001 or /ip4/127.0.0.1/tcp/5001 if using local local env (for 'workers deploy' IPFS address provided by relay that you are connected to is used) | -| `marineBuildArgs` | string | No | Space separated `cargo build` flags and args to pass to marine build. Can be overridden using --marine-build-args flag Default: --release | -| `marineVersion` | string | No | Marine version | -| `mreplVersion` | string | No | Mrepl version | -| `relaysPath` | string, array, or null | No | Single or multiple paths to the directories where you want relays.json file to be generated. Must be relative to the project root dir. This file contains a list of relays to use when connecting to Fluence network and depends on the default environment that you use in your project | -| `rustToolchain` | string | No | Rust toolchain to use for building the project. By default nightly-2024-06-10 is used | -| `services` | [object](#services) | No | A map with service names as keys and Service configs as values. You can have any number of services listed here as long as service name keys start with a lowercase letter and contain only letters numbers and underscores. You can use `fluence service add` command to add a service to this config | -| `spells` | [object](#spells) | No | A map with spell names as keys and spell configs as values | - -## aquaDependencies - -A map of npm aqua dependency versions - -### Properties - -| Property | Type | Required | Description | -|----------------------------|--------|----------|---------------------------------------------------------------------------------------------------------------------------------| -| `npm-aqua-dependency-name` | string | No | Valid npm dependency version specification (check out https://docs.npmjs.com/cli/v10/configuring-npm/package-json#dependencies) | - -## compileAqua - -A map of aqua files to compile - -### Properties - -| Property | Type | Required | Description | -|--------------------|-----------------------------|----------|-------------| -| `aqua-config-name` | [object](#aqua-config-name) | No | | - -### aqua-config-name - -#### Properties - -| Property | Type | Required | Description | -|-------------------|----------------------|----------|-------------------------------------------------------------------------------------------------------------------------| -| `input` | string | **Yes** | Relative path to the aqua file or directory with aqua files | -| `output` | string | **Yes** | Relative path to the output directory | -| `target` | string | **Yes** | Compilation target Possible values are: `ts`, `js`, `air`. | -| `constants` | [object](#constants) | No | A list of constants to pass to the compiler. Constant name must be uppercase | -| `logLevel` | string | No | Log level for the compiler. Default: info Possible values are: `all`, `trace`, `debug`, `info`, `warn`, `error`, `off`. | -| `noEmptyResponse` | boolean | No | Do not generate response call if there are no returned values. Default: false | -| `noRelay` | boolean | No | Do not generate a pass through the relay node. Default: false | -| `noXor` | boolean | No | Do not generate a wrapper that catches and displays errors. Default: false | -| `tracing` | boolean | No | Compile aqua in tracing mode (for debugging purposes). Default: false | - -#### constants - -A list of constants to pass to the compiler. Constant name must be uppercase - -##### Properties - -| Property | Type | Required | Description | -|-----------------|----------------------------|----------|-------------| -| `SOME_CONSTANT` | string, number, or boolean | No | | - -## customFluenceEnv - -Custom Fluence environment to use when connecting to Fluence network - -### Properties - -| Property | Type | Required | Description | -|----------------|----------|----------|--------------------------------------------------------------------------------------------------------------------------------------------------------| -| `contractsEnv` | string | **Yes** | Contracts environment to use for this fluence network to sign contracts on the blockchain Possible values are: `testnet`, `mainnet`, `stage`, `local`. | -| `relays` | string[] | **Yes** | List of custom relay multiaddresses to use when connecting to Fluence network | - -## deployments - -A map with deployment names as keys and deployments as values - -### Properties - -| Property | Type | Required | Description | -|------------------|---------------------------|----------|-------------------| -| `deploymentName` | [object](#deploymentname) | No | Deployment config | - -### deploymentName - -Deployment config - -#### Properties - -| Property | Type | Required | Description | -|-------------------------|----------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `blacklist` | string[] | No | Blacklist of providers to deploy to. Can't be used together with whitelist | -| `computeUnits` | integer | No | DEPRECATED. USE cuCountPerWorker INSTEAD. Number of compute units you require. 1 compute unit = 2GB. Currently the only allowed value is 1. This will change in the future. Default: 1 | -| `cuCountPerWorker` | integer | No | Number of compute units per worker. Default: 1 | -| `effectors` | string[] | No | Effector CIDs to be used in the deal. Must be a valid CID | -| `initialBalance` | string | No | Initial balance after deploy in USDC. Default: targetWorkers * pricePerCuPerEpoch * minDealDepositedEpochs. For local environment: enough for deal to be active for 1 day | -| `maxWorkersPerProvider` | integer | No | Max workers per provider. Matches target workers by default | -| `minWorkers` | integer | No | Required workers to activate the deal. Matches target workers by default | -| `pricePerCuPerEpoch` | string | No | Price per compute unit per epoch in USDC | -| `protocolVersion` | integer | No | Protocol version. Default: 1 | -| `services` | string[] | No | An array of service names to include in this worker. Service names must be listed in fluence.yaml | -| `spells` | string[] | No | An array of spell names to include in this worker. Spell names must be listed in fluence.yaml | -| `targetWorkers` | integer | No | Max workers in the deal | -| `whitelist` | string[] | No | Whitelist of providers to deploy to. Can't be used together with blacklist | - -## hosts - -A map of objects with worker names as keys, each object defines a list of peer IDs to host the worker on. Intended to be used by providers to deploy directly without using the blockchain - -### Properties - -| Property | Type | Required | Description | -|--------------|-----------------------|----------|-------------------| -| `workerName` | [object](#workername) | No | Deployment config | - -### workerName - -Deployment config - -#### Properties - -| Property | Type | Required | Description | -|------------|----------|----------|---------------------------------------------------------------------------------------------------| -| `peerIds` | string[] | No | An array of peer IDs to deploy on | -| `services` | string[] | No | An array of service names to include in this worker. Service names must be listed in fluence.yaml | -| `spells` | string[] | No | An array of spell names to include in this worker. Spell names must be listed in fluence.yaml | - -## services - -A map with service names as keys and Service configs as values. You can have any number of services listed here as long as service name keys start with a lowercase letter and contain only letters numbers and underscores. You can use `fluence service add` command to add a service to this config - -### Properties - -| Property | Type | Required | Description | -|----------------|-------------------------|----------|-------------------------------------------------------------------| -| `Service_name` | [object](#service_name) | No | Service config. Defines where the service is and how to deploy it | - -### Service_name - -Service config. Defines where the service is and how to deploy it - -#### Properties - -| Property | Type | Required | Description | -|--------------------|----------------------------|----------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `get` | string | **Yes** | Path to service directory or URL to the tar.gz archive with the service | -| `overrideModules` | [object](#overridemodules) | No | A map of modules to override | -| `totalMemoryLimit` | string | No | Memory limit for all service modules. If you specify this property please make sure it's at least `2 MiB * numberOfModulesInTheService`. In repl default is the entire compute unit memory: 2GB. When deploying service as part of the worker default is: computeUnits * 2GB / (amount of services in the worker). Format: [number][whitespace?][B] where ? is an optional field and B is one of the following: kB, KB, kiB, KiB, KIB, mB, MB, miB, MiB, MIB, gB, GB, giB, GiB, GIB | - -#### overrideModules - -A map of modules to override - -##### Properties - -| Property | Type | Required | Description | -|---------------|------------------------|----------|---------------------------------| -| `Module_name` | [object](#module_name) | No | Overrides for the module config | - -##### Module_name - -Overrides for the module config - -###### Properties - -| Property | Type | Required | Description | -|-----------|--------------------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `effects` | [object](#effects) | No | Effects configuration. Only providers can allow and control effector modules by changing the nox configuration. Properties in this config are ignored when you deploy your code | -| `repl` | [object](#repl) | No | REPL configuration. Properties in this config are ignored when you deploy your code | - -###### effects - -Effects configuration. Only providers can allow and control effector modules by changing the nox configuration. Properties in this config are ignored when you deploy your code - -**Properties** - -| Property | Type | Required | Description | -|------------|---------------------|----------|-----------------------------------------------------------------------------------------------| -| `binaries` | [object](#binaries) | No | A map of binary executable files that module is allowed to call. Example: curl: /usr/bin/curl | - -**binaries** - -A map of binary executable files that module is allowed to call. Example: curl: /usr/bin/curl - -**Properties** - -| Property | Type | Required | Description | -|---------------|--------|----------|------------------| -| `binary-name` | string | No | Path to a binary | - -###### repl - -REPL configuration. Properties in this config are ignored when you deploy your code - -**Properties** - -| Property | Type | Required | Description | -|-----------------|---------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------| -| `loggerEnabled` | boolean | No | Set true to allow module to use the Marine SDK logger | -| `loggingMask` | number | No | manages the logging targets, that are described in detail here: https://fluence.dev/docs/marine-book/marine-rust-sdk/developing/logging#using-target-map | - -## spells - -A map with spell names as keys and spell configs as values - -### Properties - -| Property | Type | Required | Description | -|--------------|-----------------------|----------|--------------| -| `Spell_name` | [object](#spell_name) | No | Spell config | - -### Spell_name - -Spell config - -#### Properties - -| Property | Type | Required | Description | -|----------------|---------------------|----------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `get` | string | **Yes** | Path to spell | -| `aquaFilePath` | string | No | Path to Aqua file which contains an Aqua function that you want to use as a spell | -| `clock` | [object](#clock) | No | Trigger the spell execution periodically. If you want to disable this property by overriding it in fluence.yaml - pass empty config for it like this: `clock: {}` | -| `function` | string | No | Name of the Aqua function that you want to use as a spell | -| `initArgs` | [object](#initargs) | No | A map of Aqua function arguments names as keys and arguments values as values. They will be passed to the spell function and will be stored in the key-value storage for this particular spell. | -| `version` | integer | No | | - -#### clock - -Trigger the spell execution periodically. If you want to disable this property by overriding it in fluence.yaml - pass empty config for it like this: `clock: {}` - -##### Properties - -| Property | Type | Required | Description | -|------------------|---------|----------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `endDelaySec` | integer | No | How long to wait before the last execution in seconds. If this property or `endTimestamp` not specified, periodic execution will never end. WARNING! Currently your computer's clock is used to determine a final timestamp that is sent to the server. If it is in the past at the moment of spell creation - the spell will never be executed. This property conflicts with `endTimestamp`. You can specify only one of them | -| `endTimestamp` | string | No | An ISO timestamp when the periodic execution should end. If this property or `endDelaySec` not specified, periodic execution will never end. If it is in the past at the moment of spell creation on Rust peer - the spell will never be executed | -| `periodSec` | integer | No | How often the spell will be executed. If set to 0, the spell will be executed only once. If this value not provided at all - the spell will never be executed | -| `startDelaySec` | integer | No | How long to wait before the first execution in seconds. If this property or `startTimestamp` not specified, periodic execution will start immediately. WARNING! Currently your computer's clock is used to determine a final timestamp that is sent to the server. This property conflicts with `startTimestamp`. You can specify only one of them | -| `startTimestamp` | string | No | An ISO timestamp when the periodic execution should start. If this property or `startDelaySec` not specified, periodic execution will start immediately. If it is set to 0 - the spell will never be executed | - -#### initArgs - -A map of Aqua function arguments names as keys and arguments values as values. They will be passed to the spell function and will be stored in the key-value storage for this particular spell. - -| Property | Type | Required | Description | -|----------|------|----------|-------------| - diff --git a/cli/docs/configs/module.md b/cli/docs/configs/module.md deleted file mode 100644 index 26e70810a..000000000 --- a/cli/docs/configs/module.md +++ /dev/null @@ -1,58 +0,0 @@ -# module.yaml - -Defines Marine Module. You can use `fluence module new` command to generate a template for new module - -## Properties - -| Property | Type | Required | Description | -|--------------------|-----------------------------|----------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `name` | string | **Yes** | "name" property from the Cargo.toml (for module type "rust") or name of the precompiled .wasm file (for module type "compiled") | -| `version` | integer | **Yes** | | -| `cid` | string | No | CID of the module when it was packed | -| `effects` | [object](#effects) | No | Effects configuration. Only providers can allow and control effector modules by changing the nox configuration. Properties in this config are ignored when you deploy your code | -| `repl` | [object](#repl) | No | REPL configuration. Properties in this config are ignored when you deploy your code | -| `rustBindingCrate` | [object](#rustbindingcrate) | No | Interface crate that can be used with this module | -| `type` | string | No | Default: compiled. Module type "rust" is for the source code written in rust which can be compiled into a Marine module. Module type "compiled" is for the precompiled modules. Possible values are: `rust`, `compiled`. | - -## effects - -Effects configuration. Only providers can allow and control effector modules by changing the nox configuration. Properties in this config are ignored when you deploy your code - -### Properties - -| Property | Type | Required | Description | -|------------|---------------------|----------|-----------------------------------------------------------------------------------------------| -| `binaries` | [object](#binaries) | No | A map of binary executable files that module is allowed to call. Example: curl: /usr/bin/curl | - -### binaries - -A map of binary executable files that module is allowed to call. Example: curl: /usr/bin/curl - -#### Properties - -| Property | Type | Required | Description | -|---------------|--------|----------|------------------| -| `binary-name` | string | No | Path to a binary | - -## repl - -REPL configuration. Properties in this config are ignored when you deploy your code - -### Properties - -| Property | Type | Required | Description | -|-----------------|---------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------| -| `loggerEnabled` | boolean | No | Set true to allow module to use the Marine SDK logger | -| `loggingMask` | number | No | manages the logging targets, that are described in detail here: https://fluence.dev/docs/marine-book/marine-rust-sdk/developing/logging#using-target-map | - -## rustBindingCrate - -Interface crate that can be used with this module - -### Properties - -| Property | Type | Required | Description | -|-----------|--------|----------|-------------| -| `name` | string | **Yes** | | -| `version` | string | **Yes** | | - diff --git a/cli/docs/configs/provider-artifacts.md b/cli/docs/configs/provider-artifacts.md deleted file mode 100644 index 9e0caf48f..000000000 --- a/cli/docs/configs/provider-artifacts.md +++ /dev/null @@ -1,120 +0,0 @@ -# provider-artifacts.yaml - -Defines artifacts created by the provider - -## Properties - -| Property | Type | Required | Description | -|-----------|-------------------|----------|----------------| -| `offers` | [object](#offers) | **Yes** | Created offers | -| `version` | integer | **Yes** | Config version | - -## offers - -Created offers - -### Properties - -| Property | Type | Required | Description | -|-----------|--------------------|----------|----------------| -| `custom` | [object](#custom) | No | Created offers | -| `local` | [object](#local) | No | Created offers | -| `mainnet` | [object](#mainnet) | No | Created offers | -| `stage` | [object](#stage) | No | Created offers | -| `testnet` | [object](#testnet) | No | Created offers | - -### custom - -Created offers - -#### Properties - -| Property | Type | Required | Description | -|-----------|--------------------|----------|-------------| -| `noxName` | [object](#noxname) | No | | - -#### noxName - -##### Properties - -| Property | Type | Required | Description | -|-------------------|--------|----------|------------------| -| `id` | string | **Yes** | Offer id | -| `providerAddress` | string | **Yes** | Provider address | - -### local - -Created offers - -#### Properties - -| Property | Type | Required | Description | -|-----------|--------------------|----------|-------------| -| `noxName` | [object](#noxname) | No | | - -#### noxName - -##### Properties - -| Property | Type | Required | Description | -|-------------------|--------|----------|------------------| -| `id` | string | **Yes** | Offer id | -| `providerAddress` | string | **Yes** | Provider address | - -### mainnet - -Created offers - -#### Properties - -| Property | Type | Required | Description | -|-----------|--------------------|----------|-------------| -| `noxName` | [object](#noxname) | No | | - -#### noxName - -##### Properties - -| Property | Type | Required | Description | -|-------------------|--------|----------|------------------| -| `id` | string | **Yes** | Offer id | -| `providerAddress` | string | **Yes** | Provider address | - -### stage - -Created offers - -#### Properties - -| Property | Type | Required | Description | -|-----------|--------------------|----------|-------------| -| `noxName` | [object](#noxname) | No | | - -#### noxName - -##### Properties - -| Property | Type | Required | Description | -|-------------------|--------|----------|------------------| -| `id` | string | **Yes** | Offer id | -| `providerAddress` | string | **Yes** | Provider address | - -### testnet - -Created offers - -#### Properties - -| Property | Type | Required | Description | -|-----------|--------------------|----------|-------------| -| `noxName` | [object](#noxname) | No | | - -#### noxName - -##### Properties - -| Property | Type | Required | Description | -|-------------------|--------|----------|------------------| -| `id` | string | **Yes** | Offer id | -| `providerAddress` | string | **Yes** | Provider address | - diff --git a/cli/docs/configs/provider-secrets.md b/cli/docs/configs/provider-secrets.md deleted file mode 100644 index b8039f152..000000000 --- a/cli/docs/configs/provider-secrets.md +++ /dev/null @@ -1,32 +0,0 @@ -# provider-secrets.yaml - -Defines secrets config used for provider set up - -## Properties - -| Property | Type | Required | Description | -|-----------|------------------|----------|-------------------------------| -| `noxes` | [object](#noxes) | **Yes** | Secret keys for noxes by name | -| `version` | integer | **Yes** | Config version | - -## noxes - -Secret keys for noxes by name - -### Properties - -| Property | Type | Required | Description | -|-----------|--------------------|----------|----------------------------------------------------------------------------------| -| `noxName` | [object](#noxname) | No | Secret keys for noxes. You can put it near provider config and populate it in CI | - -### noxName - -Secret keys for noxes. You can put it near provider config and populate it in CI - -#### Properties - -| Property | Type | Required | Description | -|-----------------|--------|----------|-----------------------------------------------------------| -| `networkKey` | string | **Yes** | Network key for the nox | -| `signingWallet` | string | **Yes** | Signing wallet for built-in decider system service in nox | - diff --git a/cli/docs/configs/provider.md b/cli/docs/configs/provider.md deleted file mode 100644 index 5e450a093..000000000 --- a/cli/docs/configs/provider.md +++ /dev/null @@ -1,584 +0,0 @@ -# provider.yaml - -Defines config used for provider set up - -## Properties - -| Property | Type | Required | Description | -|-----------------------|--------------------------------|----------|-------------------------------------------------------------------------------------------------| -| `capacityCommitments` | [object](#capacitycommitments) | **Yes** | A map with nox names as keys and capacity commitments as values | -| `computePeers` | [object](#computepeers) | **Yes** | A map with compute peer names as keys and compute peers as values | -| `offers` | [object](#offers) | **Yes** | A map with offer names as keys and offers as values | -| `providerName` | string | **Yes** | Provider name. Must not be empty | -| `version` | integer | **Yes** | Config version | -| `ccp` | [object](#ccp) | No | Configuration to pass to the Capacity Commitment Prover | -| `nox` | [object](#nox) | No | Configuration to pass to the nox compute peer. Config.toml files are generated from this config | - -## capacityCommitments - -A map with nox names as keys and capacity commitments as values - -### Properties - -| Property | Type | Required | Description | -|-----------|--------------------|----------|-------------------------------| -| `noxName` | [object](#noxname) | No | Defines a capacity commitment | - -### noxName - -Defines a capacity commitment - -#### Properties - -| Property | Type | Required | Description | -|----------------|--------|----------|-------------------------------------------------------------------------------| -| `duration` | string | **Yes** | Duration of the commitment in human-readable format. Example: 1 months 1 days | -| `stakerReward` | number | **Yes** | Staker reward in percent | -| `delegator` | string | No | Delegator address | - -## ccp - -Configuration to pass to the Capacity Commitment Prover - -### Properties - -| Property | Type | Required | Description | -|----------------------|-------------------------------|----------|-------------------------------------------------------------------------------------------------| -| `logs` | [object](#logs) | No | Logs configuration | -| `prometheusEndpoint` | [object](#prometheusendpoint) | No | Prometheus endpoint configuration | -| `rawConfig` | string | No | Raw TOML config string to parse and merge with the rest of the config. Has the highest priority | -| `rpcEndpoint` | [object](#rpcendpoint) | No | RPC endpoint configuration | -| `state` | [object](#state) | No | State configuration | - -### logs - -Logs configuration - -#### Properties - -| Property | Type | Required | Description | -|------------------|---------|----------|---------------------------------| -| `logLevel` | string | No | Log level. Default: debug | -| `reportHashrate` | boolean | No | Report hashrate. Default: false | - -### prometheusEndpoint - -Prometheus endpoint configuration - -#### Properties - -| Property | Type | Required | Description | -|----------|---------|----------|-----------------------------------| -| `host` | string | No | Prometheus host. Default: 0.0.0.0 | -| `port` | integer | No | Prometheus port. Default: 9384 | - -### rpcEndpoint - -RPC endpoint configuration - -#### Properties - -| Property | Type | Required | Description | -|--------------------|-----------|----------|----------------------------| -| `host` | string | No | RPC host. Default: 0.0.0.0 | -| `port` | integer | No | RPC port. Default: 9389 | -| `utilityThreadIds` | integer[] | No | Utility thread IDs | - -### state - -State configuration - -#### Properties - -| Property | Type | Required | Description | -|----------|--------|----------|------------------------------------------| -| `path` | string | No | Path to the state file. Default: ./state | - -## computePeers - -A map with compute peer names as keys and compute peers as values - -### Properties - -| Property | Type | Required | Description | -|---------------|------------------------|----------|------------------------| -| `ComputePeer` | [object](#computepeer) | No | Defines a compute peer | - -### ComputePeer - -Defines a compute peer - -#### Properties - -| Property | Type | Required | Description | -|----------------|----------------|----------|-------------------------------------------------------------------------------------------------| -| `computeUnits` | integer | **Yes** | How many compute units should nox have. Default: 32 (each compute unit requires 2GB of RAM) | -| `ccp` | [object](#ccp) | No | Configuration to pass to the Capacity Commitment Prover | -| `nox` | [object](#nox) | No | Configuration to pass to the nox compute peer. Config.toml files are generated from this config | - -#### ccp - -Configuration to pass to the Capacity Commitment Prover - -##### Properties - -| Property | Type | Required | Description | -|----------------------|-------------------------------|----------|-------------------------------------------------------------------------------------------------| -| `logs` | [object](#logs) | No | Logs configuration | -| `prometheusEndpoint` | [object](#prometheusendpoint) | No | Prometheus endpoint configuration | -| `rawConfig` | string | No | Raw TOML config string to parse and merge with the rest of the config. Has the highest priority | -| `rpcEndpoint` | [object](#rpcendpoint) | No | RPC endpoint configuration | -| `state` | [object](#state) | No | State configuration | - -##### logs - -Logs configuration - -###### Properties - -| Property | Type | Required | Description | -|------------------|---------|----------|---------------------------------| -| `logLevel` | string | No | Log level. Default: debug | -| `reportHashrate` | boolean | No | Report hashrate. Default: false | - -##### prometheusEndpoint - -Prometheus endpoint configuration - -###### Properties - -| Property | Type | Required | Description | -|----------|---------|----------|-----------------------------------| -| `host` | string | No | Prometheus host. Default: 0.0.0.0 | -| `port` | integer | No | Prometheus port. Default: 9384 | - -##### rpcEndpoint - -RPC endpoint configuration - -###### Properties - -| Property | Type | Required | Description | -|--------------------|-----------|----------|----------------------------| -| `host` | string | No | RPC host. Default: 0.0.0.0 | -| `port` | integer | No | RPC port. Default: 9389 | -| `utilityThreadIds` | integer[] | No | Utility thread IDs | - -##### state - -State configuration - -###### Properties - -| Property | Type | Required | Description | -|----------|--------|----------|------------------------------------------| -| `path` | string | No | Path to the state file. Default: ./state | - -#### nox - -Configuration to pass to the nox compute peer. Config.toml files are generated from this config - -##### Properties - -| Property | Type | Required | Description | -|--------------------------|---------------------------|----------|-------------------------------------------------------------------------------------------------------------------| -| `aquavmPoolSize` | integer | No | Number of aquavm instances to run. Default: 2 | -| `bootstrapNodes` | string[] | No | List of bootstrap nodes. Default: all addresses for the selected env | -| `ccp` | [object](#ccp) | No | For advanced users. CCP config | -| `chain` | [object](#chain) | No | Chain config | -| `cpusRange` | string | No | Range of CPU cores to use. Default: 1-32 | -| `effectors` | [object](#effectors) | No | Effectors to allow on the nox | -| `externalMultiaddresses` | string[] | No | List of external multiaddresses | -| `httpPort` | integer | No | Both host and container HTTP port to use. Default: 918 (on local network ports will be generated by default) | -| `ipfs` | [object](#ipfs) | No | IPFS config | -| `listenIp` | string | No | IP to listen on | -| `metrics` | [object](#metrics) | No | Metrics configuration | -| `rawConfig` | string | No | Raw TOML config string to parse and merge with the rest of the config. Has the highest priority | -| `systemCpuCount` | integer | No | Number of CPU cores to allocate for the Nox itself. Default: 1 | -| `systemServices` | [object](#systemservices) | No | System services to run by default. aquaIpfs and decider are enabled by default | -| `tcpPort` | integer | No | Both host and container TCP port to use. Default: 977 (on local network ports will be generated by default) | -| `vm` | [object](#vm) | No | VM Configuration | -| `websocketPort` | integer | No | Both host and container WebSocket port to use. Default: 999 (on local network ports will be generated by default) | - -##### ccp - -For advanced users. CCP config - -###### Properties - -| Property | Type | Required | Description | -|-------------------|--------|----------|-------------------------------------------------------------------------------------------------------------| -| `ccpEndpoint` | string | No | CCP endpoint. Default comes from top-level ccp config: http://{ccp.rpcEndpoint.host}:{ccp.rpcEndpoint.port} | -| `proofPollPeriod` | string | No | Proof poll period. Default: 60 seconds | - -##### chain - -Chain config - -###### Properties - -| Property | Type | Required | Description | -|----------------------|---------|----------|---------------------------------------------------| -| `ccContract` | string | No | Capacity commitment contract address (deprecated) | -| `coreContract` | string | No | Core contract address (deprecated) | -| `dealSyncStartBlock` | string | No | Start block (deprecated) | -| `defaultBaseFee` | number | No | Default base fee | -| `defaultPriorityFee` | number | No | Default priority fee | -| `diamondContract` | string | No | Diamond contract address | -| `httpEndpoint` | string | No | HTTP endpoint of the chain | -| `marketContract` | string | No | Market contract address (deprecated) | -| `networkId` | integer | No | Network ID | -| `walletPrivateKey` | string | No | Nox wallet private key. Is generated by default | -| `wsEndpoint` | string | No | WebSocket endpoint of the chain | - -##### effectors - -Effectors to allow on the nox - -###### Properties - -| Property | Type | Required | Description | -|----------------|-------------------------|----------|------------------------| -| `effectorName` | [object](#effectorname) | No | Effector configuration | - -###### effectorName - -Effector configuration - -**Properties** - -| Property | Type | Required | Description | -|-------------------|----------------------------|----------|--------------------------| -| `wasmCID` | string | **Yes** | Wasm CID of the effector | -| `allowedBinaries` | [object](#allowedbinaries) | No | Allowed binaries | - -**allowedBinaries** - -Allowed binaries - -**Properties** - -| Property | Type | Required | Description | -|----------|--------|----------|-------------| -| `curl` | string | No | | - -##### ipfs - -IPFS config - -###### Properties - -| Property | Type | Required | Description | -|------------------------|--------|----------|-------------------------------------------------| -| `externalApiMultiaddr` | string | No | Multiaddress of external IPFS API | -| `ipfsBinaryPath` | string | No | Path to the IPFS binary. Default: /usr/bin/ipfs | -| `localApiMultiaddr` | string | No | Multiaddress of local IPFS API | - -##### metrics - -Metrics configuration - -###### Properties - -| Property | Type | Required | Description | -|-------------------------------|---------|----------|--------------------------------------| -| `enabled` | boolean | No | Metrics enabled. Default: true | -| `timerResolution` | string | No | Timer resolution. Default: 1 minute | -| `tokioDetailedMetricsEnabled` | boolean | No | Tokio detailed metrics enabled | -| `tokioMetricsEnabled` | boolean | No | Tokio metrics enabled. Default: true | - -##### systemServices - -System services to run by default. aquaIpfs and decider are enabled by default - -###### Properties - -| Property | Type | Required | Description | -|------------|---------------------|----------|-----------------------------------| -| `aquaIpfs` | [object](#aquaipfs) | No | Aqua IPFS service configuration | -| `decider` | [object](#decider) | No | Decider service configuration | -| `enable` | string[] | No | List of system services to enable | - -###### aquaIpfs - -Aqua IPFS service configuration - -**Properties** - -| Property | Type | Required | Description | -|------------------------|--------|----------|-------------------------------------------------| -| `externalApiMultiaddr` | string | No | Multiaddress of external IPFS API | -| `ipfsBinaryPath` | string | No | Path to the IPFS binary. Default: /usr/bin/ipfs | -| `localApiMultiaddr` | string | No | Multiaddress of local IPFS API | - -###### decider - -Decider service configuration - -**Properties** - -| Property | Type | Required | Description | -|-----------------------|---------|----------|-----------------------------------| -| `deciderPeriodSec` | integer | No | Decider period in seconds | -| `matcherAddress` | string | No | Matcher address (deprecated) | -| `networkApiEndpoint` | string | No | Network API endpoint (deprecated) | -| `networkId` | integer | No | Network ID (deprecated) | -| `startBlock` | string | No | Start block (deprecated) | -| `walletKey` | string | No | Wallet key (deprecated) | -| `workerIpfsMultiaddr` | string | No | Multiaddress of worker IPFS node | -| `workerPeriodSec` | integer | No | Worker period in seconds | - -##### vm - -VM Configuration - -###### Properties - -| Property | Type | Required | Description | -|--------------|--------------------|----------|--------------------------------------------| -| `network` | [object](#network) | **Yes** | VM Network Configuration | -| `allowGpu` | boolean | No | Whether to add info about GPUs to VM's XML | -| `libvirtUri` | string | No | QEMU Socket | - -###### network - -VM Network Configuration - -**Properties** - -| Property | Type | Required | Description | -|---------------|----------------------|----------|------------------------------------------------------------------| -| `publicIp` | string | **Yes** | Public IP address to assign the VM. Must be publicly accessible. | -| `bridgeName` | string | No | Name of the network bridge device | -| `hostSshPort` | integer | No | Host SSH port, default is 922 | -| `portRange` | [object](#portrange) | No | iptables-mapped port range from Host to VM | -| `vmIp` | string | No | Internal IP address to assign the VM | -| `vmSshPort` | integer | No | VM SSH port, default is 22 | - -**portRange** - -iptables-mapped port range from Host to VM - -**Properties** - -| Property | Type | Required | Description | -|----------|---------|----------|---------------------------------------------------------| -| `end` | integer | No | End of the iptables-mapped port range from Host to VM | -| `start` | integer | No | Start of the iptables-mapped port range from Host to VM | - -## nox - -Configuration to pass to the nox compute peer. Config.toml files are generated from this config - -### Properties - -| Property | Type | Required | Description | -|--------------------------|---------------------------|----------|-------------------------------------------------------------------------------------------------------------------| -| `aquavmPoolSize` | integer | No | Number of aquavm instances to run. Default: 2 | -| `bootstrapNodes` | string[] | No | List of bootstrap nodes. Default: all addresses for the selected env | -| `ccp` | [object](#ccp) | No | For advanced users. CCP config | -| `chain` | [object](#chain) | No | Chain config | -| `cpusRange` | string | No | Range of CPU cores to use. Default: 1-32 | -| `effectors` | [object](#effectors) | No | Effectors to allow on the nox | -| `externalMultiaddresses` | string[] | No | List of external multiaddresses | -| `httpPort` | integer | No | Both host and container HTTP port to use. Default: 918 (on local network ports will be generated by default) | -| `ipfs` | [object](#ipfs) | No | IPFS config | -| `listenIp` | string | No | IP to listen on | -| `metrics` | [object](#metrics) | No | Metrics configuration | -| `rawConfig` | string | No | Raw TOML config string to parse and merge with the rest of the config. Has the highest priority | -| `systemCpuCount` | integer | No | Number of CPU cores to allocate for the Nox itself. Default: 1 | -| `systemServices` | [object](#systemservices) | No | System services to run by default. aquaIpfs and decider are enabled by default | -| `tcpPort` | integer | No | Both host and container TCP port to use. Default: 977 (on local network ports will be generated by default) | -| `vm` | [object](#vm) | No | VM Configuration | -| `websocketPort` | integer | No | Both host and container WebSocket port to use. Default: 999 (on local network ports will be generated by default) | - -### ccp - -For advanced users. CCP config - -#### Properties - -| Property | Type | Required | Description | -|-------------------|--------|----------|-------------------------------------------------------------------------------------------------------------| -| `ccpEndpoint` | string | No | CCP endpoint. Default comes from top-level ccp config: http://{ccp.rpcEndpoint.host}:{ccp.rpcEndpoint.port} | -| `proofPollPeriod` | string | No | Proof poll period. Default: 60 seconds | - -### chain - -Chain config - -#### Properties - -| Property | Type | Required | Description | -|----------------------|---------|----------|---------------------------------------------------| -| `ccContract` | string | No | Capacity commitment contract address (deprecated) | -| `coreContract` | string | No | Core contract address (deprecated) | -| `dealSyncStartBlock` | string | No | Start block (deprecated) | -| `defaultBaseFee` | number | No | Default base fee | -| `defaultPriorityFee` | number | No | Default priority fee | -| `diamondContract` | string | No | Diamond contract address | -| `httpEndpoint` | string | No | HTTP endpoint of the chain | -| `marketContract` | string | No | Market contract address (deprecated) | -| `networkId` | integer | No | Network ID | -| `walletPrivateKey` | string | No | Nox wallet private key. Is generated by default | -| `wsEndpoint` | string | No | WebSocket endpoint of the chain | - -### effectors - -Effectors to allow on the nox - -#### Properties - -| Property | Type | Required | Description | -|----------------|-------------------------|----------|------------------------| -| `effectorName` | [object](#effectorname) | No | Effector configuration | - -#### effectorName - -Effector configuration - -##### Properties - -| Property | Type | Required | Description | -|-------------------|----------------------------|----------|--------------------------| -| `wasmCID` | string | **Yes** | Wasm CID of the effector | -| `allowedBinaries` | [object](#allowedbinaries) | No | Allowed binaries | - -##### allowedBinaries - -Allowed binaries - -###### Properties - -| Property | Type | Required | Description | -|----------|--------|----------|-------------| -| `curl` | string | No | | - -### ipfs - -IPFS config - -#### Properties - -| Property | Type | Required | Description | -|------------------------|--------|----------|-------------------------------------------------| -| `externalApiMultiaddr` | string | No | Multiaddress of external IPFS API | -| `ipfsBinaryPath` | string | No | Path to the IPFS binary. Default: /usr/bin/ipfs | -| `localApiMultiaddr` | string | No | Multiaddress of local IPFS API | - -### metrics - -Metrics configuration - -#### Properties - -| Property | Type | Required | Description | -|-------------------------------|---------|----------|--------------------------------------| -| `enabled` | boolean | No | Metrics enabled. Default: true | -| `timerResolution` | string | No | Timer resolution. Default: 1 minute | -| `tokioDetailedMetricsEnabled` | boolean | No | Tokio detailed metrics enabled | -| `tokioMetricsEnabled` | boolean | No | Tokio metrics enabled. Default: true | - -### systemServices - -System services to run by default. aquaIpfs and decider are enabled by default - -#### Properties - -| Property | Type | Required | Description | -|------------|---------------------|----------|-----------------------------------| -| `aquaIpfs` | [object](#aquaipfs) | No | Aqua IPFS service configuration | -| `decider` | [object](#decider) | No | Decider service configuration | -| `enable` | string[] | No | List of system services to enable | - -#### aquaIpfs - -Aqua IPFS service configuration - -##### Properties - -| Property | Type | Required | Description | -|------------------------|--------|----------|-------------------------------------------------| -| `externalApiMultiaddr` | string | No | Multiaddress of external IPFS API | -| `ipfsBinaryPath` | string | No | Path to the IPFS binary. Default: /usr/bin/ipfs | -| `localApiMultiaddr` | string | No | Multiaddress of local IPFS API | - -#### decider - -Decider service configuration - -##### Properties - -| Property | Type | Required | Description | -|-----------------------|---------|----------|-----------------------------------| -| `deciderPeriodSec` | integer | No | Decider period in seconds | -| `matcherAddress` | string | No | Matcher address (deprecated) | -| `networkApiEndpoint` | string | No | Network API endpoint (deprecated) | -| `networkId` | integer | No | Network ID (deprecated) | -| `startBlock` | string | No | Start block (deprecated) | -| `walletKey` | string | No | Wallet key (deprecated) | -| `workerIpfsMultiaddr` | string | No | Multiaddress of worker IPFS node | -| `workerPeriodSec` | integer | No | Worker period in seconds | - -### vm - -VM Configuration - -#### Properties - -| Property | Type | Required | Description | -|--------------|--------------------|----------|--------------------------------------------| -| `network` | [object](#network) | **Yes** | VM Network Configuration | -| `allowGpu` | boolean | No | Whether to add info about GPUs to VM's XML | -| `libvirtUri` | string | No | QEMU Socket | - -#### network - -VM Network Configuration - -##### Properties - -| Property | Type | Required | Description | -|---------------|----------------------|----------|------------------------------------------------------------------| -| `publicIp` | string | **Yes** | Public IP address to assign the VM. Must be publicly accessible. | -| `bridgeName` | string | No | Name of the network bridge device | -| `hostSshPort` | integer | No | Host SSH port, default is 922 | -| `portRange` | [object](#portrange) | No | iptables-mapped port range from Host to VM | -| `vmIp` | string | No | Internal IP address to assign the VM | -| `vmSshPort` | integer | No | VM SSH port, default is 22 | - -##### portRange - -iptables-mapped port range from Host to VM - -###### Properties - -| Property | Type | Required | Description | -|----------|---------|----------|---------------------------------------------------------| -| `end` | integer | No | End of the iptables-mapped port range from Host to VM | -| `start` | integer | No | Start of the iptables-mapped port range from Host to VM | - -## offers - -A map with offer names as keys and offers as values - -### Properties - -| Property | Type | Required | Description | -|----------|------------------|----------|--------------------------| -| `Offer` | [object](#offer) | No | Defines a provider offer | - -### Offer - -Defines a provider offer - -#### Properties - -| Property | Type | Required | Description | -|-------------------------|----------|----------|------------------------------------------------------------------------------------| -| `computePeers` | string[] | **Yes** | Number of Compute Units for this Compute Peer | -| `minPricePerCuPerEpoch` | string | **Yes** | Minimum price per compute unit per epoch in USDC | -| `effectors` | string[] | No | | -| `maxProtocolVersion` | integer | No | Max protocol version. Must be more then or equal to minProtocolVersion. Default: 1 | -| `minProtocolVersion` | integer | No | Min protocol version. Must be less then or equal to maxProtocolVersion. Default: 1 | - diff --git a/cli/docs/configs/service.md b/cli/docs/configs/service.md deleted file mode 100644 index bb9e96b50..000000000 --- a/cli/docs/configs/service.md +++ /dev/null @@ -1,106 +0,0 @@ -# service.yaml - -Defines a [Marine service](https://fluence.dev/docs/build/concepts/#services), most importantly the modules that the service consists of. You can use `fluence service new` command to generate a template for new service - -## Properties - -| Property | Type | Required | Description | -|--------------------|--------------------|----------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `modules` | [object](#modules) | **Yes** | Service must have a facade module. Each module properties can be overridden by the same properties in the service config | -| `name` | string | **Yes** | Service name. Currently it is used for the service name only when you add service to fluence.yaml using "add" command. But this name can be overridden to any other with the --name flag or manually in fluence.yaml | -| `version` | integer | **Yes** | | -| `totalMemoryLimit` | string | No | Memory limit for all service modules. If you specify this property please make sure it's at least `2 MiB * numberOfModulesInTheService`. In repl default is the entire compute unit memory: 2GB. When deploying service as part of the worker default is: computeUnits * 2GB / (amount of services in the worker). Format: [number][whitespace?][B] where ? is an optional field and B is one of the following: kB, KB, kiB, KiB, KIB, mB, MB, miB, MiB, MIB, gB, GB, giB, GiB, GIB | - -## modules - -Service must have a facade module. Each module properties can be overridden by the same properties in the service config - -### Properties - -| Property | Type | Required | Description | -|---------------------|------------------------------|----------|-------------| -| `facade` | [object](#facade) | **Yes** | | -| `Other_module_name` | [object](#other_module_name) | No | | - -### Other_module_name - -#### Properties - -| Property | Type | Required | Description | -|-----------|--------------------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `get` | string | **Yes** | Either path to the module directory or URL to the tar.gz archive which contains the content of the module directory | -| `effects` | [object](#effects) | No | Effects configuration. Only providers can allow and control effector modules by changing the nox configuration. Properties in this config are ignored when you deploy your code | -| `repl` | [object](#repl) | No | REPL configuration. Properties in this config are ignored when you deploy your code | - -#### effects - -Effects configuration. Only providers can allow and control effector modules by changing the nox configuration. Properties in this config are ignored when you deploy your code - -##### Properties - -| Property | Type | Required | Description | -|------------|---------------------|----------|-----------------------------------------------------------------------------------------------| -| `binaries` | [object](#binaries) | No | A map of binary executable files that module is allowed to call. Example: curl: /usr/bin/curl | - -##### binaries - -A map of binary executable files that module is allowed to call. Example: curl: /usr/bin/curl - -###### Properties - -| Property | Type | Required | Description | -|---------------|--------|----------|------------------| -| `binary-name` | string | No | Path to a binary | - -#### repl - -REPL configuration. Properties in this config are ignored when you deploy your code - -##### Properties - -| Property | Type | Required | Description | -|-----------------|---------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------| -| `loggerEnabled` | boolean | No | Set true to allow module to use the Marine SDK logger | -| `loggingMask` | number | No | manages the logging targets, that are described in detail here: https://fluence.dev/docs/marine-book/marine-rust-sdk/developing/logging#using-target-map | - -### facade - -#### Properties - -| Property | Type | Required | Description | -|-----------|--------------------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `get` | string | **Yes** | Either path to the module directory or URL to the tar.gz archive which contains the content of the module directory | -| `effects` | [object](#effects) | No | Effects configuration. Only providers can allow and control effector modules by changing the nox configuration. Properties in this config are ignored when you deploy your code | -| `repl` | [object](#repl) | No | REPL configuration. Properties in this config are ignored when you deploy your code | - -#### effects - -Effects configuration. Only providers can allow and control effector modules by changing the nox configuration. Properties in this config are ignored when you deploy your code - -##### Properties - -| Property | Type | Required | Description | -|------------|---------------------|----------|-----------------------------------------------------------------------------------------------| -| `binaries` | [object](#binaries) | No | A map of binary executable files that module is allowed to call. Example: curl: /usr/bin/curl | - -##### binaries - -A map of binary executable files that module is allowed to call. Example: curl: /usr/bin/curl - -###### Properties - -| Property | Type | Required | Description | -|---------------|--------|----------|------------------| -| `binary-name` | string | No | Path to a binary | - -#### repl - -REPL configuration. Properties in this config are ignored when you deploy your code - -##### Properties - -| Property | Type | Required | Description | -|-----------------|---------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------| -| `loggerEnabled` | boolean | No | Set true to allow module to use the Marine SDK logger | -| `loggingMask` | number | No | manages the logging targets, that are described in detail here: https://fluence.dev/docs/marine-book/marine-rust-sdk/developing/logging#using-target-map | - diff --git a/cli/docs/configs/spell.md b/cli/docs/configs/spell.md deleted file mode 100644 index 49a6ccd8c..000000000 --- a/cli/docs/configs/spell.md +++ /dev/null @@ -1,35 +0,0 @@ -# spell.yaml - -Defines a spell. You can use `fluence spell new` command to generate a template for new spell - -## Properties - -| Property | Type | Required | Description | -|----------------|---------------------|----------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `aquaFilePath` | string | **Yes** | Path to Aqua file which contains an Aqua function that you want to use as a spell | -| `function` | string | **Yes** | Name of the Aqua function that you want to use as a spell | -| `version` | integer | **Yes** | | -| `clock` | [object](#clock) | No | Trigger the spell execution periodically. If you want to disable this property by overriding it in fluence.yaml - pass empty config for it like this: `clock: {}` | -| `initArgs` | [object](#initargs) | No | A map of Aqua function arguments names as keys and arguments values as values. They will be passed to the spell function and will be stored in the key-value storage for this particular spell. | - -## clock - -Trigger the spell execution periodically. If you want to disable this property by overriding it in fluence.yaml - pass empty config for it like this: `clock: {}` - -### Properties - -| Property | Type | Required | Description | -|------------------|---------|----------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `endDelaySec` | integer | No | How long to wait before the last execution in seconds. If this property or `endTimestamp` not specified, periodic execution will never end. WARNING! Currently your computer's clock is used to determine a final timestamp that is sent to the server. If it is in the past at the moment of spell creation - the spell will never be executed. This property conflicts with `endTimestamp`. You can specify only one of them | -| `endTimestamp` | string | No | An ISO timestamp when the periodic execution should end. If this property or `endDelaySec` not specified, periodic execution will never end. If it is in the past at the moment of spell creation on Rust peer - the spell will never be executed | -| `periodSec` | integer | No | How often the spell will be executed. If set to 0, the spell will be executed only once. If this value not provided at all - the spell will never be executed | -| `startDelaySec` | integer | No | How long to wait before the first execution in seconds. If this property or `startTimestamp` not specified, periodic execution will start immediately. WARNING! Currently your computer's clock is used to determine a final timestamp that is sent to the server. This property conflicts with `startTimestamp`. You can specify only one of them | -| `startTimestamp` | string | No | An ISO timestamp when the periodic execution should start. If this property or `startDelaySec` not specified, periodic execution will start immediately. If it is set to 0 - the spell will never be executed | - -## initArgs - -A map of Aqua function arguments names as keys and arguments values as values. They will be passed to the spell function and will be stored in the key-value storage for this particular spell. - -| Property | Type | Required | Description | -|----------|------|----------|-------------| - diff --git a/cli/docs/configs/workers.md b/cli/docs/configs/workers.md deleted file mode 100644 index 990d5894a..000000000 --- a/cli/docs/configs/workers.md +++ /dev/null @@ -1,340 +0,0 @@ -# workers.yaml - -A result of app deployment. This file is created automatically after successful deployment using `fluence workers deploy` command - -## Properties - -| Property | Type | Required | Description | -|-----------|------------------|----------|----------------------------------------------------------------------------------------------------| -| `version` | integer | **Yes** | Config version | -| `deals` | [object](#deals) | No | Info about deals created when deploying workers that is stored by environment that you deployed to | -| `hosts` | [object](#hosts) | No | Info about directly deployed workers that is stored by environment that you deployed to | - -## deals - -Info about deals created when deploying workers that is stored by environment that you deployed to - -### Properties - -| Property | Type | Required | Description | -|-----------|--------------------|----------|------------------------| -| `custom` | [object](#custom) | No | A map of created deals | -| `local` | [object](#local) | No | A map of created deals | -| `mainnet` | [object](#mainnet) | No | A map of created deals | -| `stage` | [object](#stage) | No | A map of created deals | -| `testnet` | [object](#testnet) | No | A map of created deals | - -### custom - -A map of created deals - -#### Properties - -| Property | Type | Required | Description | -|-------------------------------|----------------------------------------|----------|-----------------------------------------------------------------------------------------------------------------------------| -| `Worker_deployed_using_deals` | [object](#worker_deployed_using_deals) | No | Contains data related to your deployment, including, most importantly, deal id, that can be used to resolve workers in aqua | - -#### Worker_deployed_using_deals - -Contains data related to your deployment, including, most importantly, deal id, that can be used to resolve workers in aqua - -##### Properties - -| Property | Type | Required | Description | -|------------------|---------|----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `chainNetworkId` | integer | **Yes** | Blockchain network id that was used when deploying workers | -| `dealIdOriginal` | string | **Yes** | Blockchain transaction id that you get when deploy workers. Can be used in aqua to get worker and host ids. Check out example in the aqua generated in the default template | -| `dealId` | string | **Yes** | Lowercased version of dealIdOriginal without 0x prefix. Currently unused. Was previously used to resolve workers in aqua | -| `definition` | string | **Yes** | CID of uploaded to IPFS App Definition, which contains the data about everything that you are trying to deploy, including spells, service and module configs and CIDs for service wasms | -| `timestamp` | string | **Yes** | ISO timestamp of the time when the worker was deployed | -| `chainNetwork` | string | No | DEPRECATED. Blockchain network name that was used when deploying workers Possible values are: `kras`, `dar`, `stage`, `local`. | -| `matched` | boolean | No | Is deal matched | - -### local - -A map of created deals - -#### Properties - -| Property | Type | Required | Description | -|-------------------------------|----------------------------------------|----------|-----------------------------------------------------------------------------------------------------------------------------| -| `Worker_deployed_using_deals` | [object](#worker_deployed_using_deals) | No | Contains data related to your deployment, including, most importantly, deal id, that can be used to resolve workers in aqua | - -#### Worker_deployed_using_deals - -Contains data related to your deployment, including, most importantly, deal id, that can be used to resolve workers in aqua - -##### Properties - -| Property | Type | Required | Description | -|------------------|---------|----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `chainNetworkId` | integer | **Yes** | Blockchain network id that was used when deploying workers | -| `dealIdOriginal` | string | **Yes** | Blockchain transaction id that you get when deploy workers. Can be used in aqua to get worker and host ids. Check out example in the aqua generated in the default template | -| `dealId` | string | **Yes** | Lowercased version of dealIdOriginal without 0x prefix. Currently unused. Was previously used to resolve workers in aqua | -| `definition` | string | **Yes** | CID of uploaded to IPFS App Definition, which contains the data about everything that you are trying to deploy, including spells, service and module configs and CIDs for service wasms | -| `timestamp` | string | **Yes** | ISO timestamp of the time when the worker was deployed | -| `chainNetwork` | string | No | DEPRECATED. Blockchain network name that was used when deploying workers Possible values are: `kras`, `dar`, `stage`, `local`. | -| `matched` | boolean | No | Is deal matched | - -### mainnet - -A map of created deals - -#### Properties - -| Property | Type | Required | Description | -|-------------------------------|----------------------------------------|----------|-----------------------------------------------------------------------------------------------------------------------------| -| `Worker_deployed_using_deals` | [object](#worker_deployed_using_deals) | No | Contains data related to your deployment, including, most importantly, deal id, that can be used to resolve workers in aqua | - -#### Worker_deployed_using_deals - -Contains data related to your deployment, including, most importantly, deal id, that can be used to resolve workers in aqua - -##### Properties - -| Property | Type | Required | Description | -|------------------|---------|----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `chainNetworkId` | integer | **Yes** | Blockchain network id that was used when deploying workers | -| `dealIdOriginal` | string | **Yes** | Blockchain transaction id that you get when deploy workers. Can be used in aqua to get worker and host ids. Check out example in the aqua generated in the default template | -| `dealId` | string | **Yes** | Lowercased version of dealIdOriginal without 0x prefix. Currently unused. Was previously used to resolve workers in aqua | -| `definition` | string | **Yes** | CID of uploaded to IPFS App Definition, which contains the data about everything that you are trying to deploy, including spells, service and module configs and CIDs for service wasms | -| `timestamp` | string | **Yes** | ISO timestamp of the time when the worker was deployed | -| `chainNetwork` | string | No | DEPRECATED. Blockchain network name that was used when deploying workers Possible values are: `kras`, `dar`, `stage`, `local`. | -| `matched` | boolean | No | Is deal matched | - -### stage - -A map of created deals - -#### Properties - -| Property | Type | Required | Description | -|-------------------------------|----------------------------------------|----------|-----------------------------------------------------------------------------------------------------------------------------| -| `Worker_deployed_using_deals` | [object](#worker_deployed_using_deals) | No | Contains data related to your deployment, including, most importantly, deal id, that can be used to resolve workers in aqua | - -#### Worker_deployed_using_deals - -Contains data related to your deployment, including, most importantly, deal id, that can be used to resolve workers in aqua - -##### Properties - -| Property | Type | Required | Description | -|------------------|---------|----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `chainNetworkId` | integer | **Yes** | Blockchain network id that was used when deploying workers | -| `dealIdOriginal` | string | **Yes** | Blockchain transaction id that you get when deploy workers. Can be used in aqua to get worker and host ids. Check out example in the aqua generated in the default template | -| `dealId` | string | **Yes** | Lowercased version of dealIdOriginal without 0x prefix. Currently unused. Was previously used to resolve workers in aqua | -| `definition` | string | **Yes** | CID of uploaded to IPFS App Definition, which contains the data about everything that you are trying to deploy, including spells, service and module configs and CIDs for service wasms | -| `timestamp` | string | **Yes** | ISO timestamp of the time when the worker was deployed | -| `chainNetwork` | string | No | DEPRECATED. Blockchain network name that was used when deploying workers Possible values are: `kras`, `dar`, `stage`, `local`. | -| `matched` | boolean | No | Is deal matched | - -### testnet - -A map of created deals - -#### Properties - -| Property | Type | Required | Description | -|-------------------------------|----------------------------------------|----------|-----------------------------------------------------------------------------------------------------------------------------| -| `Worker_deployed_using_deals` | [object](#worker_deployed_using_deals) | No | Contains data related to your deployment, including, most importantly, deal id, that can be used to resolve workers in aqua | - -#### Worker_deployed_using_deals - -Contains data related to your deployment, including, most importantly, deal id, that can be used to resolve workers in aqua - -##### Properties - -| Property | Type | Required | Description | -|------------------|---------|----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `chainNetworkId` | integer | **Yes** | Blockchain network id that was used when deploying workers | -| `dealIdOriginal` | string | **Yes** | Blockchain transaction id that you get when deploy workers. Can be used in aqua to get worker and host ids. Check out example in the aqua generated in the default template | -| `dealId` | string | **Yes** | Lowercased version of dealIdOriginal without 0x prefix. Currently unused. Was previously used to resolve workers in aqua | -| `definition` | string | **Yes** | CID of uploaded to IPFS App Definition, which contains the data about everything that you are trying to deploy, including spells, service and module configs and CIDs for service wasms | -| `timestamp` | string | **Yes** | ISO timestamp of the time when the worker was deployed | -| `chainNetwork` | string | No | DEPRECATED. Blockchain network name that was used when deploying workers Possible values are: `kras`, `dar`, `stage`, `local`. | -| `matched` | boolean | No | Is deal matched | - -## hosts - -Info about directly deployed workers that is stored by environment that you deployed to - -### Properties - -| Property | Type | Required | Description | -|-----------|--------------------|----------|------------------------------------| -| `custom` | [object](#custom) | No | A map of directly deployed workers | -| `local` | [object](#local) | No | A map of directly deployed workers | -| `mainnet` | [object](#mainnet) | No | A map of directly deployed workers | -| `stage` | [object](#stage) | No | A map of directly deployed workers | -| `testnet` | [object](#testnet) | No | A map of directly deployed workers | - -### custom - -A map of directly deployed workers - -#### Properties - -| Property | Type | Required | Description | -|----------------------------------------|-------------------------------------------------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `Worker_deployed_using_direct_hosting` | [object](#worker_deployed_using_direct_hosting) | No | Contains data related to your direct deployment. Most importantly, it contains ids in installation_spells property that can be used to resolve workers in aqua | - -#### Worker_deployed_using_direct_hosting - -Contains data related to your direct deployment. Most importantly, it contains ids in installation_spells property that can be used to resolve workers in aqua - -##### Properties - -| Property | Type | Required | Description | -|-----------------------|----------------------------------|----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `definition` | string | **Yes** | CID of uploaded to IPFS App Definition, which contains the data about everything that you are trying to deploy, including spells, service and module configs and CIDs for service wasms | -| `dummyDealId` | string | **Yes** | random string generated by CLI, used in Nox. You can get worker id from it | -| `installation_spells` | [object](#installation_spells)[] | **Yes** | A list of installation spells | -| `relayId` | string | **Yes** | relay peer id that was used when deploying | -| `timestamp` | string | **Yes** | ISO timestamp of the time when the worker was deployed | - -##### installation_spells - -###### Properties - -| Property | Type | Required | Description | -|-------------|--------|----------|--------------------------------------------------------------------| -| `host_id` | string | **Yes** | Can be used to access worker in aqua: `on s.workerId via s.hostId` | -| `spell_id` | string | **Yes** | id of the installation spell, can be used to e.g. print spell logs | -| `worker_id` | string | **Yes** | Can be used to access worker in aqua: `on s.workerId via s.hostId` | - -### local - -A map of directly deployed workers - -#### Properties - -| Property | Type | Required | Description | -|----------------------------------------|-------------------------------------------------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `Worker_deployed_using_direct_hosting` | [object](#worker_deployed_using_direct_hosting) | No | Contains data related to your direct deployment. Most importantly, it contains ids in installation_spells property that can be used to resolve workers in aqua | - -#### Worker_deployed_using_direct_hosting - -Contains data related to your direct deployment. Most importantly, it contains ids in installation_spells property that can be used to resolve workers in aqua - -##### Properties - -| Property | Type | Required | Description | -|-----------------------|----------------------------------|----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `definition` | string | **Yes** | CID of uploaded to IPFS App Definition, which contains the data about everything that you are trying to deploy, including spells, service and module configs and CIDs for service wasms | -| `dummyDealId` | string | **Yes** | random string generated by CLI, used in Nox. You can get worker id from it | -| `installation_spells` | [object](#installation_spells)[] | **Yes** | A list of installation spells | -| `relayId` | string | **Yes** | relay peer id that was used when deploying | -| `timestamp` | string | **Yes** | ISO timestamp of the time when the worker was deployed | - -##### installation_spells - -###### Properties - -| Property | Type | Required | Description | -|-------------|--------|----------|--------------------------------------------------------------------| -| `host_id` | string | **Yes** | Can be used to access worker in aqua: `on s.workerId via s.hostId` | -| `spell_id` | string | **Yes** | id of the installation spell, can be used to e.g. print spell logs | -| `worker_id` | string | **Yes** | Can be used to access worker in aqua: `on s.workerId via s.hostId` | - -### mainnet - -A map of directly deployed workers - -#### Properties - -| Property | Type | Required | Description | -|----------------------------------------|-------------------------------------------------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `Worker_deployed_using_direct_hosting` | [object](#worker_deployed_using_direct_hosting) | No | Contains data related to your direct deployment. Most importantly, it contains ids in installation_spells property that can be used to resolve workers in aqua | - -#### Worker_deployed_using_direct_hosting - -Contains data related to your direct deployment. Most importantly, it contains ids in installation_spells property that can be used to resolve workers in aqua - -##### Properties - -| Property | Type | Required | Description | -|-----------------------|----------------------------------|----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `definition` | string | **Yes** | CID of uploaded to IPFS App Definition, which contains the data about everything that you are trying to deploy, including spells, service and module configs and CIDs for service wasms | -| `dummyDealId` | string | **Yes** | random string generated by CLI, used in Nox. You can get worker id from it | -| `installation_spells` | [object](#installation_spells)[] | **Yes** | A list of installation spells | -| `relayId` | string | **Yes** | relay peer id that was used when deploying | -| `timestamp` | string | **Yes** | ISO timestamp of the time when the worker was deployed | - -##### installation_spells - -###### Properties - -| Property | Type | Required | Description | -|-------------|--------|----------|--------------------------------------------------------------------| -| `host_id` | string | **Yes** | Can be used to access worker in aqua: `on s.workerId via s.hostId` | -| `spell_id` | string | **Yes** | id of the installation spell, can be used to e.g. print spell logs | -| `worker_id` | string | **Yes** | Can be used to access worker in aqua: `on s.workerId via s.hostId` | - -### stage - -A map of directly deployed workers - -#### Properties - -| Property | Type | Required | Description | -|----------------------------------------|-------------------------------------------------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `Worker_deployed_using_direct_hosting` | [object](#worker_deployed_using_direct_hosting) | No | Contains data related to your direct deployment. Most importantly, it contains ids in installation_spells property that can be used to resolve workers in aqua | - -#### Worker_deployed_using_direct_hosting - -Contains data related to your direct deployment. Most importantly, it contains ids in installation_spells property that can be used to resolve workers in aqua - -##### Properties - -| Property | Type | Required | Description | -|-----------------------|----------------------------------|----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `definition` | string | **Yes** | CID of uploaded to IPFS App Definition, which contains the data about everything that you are trying to deploy, including spells, service and module configs and CIDs for service wasms | -| `dummyDealId` | string | **Yes** | random string generated by CLI, used in Nox. You can get worker id from it | -| `installation_spells` | [object](#installation_spells)[] | **Yes** | A list of installation spells | -| `relayId` | string | **Yes** | relay peer id that was used when deploying | -| `timestamp` | string | **Yes** | ISO timestamp of the time when the worker was deployed | - -##### installation_spells - -###### Properties - -| Property | Type | Required | Description | -|-------------|--------|----------|--------------------------------------------------------------------| -| `host_id` | string | **Yes** | Can be used to access worker in aqua: `on s.workerId via s.hostId` | -| `spell_id` | string | **Yes** | id of the installation spell, can be used to e.g. print spell logs | -| `worker_id` | string | **Yes** | Can be used to access worker in aqua: `on s.workerId via s.hostId` | - -### testnet - -A map of directly deployed workers - -#### Properties - -| Property | Type | Required | Description | -|----------------------------------------|-------------------------------------------------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `Worker_deployed_using_direct_hosting` | [object](#worker_deployed_using_direct_hosting) | No | Contains data related to your direct deployment. Most importantly, it contains ids in installation_spells property that can be used to resolve workers in aqua | - -#### Worker_deployed_using_direct_hosting - -Contains data related to your direct deployment. Most importantly, it contains ids in installation_spells property that can be used to resolve workers in aqua - -##### Properties - -| Property | Type | Required | Description | -|-----------------------|----------------------------------|----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `definition` | string | **Yes** | CID of uploaded to IPFS App Definition, which contains the data about everything that you are trying to deploy, including spells, service and module configs and CIDs for service wasms | -| `dummyDealId` | string | **Yes** | random string generated by CLI, used in Nox. You can get worker id from it | -| `installation_spells` | [object](#installation_spells)[] | **Yes** | A list of installation spells | -| `relayId` | string | **Yes** | relay peer id that was used when deploying | -| `timestamp` | string | **Yes** | ISO timestamp of the time when the worker was deployed | - -##### installation_spells - -###### Properties - -| Property | Type | Required | Description | -|-------------|--------|----------|--------------------------------------------------------------------| -| `host_id` | string | **Yes** | Can be used to access worker in aqua: `on s.workerId via s.hostId` | -| `spell_id` | string | **Yes** | id of the installation spell, can be used to e.g. print spell logs | -| `worker_id` | string | **Yes** | Can be used to access worker in aqua: `on s.workerId via s.hostId` | - diff --git a/package.json b/package.json index f73123c52..c7f77d0b4 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,6 @@ "scripts": { "build": "turbo run build --cache-dir=.turbo", "install-yarn-dependencies": "turbo run install-yarn-dependencies --cache-dir=.turbo", - "install-npm-dependencies": "turbo run install-npm-dependencies --cache-dir=.turbo", "gen-gql-schema": "turbo run gen-gql-schema --cache-dir=.turbo", "gql-codegen": "turbo run gql-codegen --cache-dir=.turbo", "pack-ci": "turbo run pack-ci --cache-dir=.turbo && node ./rename-packed.js", @@ -18,16 +17,11 @@ "pack-darwin-arm64": "turbo run pack-darwin-arm64 --cache-dir=.turbo && node ./rename-packed.js", "pack-win32-x64": "turbo run pack-win32-x64 --cache-dir=.turbo && node ./rename-packed.js", "on-each-commit": "turbo run on-each-commit --cache-dir=.turbo", - "download-marine-and-mrepl": "yarn ./packages/cli/package download-marine-and-mrepl", "generate-templates": "yarn ./packages/cli/package generate-templates", "local-up": "yarn ./packages/cli/package local-up", "vitest-provider": "yarn ./packages/cli/package vitest-provider", - "vitest-deal-deploy": "yarn ./packages/cli/package vitest-deal-deploy", - "vitest-deal-update": "yarn ./packages/cli/package vitest-deal-update", - "vitest-smoke": "yarn ./packages/cli/package vitest-smoke", - "vitest-js-to-aqua": "yarn ./packages/cli/package vitest-js-to-aqua", - "vitest": "yarn vitest-provider && yarn vitest-deal-deploy && yarn vitest-deal-update && yarn vitest-smoke && yarn vitest-js-to-aqua", - "test": "yarn download-marine-and-mrepl && yarn generate-templates && yarn local-up && yarn vitest", + "vitest": "yarn vitest-provider", + "test": "yarn generate-templates && yarn local-up && yarn vitest", "test-linux-x64": "yarn pack-linux-x64 && yarn test", "test-darwin-x64": "yarn pack-darwin-x64 && yarn test", "test-darwin-arm64": "yarn pack-darwin-arm64 && yarn test", diff --git a/packages/cli/package.json b/packages/cli/package.json index a3277568f..1d7fead6c 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -7,7 +7,6 @@ }, "scripts": { "install-yarn-dependencies": "yarn ./package install", - "install-npm-dependencies": "npm --prefix ./package/src/cli-aqua-dependencies install", "gen-gql-schema": "yarn ./package gen-gql-schema", "gql-codegen": "yarn ./package gql-codegen", "before-build": "yarn ./package before-build", @@ -18,11 +17,6 @@ "pack-darwin-arm64": "yarn ./package pack-darwin-arm64", "pack-win32-x64": "yarn ./package pack-win32-x64", "on-each-commit": "yarn ./package on-each-commit", - "generate-templates": "yarn ./package generate-templates", - "vitest-provider": "yarn ./package vitest-provider", - "vitest-deal-deploy": "yarn ./package vitest-deal-deploy", - "vitest-deal-update": "yarn ./package vitest-deal-update", - "vitest-smoke": "yarn ./package vitest-smoke", - "vitest-js-to-aqua": "yarn ./package vitest-js-to-aqua" + "vitest-provider": "yarn ./package vitest-provider" } } diff --git a/packages/cli/package/.gitignore b/packages/cli/package/.gitignore index c783f0275..69ec15db2 100644 --- a/packages/cli/package/.gitignore +++ b/packages/cli/package/.gitignore @@ -15,7 +15,6 @@ tmp /schemas /.f .env -rust-toolchain.toml docker-compose.yaml # recommended by Fluence @@ -26,12 +25,9 @@ docker-compose.yaml .repl_history # ignore compiled files -src/lib/compiled-aqua -src/lib/compiled-aqua-with-tracing src/lib/gql/gqlGenerated.ts src/lib/gql/gqlSchema.json src/versions -src/aqua-dependencies src/common.ts .yarn/* diff --git a/packages/cli/package/docs/commands/README.md b/packages/cli/package/docs/commands/README.md index c172cc9b5..815c9ed3c 100644 --- a/packages/cli/package/docs/commands/README.md +++ b/packages/cli/package/docs/commands/README.md @@ -1,47 +1,14 @@ # Commands -* [`fluence air beautify [PATH]`](#fluence-air-beautify-path) -* [`fluence aqua`](#fluence-aqua) -* [`fluence aqua imports`](#fluence-aqua-imports) -* [`fluence aqua json [INPUT] [OUTPUT]`](#fluence-aqua-json-input-output) -* [`fluence aqua yml [INPUT] [OUTPUT]`](#fluence-aqua-yml-input-output) * [`fluence autocomplete [SHELL]`](#fluence-autocomplete-shell) -* [`fluence build`](#fluence-build) * [`fluence chain info`](#fluence-chain-info) -* [`fluence deal change-app [DEAL-ADDRESS] [NEW-APP-CID]`](#fluence-deal-change-app-deal-address-new-app-cid) -* [`fluence deal create`](#fluence-deal-create) -* [`fluence deal deposit [AMOUNT] [DEPLOYMENT-NAMES]`](#fluence-deal-deposit-amount-deployment-names) -* [`fluence deal info [DEPLOYMENT-NAMES]`](#fluence-deal-info-deployment-names) -* [`fluence deal logs [DEPLOYMENT-NAMES]`](#fluence-deal-logs-deployment-names) -* [`fluence deal stop [DEPLOYMENT-NAMES]`](#fluence-deal-stop-deployment-names) -* [`fluence deal withdraw [AMOUNT] [DEPLOYMENT-NAMES]`](#fluence-deal-withdraw-amount-deployment-names) -* [`fluence deal workers-add [DEPLOYMENT-NAMES]`](#fluence-deal-workers-add-deployment-names) -* [`fluence deal workers-remove [WORKER-IDS]`](#fluence-deal-workers-remove-worker-ids) * [`fluence default env [ENV]`](#fluence-default-env-env) -* [`fluence default peers [ENV]`](#fluence-default-peers-env) -* [`fluence delegator collateral-add [IDS]`](#fluence-delegator-collateral-add-ids) -* [`fluence delegator collateral-withdraw [IDS]`](#fluence-delegator-collateral-withdraw-ids) -* [`fluence delegator reward-withdraw [IDS]`](#fluence-delegator-reward-withdraw-ids) -* [`fluence dep install [PACKAGE-NAME | PACKAGE-NAME@VERSION]`](#fluence-dep-install-package-name--package-nameversion) -* [`fluence dep reset`](#fluence-dep-reset) -* [`fluence dep uninstall PACKAGE-NAME`](#fluence-dep-uninstall-package-name) -* [`fluence dep versions`](#fluence-dep-versions) -* [`fluence deploy [DEPLOYMENT-NAMES]`](#fluence-deploy-deployment-names) * [`fluence help [COMMAND]`](#fluence-help-command) -* [`fluence init [PATH]`](#fluence-init-path) -* [`fluence key default [NAME]`](#fluence-key-default-name) -* [`fluence key new [NAME]`](#fluence-key-new-name) -* [`fluence key remove [NAME]`](#fluence-key-remove-name) * [`fluence local down`](#fluence-local-down) * [`fluence local init`](#fluence-local-init) * [`fluence local logs`](#fluence-local-logs) * [`fluence local ps`](#fluence-local-ps) * [`fluence local up`](#fluence-local-up) -* [`fluence module add [PATH | URL]`](#fluence-module-add-path--url) -* [`fluence module build [PATH]`](#fluence-module-build-path) -* [`fluence module new [NAME]`](#fluence-module-new-name) -* [`fluence module pack [PATH]`](#fluence-module-pack-path) -* [`fluence module remove [NAME | PATH | URL]`](#fluence-module-remove-name--path--url) * [`fluence provider cc-activate`](#fluence-provider-cc-activate) * [`fluence provider cc-create`](#fluence-provider-cc-create) * [`fluence provider cc-finish`](#fluence-provider-cc-finish) @@ -52,6 +19,7 @@ * [`fluence provider deal-list`](#fluence-provider-deal-list) * [`fluence provider deal-rewards-info [DEAL-ADDRESS] [ON-CHAIN-WORKER-ID]`](#fluence-provider-deal-rewards-info-deal-address-on-chain-worker-id) * [`fluence provider deal-rewards-withdraw`](#fluence-provider-deal-rewards-withdraw) +* [`fluence provider deploy`](#fluence-provider-deploy) * [`fluence provider gen`](#fluence-provider-gen) * [`fluence provider info`](#fluence-provider-info) * [`fluence provider init`](#fluence-provider-init) @@ -63,869 +31,103 @@ * [`fluence provider tokens-distribute`](#fluence-provider-tokens-distribute) * [`fluence provider tokens-withdraw`](#fluence-provider-tokens-withdraw) * [`fluence provider update`](#fluence-provider-update) -* [`fluence run`](#fluence-run) -* [`fluence service add [PATH | URL]`](#fluence-service-add-path--url) -* [`fluence service new [NAME]`](#fluence-service-new-name) -* [`fluence service remove [NAME | PATH | URL]`](#fluence-service-remove-name--path--url) -* [`fluence service repl [NAME | PATH | URL]`](#fluence-service-repl-name--path--url) -* [`fluence spell build [SPELL-NAMES]`](#fluence-spell-build-spell-names) -* [`fluence spell new [NAME]`](#fluence-spell-new-name) * [`fluence update [CHANNEL]`](#fluence-update-channel) -## `fluence air beautify [PATH]` - -Prints AIR script in human-readable Python-like representation. This representation cannot be executed and is intended to be read by mere mortals. Alias: fluence air b - -``` -USAGE - $ fluence air beautify [PATH] [--no-input] - -ARGUMENTS - PATH Path to an AIR file. Must be relative to the current working directory or absolute - -FLAGS - --no-input Don't interactively ask for any input from the user - -DESCRIPTION - Prints AIR script in human-readable Python-like representation. This representation cannot be executed and is intended - to be read by mere mortals. Alias: fluence air b -``` - -_See code: [src/commands/air/beautify.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/air/beautify.ts)_ - -## `fluence aqua` - -Compile aqua defined in 'compileAqua' property of fluence.yaml. If --input flag is used - then content of 'compileAqua' property in fluence.yaml will be ignored - -``` -USAGE - $ fluence aqua [-n ] [--no-input] [-w] [-o ] [--air | --js] [--import ...] [-i - ] [--const ...] [--log-level-compiler ] [--no-relay] [--no-xor] [--tracing] - [--no-empty-response] [--dry] - -FLAGS - -i, --input= Path to an aqua file or a directory that contains your aqua files - -n, --names= Comma-separated names of the configs from 'compileAqua' property of fluence.yaml to - compile. If not specified, all configs will be compiled - -o, --output= Path to the output directory. Must be relative to the current working directory or - absolute. Will be created if it doesn't exists - -w, --watch Watch aqua file or folder for changes and recompile - --air Generate .air file instead of .ts - --const=... Constants to be passed to the compiler - --dry Checks if compilation succeeded, without output - --import=... Path to a directory to import aqua files from. May be used several times - --js Generate .js file instead of .ts - --log-level-compiler= Set log level for the compiler. Must be one of: all, trace, debug, info, warn, - error, off - --no-empty-response Do not generate response call if there are no returned values - --no-input Don't interactively ask for any input from the user - --no-relay Do not generate a pass through the relay node - --no-xor Do not generate a wrapper that catches and displays errors - --tracing Compile aqua in tracing mode (for debugging purposes) - -DESCRIPTION - Compile aqua defined in 'compileAqua' property of fluence.yaml. If --input flag is used - then content of - 'compileAqua' property in fluence.yaml will be ignored - -EXAMPLES - $ fluence aqua -``` - -_See code: [src/commands/aqua.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/aqua.ts)_ - -## `fluence aqua imports` - -Returns a list of aqua imports that CLI produces - -``` -USAGE - $ fluence aqua imports [--no-input] - -FLAGS - --no-input Don't interactively ask for any input from the user - -DESCRIPTION - Returns a list of aqua imports that CLI produces -``` - -_See code: [src/commands/aqua/imports.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/aqua/imports.ts)_ - -## `fluence aqua json [INPUT] [OUTPUT]` - -Infers aqua types for an arbitrary json file, generates valid aqua code with a function call that returns an aqua object literal with the same structure as the json file. For valid generation please refer to aqua documentation https://fluence.dev/docs/aqua-book/language/ to learn about what kind of structures are valid in aqua language and what they translate into - -``` -USAGE - $ fluence aqua json [INPUT] [OUTPUT] [--no-input] [--f64] [--types ] - -ARGUMENTS - INPUT Path to json file - OUTPUT Path to the output dir - -FLAGS - --f64 Convert all numbers to f64. Useful for arrays objects that contain numbers of different types in them. - Without this flag, numbers will be converted to u64, i64 or f64 depending on their value - --no-input Don't interactively ask for any input from the user - --types= Experimental! Path to a file with custom types. Must be a list with objects that have 'name' and - 'properties'. 'properties' must be a list of all custom type properties - -DESCRIPTION - Infers aqua types for an arbitrary json file, generates valid aqua code with a function call that returns an aqua - object literal with the same structure as the json file. For valid generation please refer to aqua documentation - https://fluence.dev/docs/aqua-book/language/ to learn about what kind of structures are valid in aqua language and - what they translate into -``` - -_See code: [src/commands/aqua/json.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/aqua/json.ts)_ - -## `fluence aqua yml [INPUT] [OUTPUT]` - -Infers aqua types for an arbitrary yaml file, generates valid aqua code with a function call that returns an aqua object literal with the same structure as the yaml file. For valid generation please refer to aqua documentation https://fluence.dev/docs/aqua-book/language/ to learn about what kind of structures are valid in aqua language and what they translate into. Alias: fluence aqua yaml - -``` -USAGE - $ fluence aqua yml [INPUT] [OUTPUT] [--no-input] [--f64] [--types ] - -ARGUMENTS - INPUT Path to yaml file - OUTPUT Path to the output dir - -FLAGS - --f64 Convert all numbers to f64. Useful for arrays objects that contain numbers of different types in them. - Without this flag, numbers will be converted to u64, i64 or f64 depending on their value - --no-input Don't interactively ask for any input from the user - --types= Experimental! Path to a file with custom types. Must be a list with objects that have 'name' and - 'properties'. 'properties' must be a list of all custom type properties - -DESCRIPTION - Infers aqua types for an arbitrary yaml file, generates valid aqua code with a function call that returns an aqua - object literal with the same structure as the yaml file. For valid generation please refer to aqua documentation - https://fluence.dev/docs/aqua-book/language/ to learn about what kind of structures are valid in aqua language and - what they translate into. Alias: fluence aqua yaml -``` - -_See code: [src/commands/aqua/yml.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/aqua/yml.ts)_ - -## `fluence autocomplete [SHELL]` - -Display autocomplete installation instructions. - -``` -USAGE - $ fluence autocomplete [SHELL] [-r] - -ARGUMENTS - SHELL (zsh|bash|powershell) Shell type - -FLAGS - -r, --refresh-cache Refresh cache (ignores displaying instructions) - -DESCRIPTION - Display autocomplete installation instructions. - -EXAMPLES - $ fluence autocomplete - - $ fluence autocomplete bash - - $ fluence autocomplete zsh - - $ fluence autocomplete powershell - - $ fluence autocomplete --refresh-cache -``` - -_See code: [@oclif/plugin-autocomplete](https://github.com/oclif/plugin-autocomplete/blob/v3.2.6/src/commands/autocomplete/index.ts)_ - -## `fluence build` - -Build all application services, described in fluence.yaml and generate aqua interfaces for them - -``` -USAGE - $ fluence build [--no-input] [--marine-build-args <--flag arg>] [--import ...] [--env ] - -FLAGS - --env= Fluence Environment to use when running the command - --import=... Path to a directory to import aqua files from. May be used several times - --marine-build-args=<--flag arg> Space separated `cargo build` flags and args to pass to marine build. - Overrides 'marineBuildArgs' property in fluence.yaml. Default: --release - --no-input Don't interactively ask for any input from the user - -DESCRIPTION - Build all application services, described in fluence.yaml and generate aqua interfaces for them - -EXAMPLES - $ fluence build -``` - -_See code: [src/commands/build.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/build.ts)_ - -## `fluence chain info` - -Show contract addresses for the fluence environment and accounts for the local environment - -``` -USAGE - $ fluence chain info [--no-input] [--env ] [--priv-key ] - -FLAGS - --env= Fluence Environment to use when running the command - --no-input Don't interactively ask for any input from the user - --priv-key= !WARNING! for debug purposes only. Passing private keys through flags is - unsecure. On local env - 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is used - by default when CLI is used in non-interactive mode - -DESCRIPTION - Show contract addresses for the fluence environment and accounts for the local environment -``` - -_See code: [src/commands/chain/info.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/chain/info.ts)_ - -## `fluence deal change-app [DEAL-ADDRESS] [NEW-APP-CID]` - -Change app id in the deal - -``` -USAGE - $ fluence deal change-app [DEAL-ADDRESS] [NEW-APP-CID] [--no-input] [--env ] - [--priv-key ] - -ARGUMENTS - DEAL-ADDRESS Deal address - NEW-APP-CID New app CID for the deal - -FLAGS - --env= Fluence Environment to use when running the command - --no-input Don't interactively ask for any input from the user - --priv-key= !WARNING! for debug purposes only. Passing private keys through flags is - unsecure. On local env - 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is used - by default when CLI is used in non-interactive mode - -DESCRIPTION - Change app id in the deal -``` - -_See code: [src/commands/deal/change-app.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/deal/change-app.ts)_ - -## `fluence deal create` - -Create your deal with the specified parameters - -``` -USAGE - $ fluence deal create --app-cid --collateral-per-worker --min-workers --target-workers - --max-workers-per-provider --price-per-cu-per-epoch --cu-count-per-worker - [--no-input] [--initial-balance ] [--effectors ] [--whitelist | --blacklist ] - [--protocol-version ] [--env ] [--priv-key ] - -FLAGS - --app-cid= (required) CID of the application that will be deployed - --blacklist= Comma-separated list of blacklisted providers - --collateral-per-worker= (required) Collateral per worker - --cu-count-per-worker= (required) Compute unit count per worker - --effectors= Comma-separated list of effector to be used in the deal - --env= Fluence Environment to use when running the command - --initial-balance= Initial balance - --max-workers-per-provider= (required) Max workers per provider - --min-workers= (required) Required workers to activate the deal - --no-input Don't interactively ask for any input from the user - --price-per-cu-per-epoch= (required) Price per CU per epoch - --priv-key= !WARNING! for debug purposes only. Passing private keys through flags is - unsecure. On local env - 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is used - by default when CLI is used in non-interactive mode - --protocol-version= Protocol version - --target-workers= (required) Max workers in the deal - --whitelist= Comma-separated list of whitelisted providers - -DESCRIPTION - Create your deal with the specified parameters -``` - -_See code: [src/commands/deal/create.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/deal/create.ts)_ - -## `fluence deal deposit [AMOUNT] [DEPLOYMENT-NAMES]` - -Deposit do the deal - -``` -USAGE - $ fluence deal deposit [AMOUNT] [DEPLOYMENT-NAMES] [--no-input] [--env ] - [--priv-key ] [--deal-ids ] - -ARGUMENTS - AMOUNT Amount of USDC tokens to deposit - DEPLOYMENT-NAMES Comma separated names of deployments. Can't be used together with --deal-ids flag - -FLAGS - --deal-ids= Comma-separated deal ids - --env= Fluence Environment to use when running the command - --no-input Don't interactively ask for any input from the user - --priv-key= !WARNING! for debug purposes only. Passing private keys through flags is - unsecure. On local env - 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is used - by default when CLI is used in non-interactive mode - -DESCRIPTION - Deposit do the deal -``` - -_See code: [src/commands/deal/deposit.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/deal/deposit.ts)_ - -## `fluence deal info [DEPLOYMENT-NAMES]` - -Get info about the deal - -``` -USAGE - $ fluence deal info [DEPLOYMENT-NAMES] [--no-input] [--env ] [--priv-key - ] [--deal-ids ] - -ARGUMENTS - DEPLOYMENT-NAMES Comma separated names of deployments. Can't be used together with --deal-ids flag - -FLAGS - --deal-ids= Comma-separated deal ids - --env= Fluence Environment to use when running the command - --no-input Don't interactively ask for any input from the user - --priv-key= !WARNING! for debug purposes only. Passing private keys through flags is - unsecure. On local env - 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is used - by default when CLI is used in non-interactive mode - -DESCRIPTION - Get info about the deal -``` - -_See code: [src/commands/deal/info.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/deal/info.ts)_ - -## `fluence deal logs [DEPLOYMENT-NAMES]` - -Get logs from deployed workers for deals listed in workers.yaml - -``` -USAGE - $ fluence deal logs [DEPLOYMENT-NAMES] [--no-input] [-k ] [--relay ] [--ttl - ] [--dial-timeout ] [--particle-id] [--env ] - [--off-aqua-logs] [--tracing] [--deal-ids ] [--spell ] - -ARGUMENTS - DEPLOYMENT-NAMES Comma separated names of deployments. Can't be used together with --deal-ids flag - -FLAGS - -k, --sk= Name of the secret key for js-client inside CLI to use. If not - specified, will use the default key for the project. If there is no - fluence project or there is no default key, will use user's default key - --deal-ids= Comma-separated deal ids - --dial-timeout= [default: 15000] Timeout for Fluence js-client to connect to relay peer - --env= Fluence Environment to use when running the command - --no-input Don't interactively ask for any input from the user - --off-aqua-logs Turns off logs from Console.print in aqua and from IPFS service - --particle-id Print particle ids when running Fluence js-client - --relay= Relay for Fluence js-client to connect to - --spell= [default: worker-spell] Spell name to get logs for - --tracing Compile aqua in tracing mode (for debugging purposes) - --ttl= [default: 15000] Particle Time To Live since 'now'. After that, - particle is expired and not processed. - -DESCRIPTION - Get logs from deployed workers for deals listed in workers.yaml - -EXAMPLES - $ fluence deal logs -``` - -_See code: [src/commands/deal/logs.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/deal/logs.ts)_ - -## `fluence deal stop [DEPLOYMENT-NAMES]` - -Stop the deal - -``` -USAGE - $ fluence deal stop [DEPLOYMENT-NAMES] [--no-input] [--env ] [--priv-key - ] [--deal-ids ] - -ARGUMENTS - DEPLOYMENT-NAMES Comma separated names of deployments. Can't be used together with --deal-ids flag - -FLAGS - --deal-ids= Comma-separated deal ids - --env= Fluence Environment to use when running the command - --no-input Don't interactively ask for any input from the user - --priv-key= !WARNING! for debug purposes only. Passing private keys through flags is - unsecure. On local env - 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is used - by default when CLI is used in non-interactive mode - -DESCRIPTION - Stop the deal -``` - -_See code: [src/commands/deal/stop.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/deal/stop.ts)_ - -## `fluence deal withdraw [AMOUNT] [DEPLOYMENT-NAMES]` - -Withdraw tokens from the deal - -``` -USAGE - $ fluence deal withdraw [AMOUNT] [DEPLOYMENT-NAMES] [--no-input] [--env ] - [--priv-key ] [--deal-ids ] - -ARGUMENTS - AMOUNT Amount of USDC tokens to withdraw - DEPLOYMENT-NAMES Comma separated names of deployments. Can't be used together with --deal-ids flag - -FLAGS - --deal-ids= Comma-separated deal ids - --env= Fluence Environment to use when running the command - --no-input Don't interactively ask for any input from the user - --priv-key= !WARNING! for debug purposes only. Passing private keys through flags is - unsecure. On local env - 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is used - by default when CLI is used in non-interactive mode - -DESCRIPTION - Withdraw tokens from the deal -``` - -_See code: [src/commands/deal/withdraw.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/deal/withdraw.ts)_ - -## `fluence deal workers-add [DEPLOYMENT-NAMES]` - -Add missing workers to the deal. Alias: fluence deal wa - -``` -USAGE - $ fluence deal workers-add [DEPLOYMENT-NAMES] [--no-input] [--env ] [--priv-key - ] [--deal-ids ] - -ARGUMENTS - DEPLOYMENT-NAMES Comma separated names of deployments. Can't be used together with --deal-ids flag - -FLAGS - --deal-ids= Comma-separated deal ids - --env= Fluence Environment to use when running the command - --no-input Don't interactively ask for any input from the user - --priv-key= !WARNING! for debug purposes only. Passing private keys through flags is - unsecure. On local env - 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is used - by default when CLI is used in non-interactive mode - -DESCRIPTION - Add missing workers to the deal. Alias: fluence deal wa -``` - -_See code: [src/commands/deal/workers-add.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/deal/workers-add.ts)_ - -## `fluence deal workers-remove [WORKER-IDS]` - -Remove unit from the deal. Alias: fluence deal wr - -``` -USAGE - $ fluence deal workers-remove [WORKER-IDS] [--no-input] [--env ] [--priv-key - ] [--deal-id ] [--name ] - -ARGUMENTS - WORKER-IDS Comma-separated compute unit ids. You can get them using 'fluence deal info' command - -FLAGS - --deal-id= Deal id. You can get it using 'fluence deal info' command - --env= Fluence Environment to use when running the command - --name= Name of the deployment from workers.yaml - --no-input Don't interactively ask for any input from the user - --priv-key= !WARNING! for debug purposes only. Passing private keys through flags is - unsecure. On local env - 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is used - by default when CLI is used in non-interactive mode - -DESCRIPTION - Remove unit from the deal. Alias: fluence deal wr -``` - -_See code: [src/commands/deal/workers-remove.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/deal/workers-remove.ts)_ - -## `fluence default env [ENV]` - -Switch default Fluence Environment - -``` -USAGE - $ fluence default env [ENV] [--no-input] - -ARGUMENTS - ENV Fluence Environment to use when running the command - -FLAGS - --no-input Don't interactively ask for any input from the user - -DESCRIPTION - Switch default Fluence Environment - -EXAMPLES - $ fluence default env -``` - -_See code: [src/commands/default/env.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/default/env.ts)_ - -## `fluence default peers [ENV]` - -Print default Fluence network peer addresses - -``` -USAGE - $ fluence default peers [ENV] [--no-input] - -ARGUMENTS - ENV Fluence Environment to use when running the command - -FLAGS - --no-input Don't interactively ask for any input from the user - -DESCRIPTION - Print default Fluence network peer addresses - -EXAMPLES - $ fluence default peers -``` - -_See code: [src/commands/default/peers.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/default/peers.ts)_ - -## `fluence delegator collateral-add [IDS]` - -Add FLT collateral to capacity commitment. Alias: fluence delegator ca - -``` -USAGE - $ fluence delegator collateral-add [IDS] [--no-input] [--env ] [--priv-key - ] - -ARGUMENTS - IDS Comma separated capacity commitment IDs - -FLAGS - --env= Fluence Environment to use when running the command - --no-input Don't interactively ask for any input from the user - --priv-key= !WARNING! for debug purposes only. Passing private keys through flags is - unsecure. On local env - 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is used - by default when CLI is used in non-interactive mode - -DESCRIPTION - Add FLT collateral to capacity commitment. Alias: fluence delegator ca -``` - -_See code: [src/commands/delegator/collateral-add.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/delegator/collateral-add.ts)_ - -## `fluence delegator collateral-withdraw [IDS]` - -Withdraw FLT collateral from capacity commitment. Alias: fluence delegator cw - -``` -USAGE - $ fluence delegator collateral-withdraw [IDS] [--no-input] [--env ] [--priv-key ] - [--finish] - -ARGUMENTS - IDS Comma separated capacity commitment IDs - -FLAGS - --env= Fluence Environment to use when running the command - --finish Finish capacity commitment after collateral withdrawal - --no-input Don't interactively ask for any input from the user - --priv-key= !WARNING! for debug purposes only. Passing private keys through flags is - unsecure. On local env - 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is used - by default when CLI is used in non-interactive mode - -DESCRIPTION - Withdraw FLT collateral from capacity commitment. Alias: fluence delegator cw -``` - -_See code: [src/commands/delegator/collateral-withdraw.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/delegator/collateral-withdraw.ts)_ - -## `fluence delegator reward-withdraw [IDS]` - -Withdraw FLT rewards from capacity commitment. Alias: fluence delegator rw - -``` -USAGE - $ fluence delegator reward-withdraw [IDS] [--no-input] [--env ] [--priv-key - ] - -ARGUMENTS - IDS Comma separated capacity commitment IDs - -FLAGS - --env= Fluence Environment to use when running the command - --no-input Don't interactively ask for any input from the user - --priv-key= !WARNING! for debug purposes only. Passing private keys through flags is - unsecure. On local env - 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is used - by default when CLI is used in non-interactive mode - -DESCRIPTION - Withdraw FLT rewards from capacity commitment. Alias: fluence delegator rw -``` - -_See code: [src/commands/delegator/reward-withdraw.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/delegator/reward-withdraw.ts)_ - -## `fluence dep install [PACKAGE-NAME | PACKAGE-NAME@VERSION]` - -Install aqua project dependencies (currently npm is used under the hood for managing aqua dependencies). Alias: fluence dep i - -``` -USAGE - $ fluence dep install [PACKAGE-NAME | PACKAGE-NAME@VERSION] [--no-input] - -ARGUMENTS - PACKAGE-NAME | PACKAGE-NAME@VERSION Valid argument for npm install command. If this argument is omitted all project - aqua dependencies will be installed and command will also make sure marine and - mrepl are installed - -FLAGS - --no-input Don't interactively ask for any input from the user - -DESCRIPTION - Install aqua project dependencies (currently npm is used under the hood for managing aqua dependencies). Alias: - fluence dep i - -EXAMPLES - $ fluence dep install -``` - -_See code: [src/commands/dep/install.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/dep/install.ts)_ - -## `fluence dep reset` - -Reset all project dependencies to recommended versions. Alias: fluence dep r - -``` -USAGE - $ fluence dep reset [--no-input] - -FLAGS - --no-input Don't interactively ask for any input from the user - -DESCRIPTION - Reset all project dependencies to recommended versions. Alias: fluence dep r - -EXAMPLES - $ fluence dep reset -``` - -_See code: [src/commands/dep/reset.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/dep/reset.ts)_ - -## `fluence dep uninstall PACKAGE-NAME` - -Uninstall aqua project dependencies (currently npm is used under the hood for managing aqua dependencies). Alias: fluence dep un - -``` -USAGE - $ fluence dep uninstall PACKAGE-NAME [--no-input] - -ARGUMENTS - PACKAGE-NAME Aqua dependency name - -FLAGS - --no-input Don't interactively ask for any input from the user - -DESCRIPTION - Uninstall aqua project dependencies (currently npm is used under the hood for managing aqua dependencies). Alias: - fluence dep un - -EXAMPLES - $ fluence dep uninstall -``` - -_See code: [src/commands/dep/uninstall.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/dep/uninstall.ts)_ - -## `fluence dep versions` - -Get versions of all cli dependencies, including aqua, marine, mrepl and internal. Alias: fluence dep v - -``` -USAGE - $ fluence dep versions [--no-input] [--default] [--json] - -FLAGS - --default Display default npm and cargo dependencies and their versions for current CLI version. Default npm - dependencies are always available to be imported in Aqua - --json Output JSON - --no-input Don't interactively ask for any input from the user - -DESCRIPTION - Get versions of all cli dependencies, including aqua, marine, mrepl and internal. Alias: fluence dep v - -EXAMPLES - $ fluence dep versions -``` - -_See code: [src/commands/dep/versions.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/dep/versions.ts)_ - -## `fluence deploy [DEPLOYMENT-NAMES]` - -Deploy according to 'deployments' property in fluence.yaml - -``` -USAGE - $ fluence deploy [DEPLOYMENT-NAMES] [--no-input] [--env ] [--priv-key - ] [--import ...] [--no-build] [--marine-build-args <--flag arg>] [-u] [--peer-ids ] - -ARGUMENTS - DEPLOYMENT-NAMES Comma separated names of deployments. Can't be used together with --deal-ids flag - -FLAGS - -u, --update Update your previous deployment - --env= Fluence Environment to use when running the command - --import=... Path to a directory to import aqua files from. May be used several - times - --marine-build-args=<--flag arg> Space separated `cargo build` flags and args to pass to marine build. - Overrides 'marineBuildArgs' property in fluence.yaml. Default: - --release - --no-build Don't build the project before running the command - --no-input Don't interactively ask for any input from the user - --peer-ids= Comma separated list of peer ids to deploy to. Creates 1 worker for - each peer with 'cuCountPerWorker' number of compute units - --priv-key= !WARNING! for debug purposes only. Passing private keys through flags - is unsecure. On local env - 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is - used by default when CLI is used in non-interactive mode - -DESCRIPTION - Deploy according to 'deployments' property in fluence.yaml - -EXAMPLES - $ fluence deploy -``` - -_See code: [src/commands/deploy.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/deploy.ts)_ - -## `fluence help [COMMAND]` - -Display help for fluence. - -``` -USAGE - $ fluence help [COMMAND...] [-n] - -ARGUMENTS - COMMAND... Command to show help for. - -FLAGS - -n, --nested-commands Include all nested commands in the output. - -DESCRIPTION - Display help for fluence. -``` - -_See code: [@oclif/plugin-help](https://github.com/oclif/plugin-help/blob/v6.2.15/src/commands/help.ts)_ - -## `fluence init [PATH]` +## `fluence autocomplete [SHELL]` -Initialize fluence project +Display autocomplete installation instructions. ``` USAGE - $ fluence init [PATH] [--no-input] [-t ] [--env ] [--noxes - ] + $ fluence autocomplete [SHELL] [-r] ARGUMENTS - PATH Project path + SHELL (zsh|bash|powershell) Shell type FLAGS - -t, --template= Template to use for the project. One of: quickstart, minimal, ts, js - --env= Fluence Environment to use when running the command - --no-input Don't interactively ask for any input from the user - --noxes= Number of Compute Peers to generate when a new provider.yaml is created + -r, --refresh-cache Refresh cache (ignores displaying instructions) DESCRIPTION - Initialize fluence project + Display autocomplete installation instructions. EXAMPLES - $ fluence init + $ fluence autocomplete + + $ fluence autocomplete bash + + $ fluence autocomplete zsh + + $ fluence autocomplete powershell + + $ fluence autocomplete --refresh-cache ``` -_See code: [src/commands/init.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/init.ts)_ +_See code: [@oclif/plugin-autocomplete](https://github.com/oclif/plugin-autocomplete/blob/v3.2.6/src/commands/autocomplete/index.ts)_ -## `fluence key default [NAME]` +## `fluence chain info` -Set default key-pair for user or project +Show contract addresses for the fluence environment and accounts for the local environment ``` USAGE - $ fluence key default [NAME] [--no-input] [--user] - -ARGUMENTS - NAME Key-pair name + $ fluence chain info [--no-input] [--env ] [--priv-key ] FLAGS - --no-input Don't interactively ask for any input from the user - --user Set default key-pair for current user instead of current project + --env= Fluence Environment to use when running the command + --no-input Don't interactively ask for any input from the user + --priv-key= !WARNING! for debug purposes only. Passing private keys through flags is + unsecure. On local env + 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is used + by default when CLI is used in non-interactive mode DESCRIPTION - Set default key-pair for user or project - -EXAMPLES - $ fluence key default + Show contract addresses for the fluence environment and accounts for the local environment ``` -_See code: [src/commands/key/default.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/key/default.ts)_ +_See code: [src/commands/chain/info.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/chain/info.ts)_ -## `fluence key new [NAME]` +## `fluence default env [ENV]` -Generate key-pair and store it in user-secrets.yaml or project-secrets.yaml +Switch default Fluence Environment ``` USAGE - $ fluence key new [NAME] [--no-input] [--user] [--default] + $ fluence default env [ENV] [--no-input] ARGUMENTS - NAME Key-pair name + ENV Fluence Environment to use when running the command FLAGS - --default Set new key-pair as default for current project or user --no-input Don't interactively ask for any input from the user - --user Generate key-pair for current user instead of generating key-pair for current project DESCRIPTION - Generate key-pair and store it in user-secrets.yaml or project-secrets.yaml + Switch default Fluence Environment EXAMPLES - $ fluence key new + $ fluence default env ``` -_See code: [src/commands/key/new.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/key/new.ts)_ +_See code: [src/commands/default/env.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/default/env.ts)_ -## `fluence key remove [NAME]` +## `fluence help [COMMAND]` -Remove key-pair from user-secrets.yaml or project-secrets.yaml +Display help for fluence. ``` USAGE - $ fluence key remove [NAME] [--no-input] [--user] + $ fluence help [COMMAND...] [-n] ARGUMENTS - NAME Key-pair name + COMMAND... Command to show help for. FLAGS - --no-input Don't interactively ask for any input from the user - --user Remove key-pair from current user instead of removing key-pair from current project + -n, --nested-commands Include all nested commands in the output. DESCRIPTION - Remove key-pair from user-secrets.yaml or project-secrets.yaml - -EXAMPLES - $ fluence key remove + Display help for fluence. ``` -_See code: [src/commands/key/remove.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/key/remove.ts)_ +_See code: [@oclif/plugin-help](https://github.com/oclif/plugin-help/blob/v6.2.15/src/commands/help.ts)_ ## `fluence local down` @@ -1023,7 +225,7 @@ Run docker-compose.yaml using docker compose and set up provider using all the o ``` USAGE - $ fluence local up [--no-input] [--noxes ] [--timeout ] [--priv-key ] + $ fluence local up [--no-input] [--peers ] [--timeout ] [--priv-key ] [--quiet-pull] [-d] [--build] [--flags <--flag arg>] [-r] [--no-wait] [--no-set-up] FLAGS @@ -1036,7 +238,7 @@ FLAGS --no-set-up Don't set up provider, offer, commitments and deposit collateral, so there will be no active offer on the network after command is finished --no-wait Don't wait for services to be running|healthy - --noxes= Number of Compute Peers to generate when a new provider.yaml is created + --peers= Number of peers to generate when a new provider.yaml is created --priv-key= !WARNING! for debug purposes only. Passing private keys through flags is unsecure. On local env 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is used by default when CLI is used in non-interactive mode @@ -1054,134 +256,6 @@ EXAMPLES _See code: [src/commands/local/up.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/local/up.ts)_ -## `fluence module add [PATH | URL]` - -Add module to service.yaml - -``` -USAGE - $ fluence module add [PATH | URL] [--no-input] [--name ] [--service ] - -ARGUMENTS - PATH | URL Path to a module or url to .tar.gz archive - -FLAGS - --name= Override module name - --no-input Don't interactively ask for any input from the user - --service= Service name from fluence.yaml or path to the service config or directory that contains - service.yaml - -DESCRIPTION - Add module to service.yaml - -EXAMPLES - $ fluence module add -``` - -_See code: [src/commands/module/add.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/module/add.ts)_ - -## `fluence module build [PATH]` - -Build module - -``` -USAGE - $ fluence module build [PATH] [--no-input] [--marine-build-args <--flag arg>] - -ARGUMENTS - PATH Path to a module - -FLAGS - --marine-build-args=<--flag arg> Space separated `cargo build` flags and args to pass to marine build. Overrides - 'marineBuildArgs' property in fluence.yaml. Default: --release - --no-input Don't interactively ask for any input from the user - -DESCRIPTION - Build module - -EXAMPLES - $ fluence module build -``` - -_See code: [src/commands/module/build.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/module/build.ts)_ - -## `fluence module new [NAME]` - -Create new marine module template - -``` -USAGE - $ fluence module new [NAME] [--no-input] [--path ] [--service ] - -ARGUMENTS - NAME Module name - -FLAGS - --no-input Don't interactively ask for any input from the user - --path= Path to module dir (default: src/modules) - --service= Name or relative path to the service to add the created module to - -DESCRIPTION - Create new marine module template - -EXAMPLES - $ fluence module new -``` - -_See code: [src/commands/module/new.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/module/new.ts)_ - -## `fluence module pack [PATH]` - -Pack module into tar.gz archive - -``` -USAGE - $ fluence module pack [PATH] [--no-input] [--marine-build-args <--flag arg>] [-d ] [-b ] - -ARGUMENTS - PATH Path to a module - -FLAGS - -b, --binding-crate= Path to a directory with rust binding crate - -d, --destination= Path to a directory where you want archive to be saved. Default: current - directory - --marine-build-args=<--flag arg> Space separated `cargo build` flags and args to pass to marine build. Overrides - 'marineBuildArgs' property in fluence.yaml. Default: --release - --no-input Don't interactively ask for any input from the user - -DESCRIPTION - Pack module into tar.gz archive - -EXAMPLES - $ fluence module pack -``` - -_See code: [src/commands/module/pack.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/module/pack.ts)_ - -## `fluence module remove [NAME | PATH | URL]` - -Remove module from service.yaml - -``` -USAGE - $ fluence module remove [NAME | PATH | URL] [--no-input] [--service ] - -ARGUMENTS - NAME | PATH | URL Module name from service.yaml, path to a module or url to .tar.gz archive - -FLAGS - --no-input Don't interactively ask for any input from the user - --service= Service name from fluence.yaml or path to the service directory - -DESCRIPTION - Remove module from service.yaml - -EXAMPLES - $ fluence module remove -``` - -_See code: [src/commands/module/remove.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/module/remove.ts)_ - ## `fluence provider cc-activate` Add FLT collateral to capacity commitment to activate it. Alias: fluence provider ca @@ -1189,16 +263,16 @@ Add FLT collateral to capacity commitment to activate it. Alias: fluence provide ``` USAGE $ fluence provider cc-activate [--no-input] [--env ] [--priv-key ] - [--nox-names | --cc-ids ] [--offers ] + [--peer-names | --cc-ids ] [--offers ] FLAGS --cc-ids= Comma separated capacity commitment IDs --env= Fluence Environment to use when running the command --no-input Don't interactively ask for any input from the user - --nox-names= Comma-separated names of noxes from provider.yaml. To use all of your - noxes: --nox-names all --offers= Comma-separated list of offer names. To use all of your offers: --offers all + --peer-names= Comma-separated names of peers from provider.yaml. To use all of your + peers: --peer-names all --priv-key= !WARNING! for debug purposes only. Passing private keys through flags is unsecure. On local env 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is used @@ -1217,15 +291,15 @@ Create Capacity commitment. Alias: fluence provider cc ``` USAGE $ fluence provider cc-create [--no-input] [--env ] [--priv-key ] - [--nox-names ] [--offers ] + [--peer-names ] [--offers ] FLAGS --env= Fluence Environment to use when running the command --no-input Don't interactively ask for any input from the user - --nox-names= Comma-separated names of noxes from provider.yaml. To use all of your - noxes: --nox-names all --offers= Comma-separated list of offer names. To use all of your offers: --offers all + --peer-names= Comma-separated names of peers from provider.yaml. To use all of your + peers: --peer-names all --priv-key= !WARNING! for debug purposes only. Passing private keys through flags is unsecure. On local env 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is used @@ -1243,17 +317,17 @@ Move resources from deals, withdraw FLT collateral from capacity commitments, re ``` USAGE - $ fluence provider cc-finish [--no-input] [--nox-names | --cc-ids ] [--offers ] - [--env ] [--priv-key ] + $ fluence provider cc-finish [--no-input] [--peer-names | --cc-ids ] [--offers + ] [--env ] [--priv-key ] FLAGS --cc-ids= Comma separated capacity commitment IDs --env= Fluence Environment to use when running the command --no-input Don't interactively ask for any input from the user - --nox-names= Comma-separated names of noxes from provider.yaml. To use all of your - noxes: --nox-names all --offers= Comma-separated list of offer names. To use all of your offers: --offers all + --peer-names= Comma-separated names of peers from provider.yaml. To use all of your + peers: --peer-names all --priv-key= !WARNING! for debug purposes only. Passing private keys through flags is unsecure. On local env 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is used @@ -1273,17 +347,17 @@ Get info about capacity commitments. Alias: fluence provider ci ``` USAGE $ fluence provider cc-info [--no-input] [--env ] [--priv-key ] - [--nox-names | --cc-ids ] [--offers ] [--json] + [--peer-names | --cc-ids ] [--offers ] [--json] FLAGS --cc-ids= Comma separated capacity commitment IDs --env= Fluence Environment to use when running the command --json Output JSON --no-input Don't interactively ask for any input from the user - --nox-names= Comma-separated names of noxes from provider.yaml. To use all of your - noxes: --nox-names all --offers= Comma-separated list of offer names. To use all of your offers: --offers all + --peer-names= Comma-separated names of peers from provider.yaml. To use all of your + peers: --peer-names all --priv-key= !WARNING! for debug purposes only. Passing private keys through flags is unsecure. On local env 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is used @@ -1302,16 +376,16 @@ Remove Capacity commitment. You can remove it only BEFORE you activated it by de ``` USAGE $ fluence provider cc-remove [--no-input] [--env ] [--priv-key ] - [--nox-names | --cc-ids ] [--offers ] + [--peer-names | --cc-ids ] [--offers ] FLAGS --cc-ids= Comma separated capacity commitment IDs --env= Fluence Environment to use when running the command --no-input Don't interactively ask for any input from the user - --nox-names= Comma-separated names of noxes from provider.yaml. To use all of your - noxes: --nox-names all --offers= Comma-separated list of offer names. To use all of your offers: --offers all + --peer-names= Comma-separated names of peers from provider.yaml. To use all of your + peers: --peer-names all --priv-key= !WARNING! for debug purposes only. Passing private keys through flags is unsecure. On local env 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is used @@ -1330,17 +404,17 @@ Withdraw FLT rewards from capacity commitments. Alias: fluence provider crw ``` USAGE - $ fluence provider cc-rewards-withdraw [--no-input] [--nox-names | --cc-ids ] [--offers ] - [--env ] [--priv-key ] + $ fluence provider cc-rewards-withdraw [--no-input] [--peer-names | --cc-ids ] [--offers + ] [--env ] [--priv-key ] FLAGS --cc-ids= Comma separated capacity commitment IDs --env= Fluence Environment to use when running the command --no-input Don't interactively ask for any input from the user - --nox-names= Comma-separated names of noxes from provider.yaml. To use all of your - noxes: --nox-names all --offers= Comma-separated list of offer names. To use all of your offers: --offers all + --peer-names= Comma-separated names of peers from provider.yaml. To use all of your + peers: --peer-names all --priv-key= !WARNING! for debug purposes only. Passing private keys through flags is unsecure. On local env 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is used @@ -1449,6 +523,36 @@ DESCRIPTION _See code: [src/commands/provider/deal-rewards-withdraw.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/provider/deal-rewards-withdraw.ts)_ +## `fluence provider deploy` + +Deploy manifests + +``` +USAGE + $ fluence provider deploy [--no-input] [--env ] [--priv-key ] + [--peer-names ] [--offers ] + +FLAGS + --env= Fluence Environment to use when running the command + --no-input Don't interactively ask for any input from the user + --offers= Comma-separated list of offer names. To use all of your offers: --offers + all + --peer-names= Comma-separated names of peers from provider.yaml. To use all of your + peers: --peer-names all + --priv-key= !WARNING! for debug purposes only. Passing private keys through flags is + unsecure. On local env + 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is used + by default when CLI is used in non-interactive mode + +DESCRIPTION + Deploy manifests + +EXAMPLES + $ fluence provider deploy +``` + +_See code: [src/commands/provider/deploy.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/provider/deploy.ts)_ + ## `fluence provider gen` Generate Config.toml files according to provider.yaml and secrets according to provider-secrets.yaml @@ -1456,18 +560,18 @@ Generate Config.toml files according to provider.yaml and secrets according to p ``` USAGE $ fluence provider gen [--no-input] [--env ] [--priv-key ] - [--reset-nox-secrets] [--no-withdraw] + [--reset-peer-secrets] [--no-withdraw] FLAGS --env= Fluence Environment to use when running the command --no-input Don't interactively ask for any input from the user - --no-withdraw Is used only when --reset-nox-secrets flag is present. Will not withdraw - tokens from noxes (if you don't need it or it fails for some reason) + --no-withdraw Is used only when --reset-peer-secrets flag is present. Will not withdraw + tokens from peers (if you don't need it or it fails for some reason) --priv-key= !WARNING! for debug purposes only. Passing private keys through flags is unsecure. On local env 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is used by default when CLI is used in non-interactive mode - --reset-nox-secrets Withdraw remaining tokens from your noxes, backup nox secrets from + --reset-peer-secrets Withdraw remaining tokens from your peers, backup peer secrets from .fluence/provider-secrets.yaml and .fluence/secrets (if they exist) to .fluence/backups and generate new ones @@ -1482,27 +586,29 @@ _See code: [src/commands/provider/gen.ts](https://github.com/fluencelabs/cli/blo ## `fluence provider info` -Print nox signing wallets and peer ids. Alias: fluence provider i +Print peer signing wallets and peer ids. Alias: fluence provider i ``` USAGE $ fluence provider info [--no-input] [--env ] [--priv-key ] - [--nox-names ] [--json] [--address
] + [--peer-names ] [--offers ] [--json] [--address
] FLAGS --address=
Provider address --env= Fluence Environment to use when running the command --json Output JSON --no-input Don't interactively ask for any input from the user - --nox-names= Comma-separated names of noxes from provider.yaml. To use all of your - noxes: --nox-names all + --offers= Comma-separated list of offer names. To use all of your offers: --offers + all + --peer-names= Comma-separated names of peers from provider.yaml. To use all of your + peers: --peer-names all --priv-key= !WARNING! for debug purposes only. Passing private keys through flags is unsecure. On local env 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is used by default when CLI is used in non-interactive mode DESCRIPTION - Print nox signing wallets and peer ids. Alias: fluence provider i + Print peer signing wallets and peer ids. Alias: fluence provider i ``` _See code: [src/commands/provider/info.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/provider/info.ts)_ @@ -1513,14 +619,13 @@ Init provider config. Creates a provider.yaml file ``` USAGE - $ fluence provider init [--no-input] [--noxes ] [--env ] [--priv-key - ] [--no-vm] + $ fluence provider init [--no-input] [--peers ] [--env ] [--priv-key + ] FLAGS --env= Fluence Environment to use when running the command --no-input Don't interactively ask for any input from the user - --no-vm Generate provider.yaml without vm configuration - --noxes= Number of Compute Peers to generate when a new provider.yaml is created + --peers= Number of peers to generate when a new provider.yaml is created --priv-key= !WARNING! for debug purposes only. Passing private keys through flags is unsecure. On local env 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is used @@ -1660,55 +765,57 @@ _See code: [src/commands/provider/register.ts](https://github.com/fluencelabs/cl ## `fluence provider tokens-distribute` -Distribute FLT tokens to noxes. Alias: fluence provider td +Distribute FLT tokens to peers. Alias: fluence provider td ``` USAGE $ fluence provider tokens-distribute [--no-input] [--env ] [--priv-key ] - [--nox-names ] [--offers ] [--amount ] + [--peer-names ] [--offers ] [--amount ] FLAGS - --amount= Amount of FLT tokens to distribute to noxes + --amount= Amount of FLT tokens to distribute to peers --env= Fluence Environment to use when running the command --no-input Don't interactively ask for any input from the user - --nox-names= Comma-separated names of noxes from provider.yaml. To use all of your - noxes: --nox-names all --offers= Comma-separated list of offer names. To use all of your offers: --offers all + --peer-names= Comma-separated names of peers from provider.yaml. To use all of your + peers: --peer-names all --priv-key= !WARNING! for debug purposes only. Passing private keys through flags is unsecure. On local env 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is used by default when CLI is used in non-interactive mode DESCRIPTION - Distribute FLT tokens to noxes. Alias: fluence provider td + Distribute FLT tokens to peers. Alias: fluence provider td ``` _See code: [src/commands/provider/tokens-distribute.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/provider/tokens-distribute.ts)_ ## `fluence provider tokens-withdraw` -Withdraw FLT tokens from noxes. Alias: fluence provider tw +Withdraw FLT tokens from peers. Alias: fluence provider tw ``` USAGE $ fluence provider tokens-withdraw [--no-input] [--env ] [--priv-key ] - [--nox-names ] [--amount ] + [--peer-names ] [--offers ] [--amount ] FLAGS - --amount= Amount of FLT tokens to withdraw from noxes. Use --amount max to withdraw + --amount= Amount of FLT tokens to withdraw from peers. Use --amount max to withdraw maximum possible amount --env= Fluence Environment to use when running the command --no-input Don't interactively ask for any input from the user - --nox-names= Comma-separated names of noxes from provider.yaml. To use all of your - noxes: --nox-names all + --offers= Comma-separated list of offer names. To use all of your offers: --offers + all + --peer-names= Comma-separated names of peers from provider.yaml. To use all of your + peers: --peer-names all --priv-key= !WARNING! for debug purposes only. Passing private keys through flags is unsecure. On local env 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 is used by default when CLI is used in non-interactive mode DESCRIPTION - Withdraw FLT tokens from noxes. Alias: fluence provider tw + Withdraw FLT tokens from peers. Alias: fluence provider tw ``` _See code: [src/commands/provider/tokens-withdraw.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/provider/tokens-withdraw.ts)_ @@ -1735,210 +842,6 @@ DESCRIPTION _See code: [src/commands/provider/update.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/provider/update.ts)_ -## `fluence run` - -Run the first aqua function CLI is able to find and compile among all aqua files specified in 'compileAqua' property of fluence.yaml file. If --input flag is used - then content of 'compileAqua' property in fluence.yaml will be ignored - -``` -USAGE - $ fluence run [--no-input] [--data ] [--data-path ] [--quiet] [-f ] - [--print-air | -b] [--off-aqua-logs] [-k ] [--relay ] [--ttl ] [--dial-timeout - ] [--particle-id] [--env ] [--import ...] [-i ] - [--const ...] [--log-level-compiler ] [--no-relay] [--no-xor] [--tracing] [--no-empty-response] - -FLAGS - -b, --print-beautified-air Prints beautified AIR code instead of function execution - -f, --func= Function call. Example: funcName("stringArg") - -i, --input= Path to an aqua file or a directory that contains your aqua files - -k, --sk= Name of the secret key for js-client inside CLI to use. If not - specified, will use the default key for the project. If there is no - fluence project or there is no default key, will use user's default key - --const=... Constants to be passed to the compiler - --data= JSON in { [argumentName]: argumentValue } format. You can call a - function using these argument names like this: -f - 'myFunc(argumentName)'. Arguments in this flag override arguments in - the --data-path flag - --data-path= Path to a JSON file in { [argumentName]: argumentValue } format. You - can call a function using these argument names like this: -f - 'myFunc(argumentName)'. Arguments in this flag can be overridden using - --data flag - --dial-timeout= [default: 15000] Timeout for Fluence js-client to connect to relay peer - --env= Fluence Environment to use when running the command - --import=... Path to a directory to import aqua files from. May be used several - times - --log-level-compiler= Set log level for the compiler. Must be one of: all, trace, debug, - info, warn, error, off - --no-empty-response Do not generate response call if there are no returned values - --no-input Don't interactively ask for any input from the user - --no-relay Do not generate a pass through the relay node - --no-xor Do not generate a wrapper that catches and displays errors - --off-aqua-logs Turns off logs from Console.print in aqua and from IPFS service - --particle-id Print particle ids when running Fluence js-client - --print-air Prints generated AIR code instead of function execution - --quiet Print only execution result. Overrides all --log-level-* flags - --relay= Relay for Fluence js-client to connect to - --tracing Compile aqua in tracing mode (for debugging purposes) - --ttl= [default: 15000] Particle Time To Live since 'now'. After that, - particle is expired and not processed. - -DESCRIPTION - Run the first aqua function CLI is able to find and compile among all aqua files specified in 'compileAqua' property - of fluence.yaml file. If --input flag is used - then content of 'compileAqua' property in fluence.yaml will be ignored - -EXAMPLES - $ fluence run -f 'funcName("stringArg")' -``` - -_See code: [src/commands/run.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/run.ts)_ - -## `fluence service add [PATH | URL]` - -Add service to fluence.yaml - -``` -USAGE - $ fluence service add [PATH | URL] [--no-input] [--name ] [--marine-build-args <--flag arg>] - -ARGUMENTS - PATH | URL Path to a service or url to .tar.gz archive - -FLAGS - --marine-build-args=<--flag arg> Space separated `cargo build` flags and args to pass to marine build. Overrides - 'marineBuildArgs' property in fluence.yaml. Default: --release - --name= Override service name (must start with a lowercase letter and contain only letters, - numbers, and underscores) - --no-input Don't interactively ask for any input from the user - -DESCRIPTION - Add service to fluence.yaml - -EXAMPLES - $ fluence service add -``` - -_See code: [src/commands/service/add.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/service/add.ts)_ - -## `fluence service new [NAME]` - -Create new marine service template - -``` -USAGE - $ fluence service new [NAME] [--no-input] [--path ] - -ARGUMENTS - NAME Unique service name (must start with a lowercase letter and contain only letters, numbers, and underscores) - -FLAGS - --no-input Don't interactively ask for any input from the user - --path= Path to services dir (default: src/services) - -DESCRIPTION - Create new marine service template - -EXAMPLES - $ fluence service new -``` - -_See code: [src/commands/service/new.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/service/new.ts)_ - -## `fluence service remove [NAME | PATH | URL]` - -Remove service from fluence.yaml services property and from all of the workers - -``` -USAGE - $ fluence service remove [NAME | PATH | URL] [--no-input] - -ARGUMENTS - NAME | PATH | URL Service name from fluence.yaml, path to a service or url to .tar.gz archive - -FLAGS - --no-input Don't interactively ask for any input from the user - -DESCRIPTION - Remove service from fluence.yaml services property and from all of the workers - -EXAMPLES - $ fluence service remove -``` - -_See code: [src/commands/service/remove.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/service/remove.ts)_ - -## `fluence service repl [NAME | PATH | URL]` - -Open service inside repl (downloads and builds modules if necessary) - -``` -USAGE - $ fluence service repl [NAME | PATH | URL] [--no-input] [--marine-build-args <--flag arg>] - -ARGUMENTS - NAME | PATH | URL Service name from fluence.yaml, path to a service or url to .tar.gz archive - -FLAGS - --marine-build-args=<--flag arg> Space separated `cargo build` flags and args to pass to marine build. Overrides - 'marineBuildArgs' property in fluence.yaml. Default: --release - --no-input Don't interactively ask for any input from the user - -DESCRIPTION - Open service inside repl (downloads and builds modules if necessary) - -EXAMPLES - $ fluence service repl -``` - -_See code: [src/commands/service/repl.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/service/repl.ts)_ - -## `fluence spell build [SPELL-NAMES]` - -Check spells aqua is able to compile without any errors - -``` -USAGE - $ fluence spell build [SPELL-NAMES] [--no-input] [--import ...] - -ARGUMENTS - SPELL-NAMES Comma separated names of spells to build. Example: "spell1,spell2" (by default all spells from 'spells' - property in fluence.yaml will be built) - -FLAGS - --import=... Path to a directory to import aqua files from. May be used several times - --no-input Don't interactively ask for any input from the user - -DESCRIPTION - Check spells aqua is able to compile without any errors - -EXAMPLES - $ fluence spell build -``` - -_See code: [src/commands/spell/build.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/spell/build.ts)_ - -## `fluence spell new [NAME]` - -Create a new spell template - -``` -USAGE - $ fluence spell new [NAME] [--no-input] [--path ] - -ARGUMENTS - NAME Spell name - -FLAGS - --no-input Don't interactively ask for any input from the user - --path= Path to spells dir (default: src/spells) - -DESCRIPTION - Create a new spell template - -EXAMPLES - $ fluence spell new -``` - -_See code: [src/commands/spell/new.ts](https://github.com/fluencelabs/cli/blob/fluence-cli-v0.22.0/src/commands/spell/new.ts)_ - ## `fluence update [CHANNEL]` update the fluence CLI diff --git a/packages/cli/package/docs/configs/README.md b/packages/cli/package/docs/configs/README.md index 9d0a035f9..6c4c70e0d 100644 --- a/packages/cli/package/docs/configs/README.md +++ b/packages/cli/package/docs/configs/README.md @@ -1,9 +1,5 @@ # Fluence CLI Configs -## [fluence.yaml](./fluence.md) - -Defines Fluence Project, most importantly - what exactly you want to deploy and how. You can use `fluence init` command to generate a template for new Fluence project - ## [provider.yaml](./provider.md) Defines config used for provider set up @@ -12,22 +8,6 @@ Defines config used for provider set up Defines secrets config used for provider set up -## [module.yaml](./module.md) - -Defines Marine Module. You can use `fluence module new` command to generate a template for new module - -## [service.yaml](./service.md) - -Defines a [Marine service](https://fluence.dev/docs/build/concepts/#services), most importantly the modules that the service consists of. You can use `fluence service new` command to generate a template for new service - -## [spell.yaml](./spell.md) - -Defines a spell. You can use `fluence spell new` command to generate a template for new spell - -## [workers.yaml](./workers.md) - -A result of app deployment. This file is created automatically after successful deployment using `fluence workers deploy` command - ## [config.yaml](./config.md) Defines global config for Fluence CLI diff --git a/packages/cli/package/docs/configs/config.md b/packages/cli/package/docs/configs/config.md index 48988998b..264e7b90f 100644 --- a/packages/cli/package/docs/configs/config.md +++ b/packages/cli/package/docs/configs/config.md @@ -4,9 +4,9 @@ Defines global config for Fluence CLI ## Properties -| Property | Type | Required | Description | -|------------------------|---------|----------|--------------------------------------------------------------------------------------------| -| `countlyConsent` | boolean | **Yes** | Weather you consent to send usage data to Countly | -| `defaultSecretKeyName` | string | **Yes** | Secret key with this name will be used by default by js-client inside CLI to run Aqua code | -| `version` | integer | **Yes** | Config version | +| Property | Type | Required | Description | +|------------------------|---------|----------|--------------------------------------------------------------------------------------------------------| +| `countlyConsent` | boolean | **Yes** | Weather you consent to send usage data to Countly | +| `version` | integer | **Yes** | Config version | +| `defaultSecretKeyName` | string | No | DEPRECATED: Secret key with this name will be used by default by js-client inside CLI to run Aqua code | diff --git a/packages/cli/package/docs/configs/env.md b/packages/cli/package/docs/configs/env.md index 05ae067ab..4c4994e88 100644 --- a/packages/cli/package/docs/configs/env.md +++ b/packages/cli/package/docs/configs/env.md @@ -11,6 +11,7 @@ Defines project user's preferences | `chainId` | number | No | Chain ID to use | | `deployment` | [object](#deployment) | No | Deployed contract address overrides | | `fluenceEnv` | string | No | Fluence environment to connect to Possible values are: `testnet`, `mainnet`, `stage`, `local`. | +| `ipfsGateway` | string | No | IPFS gateway URL to use | | `relays` | string[] | No | List of custom relay multiaddresses to use when connecting to Fluence network | | `rpcUrl` | string | No | RPC URL to use | | `subgraphUrl` | string | No | Subgraph URL to use | diff --git a/packages/cli/package/docs/configs/fluence.md b/packages/cli/package/docs/configs/fluence.md deleted file mode 100644 index d9af60f7b..000000000 --- a/packages/cli/package/docs/configs/fluence.md +++ /dev/null @@ -1,257 +0,0 @@ -# fluence.yaml - -Defines Fluence Project, most importantly - what exactly you want to deploy and how. You can use `fluence init` command to generate a template for new Fluence project - -## Properties - -| Property | Type | Required | Description | -|------------------------|-----------------------------|----------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `aquaDependencies` | [object](#aquadependencies) | **Yes** | A map of npm aqua dependency versions | -| `version` | integer | **Yes** | | -| `aquaImports` | string[] | No | A list of path to be considered by aqua compiler to be used as imports. First dependency in the list has the highest priority. Priority of imports is considered in the following order: imports from --import flags, imports from aquaImports property in fluence.yaml, project's .fluence/aqua dir, npm dependencies from fluence.yaml, npm dependencies from user's .fluence/config.yaml, npm dependencies recommended by fluence | -| `cliVersion` | string | No | The version of the Fluence CLI that is compatible with this project. Set this to enforce a particular set of versions of all fluence components | -| `compileAqua` | [object](#compileaqua) | No | A map of aqua files to compile | -| `customFluenceEnv` | [object](#customfluenceenv) | No | Custom Fluence environment to use when connecting to Fluence network | -| `defaultSecretKeyName` | string | No | Secret key with this name will be used by default by js-client inside CLI to run Aqua code | -| `deployments` | [object](#deployments) | No | A map with deployment names as keys and deployments as values | -| `hosts` | [object](#hosts) | No | A map of objects with worker names as keys, each object defines a list of peer IDs to host the worker on. Intended to be used by providers to deploy directly without using the blockchain | -| `ipfsAddr` | string | No | IPFS multiaddress to use when uploading workers with 'fluence deploy'. Default: /dns4/ipfs.fluence.dev/tcp/5001 or /ip4/127.0.0.1/tcp/5001 if using local local env (for 'workers deploy' IPFS address provided by relay that you are connected to is used) | -| `marineBuildArgs` | string | No | Space separated `cargo build` flags and args to pass to marine build. Can be overridden using --marine-build-args flag Default: --release | -| `marineVersion` | string | No | Marine version | -| `mreplVersion` | string | No | Mrepl version | -| `relaysPath` | string, array, or null | No | Single or multiple paths to the directories where you want relays.json file to be generated. Must be relative to the project root dir. This file contains a list of relays to use when connecting to Fluence network and depends on the default environment that you use in your project | -| `rustToolchain` | string | No | Rust toolchain to use for building the project. By default nightly-2024-06-10 is used | -| `services` | [object](#services) | No | A map with service names as keys and Service configs as values. You can have any number of services listed here as long as service name keys start with a lowercase letter and contain only letters numbers and underscores. You can use `fluence service add` command to add a service to this config | -| `spells` | [object](#spells) | No | A map with spell names as keys and spell configs as values | - -## aquaDependencies - -A map of npm aqua dependency versions - -### Properties - -| Property | Type | Required | Description | -|----------------------------|--------|----------|---------------------------------------------------------------------------------------------------------------------------------| -| `npm-aqua-dependency-name` | string | No | Valid npm dependency version specification (check out https://docs.npmjs.com/cli/v10/configuring-npm/package-json#dependencies) | - -## compileAqua - -A map of aqua files to compile - -### Properties - -| Property | Type | Required | Description | -|--------------------|-----------------------------|----------|-------------| -| `aqua-config-name` | [object](#aqua-config-name) | No | | - -### aqua-config-name - -#### Properties - -| Property | Type | Required | Description | -|-------------------|----------------------|----------|-------------------------------------------------------------------------------------------------------------------------| -| `input` | string | **Yes** | Relative path to the aqua file or directory with aqua files | -| `output` | string | **Yes** | Relative path to the output directory | -| `target` | string | **Yes** | Compilation target Possible values are: `ts`, `js`, `air`. | -| `constants` | [object](#constants) | No | A list of constants to pass to the compiler. Constant name must be uppercase | -| `logLevel` | string | No | Log level for the compiler. Default: info Possible values are: `all`, `trace`, `debug`, `info`, `warn`, `error`, `off`. | -| `noEmptyResponse` | boolean | No | Do not generate response call if there are no returned values. Default: false | -| `noRelay` | boolean | No | Do not generate a pass through the relay node. Default: false | -| `noXor` | boolean | No | Do not generate a wrapper that catches and displays errors. Default: false | -| `tracing` | boolean | No | Compile aqua in tracing mode (for debugging purposes). Default: false | - -#### constants - -A list of constants to pass to the compiler. Constant name must be uppercase - -##### Properties - -| Property | Type | Required | Description | -|-----------------|----------------------------|----------|-------------| -| `SOME_CONSTANT` | string, number, or boolean | No | | - -## customFluenceEnv - -Custom Fluence environment to use when connecting to Fluence network - -### Properties - -| Property | Type | Required | Description | -|----------------|----------|----------|--------------------------------------------------------------------------------------------------------------------------------------------------------| -| `contractsEnv` | string | **Yes** | Contracts environment to use for this fluence network to sign contracts on the blockchain Possible values are: `testnet`, `mainnet`, `stage`, `local`. | -| `relays` | string[] | **Yes** | List of custom relay multiaddresses to use when connecting to Fluence network | - -## deployments - -A map with deployment names as keys and deployments as values - -### Properties - -| Property | Type | Required | Description | -|------------------|---------------------------|----------|-------------------| -| `deploymentName` | [object](#deploymentname) | No | Deployment config | - -### deploymentName - -Deployment config - -#### Properties - -| Property | Type | Required | Description | -|-------------------------|----------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `blacklist` | string[] | No | Blacklist of providers to deploy to. Can't be used together with whitelist | -| `computeUnits` | integer | No | DEPRECATED. USE cuCountPerWorker INSTEAD. Number of compute units you require. 1 compute unit = 2GB. Currently the only allowed value is 1. This will change in the future. Default: 1 | -| `cuCountPerWorker` | integer | No | Number of compute units per worker. Default: 1 | -| `effectors` | string[] | No | Effector CIDs to be used in the deal. Must be a valid CID | -| `initialBalance` | string | No | Initial balance after deploy in USDC. Default: targetWorkers * pricePerCuPerEpoch * minDealDepositedEpochs. For local environment: enough for deal to be active for 1 day | -| `maxWorkersPerProvider` | integer | No | Max workers per provider. Matches target workers by default | -| `minWorkers` | integer | No | Required workers to activate the deal. Matches target workers by default | -| `pricePerCuPerEpoch` | string | No | Price per compute unit per epoch in USDC | -| `protocolVersion` | integer | No | Protocol version. Default: 1 | -| `services` | string[] | No | An array of service names to include in this worker. Service names must be listed in fluence.yaml | -| `spells` | string[] | No | An array of spell names to include in this worker. Spell names must be listed in fluence.yaml | -| `targetWorkers` | integer | No | Max workers in the deal | -| `whitelist` | string[] | No | Whitelist of providers to deploy to. Can't be used together with blacklist | - -## hosts - -A map of objects with worker names as keys, each object defines a list of peer IDs to host the worker on. Intended to be used by providers to deploy directly without using the blockchain - -### Properties - -| Property | Type | Required | Description | -|--------------|-----------------------|----------|-------------------| -| `workerName` | [object](#workername) | No | Deployment config | - -### workerName - -Deployment config - -#### Properties - -| Property | Type | Required | Description | -|------------|----------|----------|---------------------------------------------------------------------------------------------------| -| `peerIds` | string[] | No | An array of peer IDs to deploy on | -| `services` | string[] | No | An array of service names to include in this worker. Service names must be listed in fluence.yaml | -| `spells` | string[] | No | An array of spell names to include in this worker. Spell names must be listed in fluence.yaml | - -## services - -A map with service names as keys and Service configs as values. You can have any number of services listed here as long as service name keys start with a lowercase letter and contain only letters numbers and underscores. You can use `fluence service add` command to add a service to this config - -### Properties - -| Property | Type | Required | Description | -|----------------|-------------------------|----------|-------------------------------------------------------------------| -| `Service_name` | [object](#service_name) | No | Service config. Defines where the service is and how to deploy it | - -### Service_name - -Service config. Defines where the service is and how to deploy it - -#### Properties - -| Property | Type | Required | Description | -|--------------------|----------------------------|----------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `get` | string | **Yes** | Path to service directory or URL to the tar.gz archive with the service | -| `overrideModules` | [object](#overridemodules) | No | A map of modules to override | -| `totalMemoryLimit` | string | No | Memory limit for all service modules. If you specify this property please make sure it's at least `2 MiB * numberOfModulesInTheService`. In repl default is the entire compute unit memory: 2GB. When deploying service as part of the worker default is: computeUnits * 2GB / (amount of services in the worker). Format: [number][whitespace?][B] where ? is an optional field and B is one of the following: kB, KB, kiB, KiB, KIB, mB, MB, miB, MiB, MIB, gB, GB, giB, GiB, GIB | - -#### overrideModules - -A map of modules to override - -##### Properties - -| Property | Type | Required | Description | -|---------------|------------------------|----------|---------------------------------| -| `Module_name` | [object](#module_name) | No | Overrides for the module config | - -##### Module_name - -Overrides for the module config - -###### Properties - -| Property | Type | Required | Description | -|-----------|--------------------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `effects` | [object](#effects) | No | Effects configuration. Only providers can allow and control effector modules by changing the nox configuration. Properties in this config are ignored when you deploy your code | -| `repl` | [object](#repl) | No | REPL configuration. Properties in this config are ignored when you deploy your code | - -###### effects - -Effects configuration. Only providers can allow and control effector modules by changing the nox configuration. Properties in this config are ignored when you deploy your code - -**Properties** - -| Property | Type | Required | Description | -|------------|---------------------|----------|-----------------------------------------------------------------------------------------------| -| `binaries` | [object](#binaries) | No | A map of binary executable files that module is allowed to call. Example: curl: /usr/bin/curl | - -**binaries** - -A map of binary executable files that module is allowed to call. Example: curl: /usr/bin/curl - -**Properties** - -| Property | Type | Required | Description | -|---------------|--------|----------|------------------| -| `binary-name` | string | No | Path to a binary | - -###### repl - -REPL configuration. Properties in this config are ignored when you deploy your code - -**Properties** - -| Property | Type | Required | Description | -|-----------------|---------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------| -| `loggerEnabled` | boolean | No | Set true to allow module to use the Marine SDK logger | -| `loggingMask` | number | No | manages the logging targets, that are described in detail here: https://fluence.dev/docs/marine-book/marine-rust-sdk/developing/logging#using-target-map | - -## spells - -A map with spell names as keys and spell configs as values - -### Properties - -| Property | Type | Required | Description | -|--------------|-----------------------|----------|--------------| -| `Spell_name` | [object](#spell_name) | No | Spell config | - -### Spell_name - -Spell config - -#### Properties - -| Property | Type | Required | Description | -|----------------|---------------------|----------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `get` | string | **Yes** | Path to spell | -| `aquaFilePath` | string | No | Path to Aqua file which contains an Aqua function that you want to use as a spell | -| `clock` | [object](#clock) | No | Trigger the spell execution periodically. If you want to disable this property by overriding it in fluence.yaml - pass empty config for it like this: `clock: {}` | -| `function` | string | No | Name of the Aqua function that you want to use as a spell | -| `initArgs` | [object](#initargs) | No | A map of Aqua function arguments names as keys and arguments values as values. They will be passed to the spell function and will be stored in the key-value storage for this particular spell. | -| `version` | integer | No | | - -#### clock - -Trigger the spell execution periodically. If you want to disable this property by overriding it in fluence.yaml - pass empty config for it like this: `clock: {}` - -##### Properties - -| Property | Type | Required | Description | -|------------------|---------|----------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `endDelaySec` | integer | No | How long to wait before the last execution in seconds. If this property or `endTimestamp` not specified, periodic execution will never end. WARNING! Currently your computer's clock is used to determine a final timestamp that is sent to the server. If it is in the past at the moment of spell creation - the spell will never be executed. This property conflicts with `endTimestamp`. You can specify only one of them | -| `endTimestamp` | string | No | An ISO timestamp when the periodic execution should end. If this property or `endDelaySec` not specified, periodic execution will never end. If it is in the past at the moment of spell creation on Rust peer - the spell will never be executed | -| `periodSec` | integer | No | How often the spell will be executed. If set to 0, the spell will be executed only once. If this value not provided at all - the spell will never be executed | -| `startDelaySec` | integer | No | How long to wait before the first execution in seconds. If this property or `startTimestamp` not specified, periodic execution will start immediately. WARNING! Currently your computer's clock is used to determine a final timestamp that is sent to the server. This property conflicts with `startTimestamp`. You can specify only one of them | -| `startTimestamp` | string | No | An ISO timestamp when the periodic execution should start. If this property or `startDelaySec` not specified, periodic execution will start immediately. If it is set to 0 - the spell will never be executed | - -#### initArgs - -A map of Aqua function arguments names as keys and arguments values as values. They will be passed to the spell function and will be stored in the key-value storage for this particular spell. - -| Property | Type | Required | Description | -|----------|------|----------|-------------| - diff --git a/packages/cli/package/docs/configs/module.md b/packages/cli/package/docs/configs/module.md deleted file mode 100644 index 26e70810a..000000000 --- a/packages/cli/package/docs/configs/module.md +++ /dev/null @@ -1,58 +0,0 @@ -# module.yaml - -Defines Marine Module. You can use `fluence module new` command to generate a template for new module - -## Properties - -| Property | Type | Required | Description | -|--------------------|-----------------------------|----------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `name` | string | **Yes** | "name" property from the Cargo.toml (for module type "rust") or name of the precompiled .wasm file (for module type "compiled") | -| `version` | integer | **Yes** | | -| `cid` | string | No | CID of the module when it was packed | -| `effects` | [object](#effects) | No | Effects configuration. Only providers can allow and control effector modules by changing the nox configuration. Properties in this config are ignored when you deploy your code | -| `repl` | [object](#repl) | No | REPL configuration. Properties in this config are ignored when you deploy your code | -| `rustBindingCrate` | [object](#rustbindingcrate) | No | Interface crate that can be used with this module | -| `type` | string | No | Default: compiled. Module type "rust" is for the source code written in rust which can be compiled into a Marine module. Module type "compiled" is for the precompiled modules. Possible values are: `rust`, `compiled`. | - -## effects - -Effects configuration. Only providers can allow and control effector modules by changing the nox configuration. Properties in this config are ignored when you deploy your code - -### Properties - -| Property | Type | Required | Description | -|------------|---------------------|----------|-----------------------------------------------------------------------------------------------| -| `binaries` | [object](#binaries) | No | A map of binary executable files that module is allowed to call. Example: curl: /usr/bin/curl | - -### binaries - -A map of binary executable files that module is allowed to call. Example: curl: /usr/bin/curl - -#### Properties - -| Property | Type | Required | Description | -|---------------|--------|----------|------------------| -| `binary-name` | string | No | Path to a binary | - -## repl - -REPL configuration. Properties in this config are ignored when you deploy your code - -### Properties - -| Property | Type | Required | Description | -|-----------------|---------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------| -| `loggerEnabled` | boolean | No | Set true to allow module to use the Marine SDK logger | -| `loggingMask` | number | No | manages the logging targets, that are described in detail here: https://fluence.dev/docs/marine-book/marine-rust-sdk/developing/logging#using-target-map | - -## rustBindingCrate - -Interface crate that can be used with this module - -### Properties - -| Property | Type | Required | Description | -|-----------|--------|----------|-------------| -| `name` | string | **Yes** | | -| `version` | string | **Yes** | | - diff --git a/packages/cli/package/docs/configs/provider.md b/packages/cli/package/docs/configs/provider.md index 5e450a093..c098c3e4b 100644 --- a/packages/cli/package/docs/configs/provider.md +++ b/packages/cli/package/docs/configs/provider.md @@ -110,11 +110,13 @@ Defines a compute peer #### Properties -| Property | Type | Required | Description | -|----------------|----------------|----------|-------------------------------------------------------------------------------------------------| -| `computeUnits` | integer | **Yes** | How many compute units should nox have. Default: 32 (each compute unit requires 2GB of RAM) | -| `ccp` | [object](#ccp) | No | Configuration to pass to the Capacity Commitment Prover | -| `nox` | [object](#nox) | No | Configuration to pass to the nox compute peer. Config.toml files are generated from this config | +| Property | Type | Required | Description | +|------------------|----------------------|----------|-------------------------------------------------------------------------------------------------| +| `computeUnits` | integer | **Yes** | How many compute units should nox have. Default: 32 (each compute unit requires 2GB of RAM) | +| `ccp` | [object](#ccp) | No | Configuration to pass to the Capacity Commitment Prover | +| `kubeconfigPath` | string | No | Path to the kubeconfig file | +| `nox` | [object](#nox) | No | Configuration to pass to the nox compute peer. Config.toml files are generated from this config | +| `resources` | [object](#resources) | No | Resources configuration | #### ccp @@ -366,6 +368,33 @@ iptables-mapped port range from Host to VM | `end` | integer | No | End of the iptables-mapped port range from Host to VM | | `start` | integer | No | Start of the iptables-mapped port range from Host to VM | +#### resources + +Resources configuration + +##### Properties + +| Property | Type | Required | Description | +|----------|---------------|----------|------------------| +| `ip` | [object](#ip) | **Yes** | IP configuration | + +##### ip + +IP configuration + +###### Properties + +| Property | Type | Required | Description | +|----------|---------------------|----------|-------------| +| `supply` | [object](#supply)[] | **Yes** | IP supply | + +###### supply + +Either specify only a `start` property (if you want a single IP) or `start` and `end` properties (if you want a range) or `cidr` property (if you want a CIDR notation) + +| Property | Type | Required | Description | +|----------|------|----------|-------------| + ## nox Configuration to pass to the nox compute peer. Config.toml files are generated from this config diff --git a/packages/cli/package/docs/configs/service.md b/packages/cli/package/docs/configs/service.md deleted file mode 100644 index bb9e96b50..000000000 --- a/packages/cli/package/docs/configs/service.md +++ /dev/null @@ -1,106 +0,0 @@ -# service.yaml - -Defines a [Marine service](https://fluence.dev/docs/build/concepts/#services), most importantly the modules that the service consists of. You can use `fluence service new` command to generate a template for new service - -## Properties - -| Property | Type | Required | Description | -|--------------------|--------------------|----------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `modules` | [object](#modules) | **Yes** | Service must have a facade module. Each module properties can be overridden by the same properties in the service config | -| `name` | string | **Yes** | Service name. Currently it is used for the service name only when you add service to fluence.yaml using "add" command. But this name can be overridden to any other with the --name flag or manually in fluence.yaml | -| `version` | integer | **Yes** | | -| `totalMemoryLimit` | string | No | Memory limit for all service modules. If you specify this property please make sure it's at least `2 MiB * numberOfModulesInTheService`. In repl default is the entire compute unit memory: 2GB. When deploying service as part of the worker default is: computeUnits * 2GB / (amount of services in the worker). Format: [number][whitespace?][B] where ? is an optional field and B is one of the following: kB, KB, kiB, KiB, KIB, mB, MB, miB, MiB, MIB, gB, GB, giB, GiB, GIB | - -## modules - -Service must have a facade module. Each module properties can be overridden by the same properties in the service config - -### Properties - -| Property | Type | Required | Description | -|---------------------|------------------------------|----------|-------------| -| `facade` | [object](#facade) | **Yes** | | -| `Other_module_name` | [object](#other_module_name) | No | | - -### Other_module_name - -#### Properties - -| Property | Type | Required | Description | -|-----------|--------------------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `get` | string | **Yes** | Either path to the module directory or URL to the tar.gz archive which contains the content of the module directory | -| `effects` | [object](#effects) | No | Effects configuration. Only providers can allow and control effector modules by changing the nox configuration. Properties in this config are ignored when you deploy your code | -| `repl` | [object](#repl) | No | REPL configuration. Properties in this config are ignored when you deploy your code | - -#### effects - -Effects configuration. Only providers can allow and control effector modules by changing the nox configuration. Properties in this config are ignored when you deploy your code - -##### Properties - -| Property | Type | Required | Description | -|------------|---------------------|----------|-----------------------------------------------------------------------------------------------| -| `binaries` | [object](#binaries) | No | A map of binary executable files that module is allowed to call. Example: curl: /usr/bin/curl | - -##### binaries - -A map of binary executable files that module is allowed to call. Example: curl: /usr/bin/curl - -###### Properties - -| Property | Type | Required | Description | -|---------------|--------|----------|------------------| -| `binary-name` | string | No | Path to a binary | - -#### repl - -REPL configuration. Properties in this config are ignored when you deploy your code - -##### Properties - -| Property | Type | Required | Description | -|-----------------|---------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------| -| `loggerEnabled` | boolean | No | Set true to allow module to use the Marine SDK logger | -| `loggingMask` | number | No | manages the logging targets, that are described in detail here: https://fluence.dev/docs/marine-book/marine-rust-sdk/developing/logging#using-target-map | - -### facade - -#### Properties - -| Property | Type | Required | Description | -|-----------|--------------------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `get` | string | **Yes** | Either path to the module directory or URL to the tar.gz archive which contains the content of the module directory | -| `effects` | [object](#effects) | No | Effects configuration. Only providers can allow and control effector modules by changing the nox configuration. Properties in this config are ignored when you deploy your code | -| `repl` | [object](#repl) | No | REPL configuration. Properties in this config are ignored when you deploy your code | - -#### effects - -Effects configuration. Only providers can allow and control effector modules by changing the nox configuration. Properties in this config are ignored when you deploy your code - -##### Properties - -| Property | Type | Required | Description | -|------------|---------------------|----------|-----------------------------------------------------------------------------------------------| -| `binaries` | [object](#binaries) | No | A map of binary executable files that module is allowed to call. Example: curl: /usr/bin/curl | - -##### binaries - -A map of binary executable files that module is allowed to call. Example: curl: /usr/bin/curl - -###### Properties - -| Property | Type | Required | Description | -|---------------|--------|----------|------------------| -| `binary-name` | string | No | Path to a binary | - -#### repl - -REPL configuration. Properties in this config are ignored when you deploy your code - -##### Properties - -| Property | Type | Required | Description | -|-----------------|---------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------| -| `loggerEnabled` | boolean | No | Set true to allow module to use the Marine SDK logger | -| `loggingMask` | number | No | manages the logging targets, that are described in detail here: https://fluence.dev/docs/marine-book/marine-rust-sdk/developing/logging#using-target-map | - diff --git a/packages/cli/package/docs/configs/spell.md b/packages/cli/package/docs/configs/spell.md deleted file mode 100644 index 49a6ccd8c..000000000 --- a/packages/cli/package/docs/configs/spell.md +++ /dev/null @@ -1,35 +0,0 @@ -# spell.yaml - -Defines a spell. You can use `fluence spell new` command to generate a template for new spell - -## Properties - -| Property | Type | Required | Description | -|----------------|---------------------|----------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `aquaFilePath` | string | **Yes** | Path to Aqua file which contains an Aqua function that you want to use as a spell | -| `function` | string | **Yes** | Name of the Aqua function that you want to use as a spell | -| `version` | integer | **Yes** | | -| `clock` | [object](#clock) | No | Trigger the spell execution periodically. If you want to disable this property by overriding it in fluence.yaml - pass empty config for it like this: `clock: {}` | -| `initArgs` | [object](#initargs) | No | A map of Aqua function arguments names as keys and arguments values as values. They will be passed to the spell function and will be stored in the key-value storage for this particular spell. | - -## clock - -Trigger the spell execution periodically. If you want to disable this property by overriding it in fluence.yaml - pass empty config for it like this: `clock: {}` - -### Properties - -| Property | Type | Required | Description | -|------------------|---------|----------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `endDelaySec` | integer | No | How long to wait before the last execution in seconds. If this property or `endTimestamp` not specified, periodic execution will never end. WARNING! Currently your computer's clock is used to determine a final timestamp that is sent to the server. If it is in the past at the moment of spell creation - the spell will never be executed. This property conflicts with `endTimestamp`. You can specify only one of them | -| `endTimestamp` | string | No | An ISO timestamp when the periodic execution should end. If this property or `endDelaySec` not specified, periodic execution will never end. If it is in the past at the moment of spell creation on Rust peer - the spell will never be executed | -| `periodSec` | integer | No | How often the spell will be executed. If set to 0, the spell will be executed only once. If this value not provided at all - the spell will never be executed | -| `startDelaySec` | integer | No | How long to wait before the first execution in seconds. If this property or `startTimestamp` not specified, periodic execution will start immediately. WARNING! Currently your computer's clock is used to determine a final timestamp that is sent to the server. This property conflicts with `startTimestamp`. You can specify only one of them | -| `startTimestamp` | string | No | An ISO timestamp when the periodic execution should start. If this property or `startDelaySec` not specified, periodic execution will start immediately. If it is set to 0 - the spell will never be executed | - -## initArgs - -A map of Aqua function arguments names as keys and arguments values as values. They will be passed to the spell function and will be stored in the key-value storage for this particular spell. - -| Property | Type | Required | Description | -|----------|------|----------|-------------| - diff --git a/packages/cli/package/docs/configs/workers.md b/packages/cli/package/docs/configs/workers.md deleted file mode 100644 index 990d5894a..000000000 --- a/packages/cli/package/docs/configs/workers.md +++ /dev/null @@ -1,340 +0,0 @@ -# workers.yaml - -A result of app deployment. This file is created automatically after successful deployment using `fluence workers deploy` command - -## Properties - -| Property | Type | Required | Description | -|-----------|------------------|----------|----------------------------------------------------------------------------------------------------| -| `version` | integer | **Yes** | Config version | -| `deals` | [object](#deals) | No | Info about deals created when deploying workers that is stored by environment that you deployed to | -| `hosts` | [object](#hosts) | No | Info about directly deployed workers that is stored by environment that you deployed to | - -## deals - -Info about deals created when deploying workers that is stored by environment that you deployed to - -### Properties - -| Property | Type | Required | Description | -|-----------|--------------------|----------|------------------------| -| `custom` | [object](#custom) | No | A map of created deals | -| `local` | [object](#local) | No | A map of created deals | -| `mainnet` | [object](#mainnet) | No | A map of created deals | -| `stage` | [object](#stage) | No | A map of created deals | -| `testnet` | [object](#testnet) | No | A map of created deals | - -### custom - -A map of created deals - -#### Properties - -| Property | Type | Required | Description | -|-------------------------------|----------------------------------------|----------|-----------------------------------------------------------------------------------------------------------------------------| -| `Worker_deployed_using_deals` | [object](#worker_deployed_using_deals) | No | Contains data related to your deployment, including, most importantly, deal id, that can be used to resolve workers in aqua | - -#### Worker_deployed_using_deals - -Contains data related to your deployment, including, most importantly, deal id, that can be used to resolve workers in aqua - -##### Properties - -| Property | Type | Required | Description | -|------------------|---------|----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `chainNetworkId` | integer | **Yes** | Blockchain network id that was used when deploying workers | -| `dealIdOriginal` | string | **Yes** | Blockchain transaction id that you get when deploy workers. Can be used in aqua to get worker and host ids. Check out example in the aqua generated in the default template | -| `dealId` | string | **Yes** | Lowercased version of dealIdOriginal without 0x prefix. Currently unused. Was previously used to resolve workers in aqua | -| `definition` | string | **Yes** | CID of uploaded to IPFS App Definition, which contains the data about everything that you are trying to deploy, including spells, service and module configs and CIDs for service wasms | -| `timestamp` | string | **Yes** | ISO timestamp of the time when the worker was deployed | -| `chainNetwork` | string | No | DEPRECATED. Blockchain network name that was used when deploying workers Possible values are: `kras`, `dar`, `stage`, `local`. | -| `matched` | boolean | No | Is deal matched | - -### local - -A map of created deals - -#### Properties - -| Property | Type | Required | Description | -|-------------------------------|----------------------------------------|----------|-----------------------------------------------------------------------------------------------------------------------------| -| `Worker_deployed_using_deals` | [object](#worker_deployed_using_deals) | No | Contains data related to your deployment, including, most importantly, deal id, that can be used to resolve workers in aqua | - -#### Worker_deployed_using_deals - -Contains data related to your deployment, including, most importantly, deal id, that can be used to resolve workers in aqua - -##### Properties - -| Property | Type | Required | Description | -|------------------|---------|----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `chainNetworkId` | integer | **Yes** | Blockchain network id that was used when deploying workers | -| `dealIdOriginal` | string | **Yes** | Blockchain transaction id that you get when deploy workers. Can be used in aqua to get worker and host ids. Check out example in the aqua generated in the default template | -| `dealId` | string | **Yes** | Lowercased version of dealIdOriginal without 0x prefix. Currently unused. Was previously used to resolve workers in aqua | -| `definition` | string | **Yes** | CID of uploaded to IPFS App Definition, which contains the data about everything that you are trying to deploy, including spells, service and module configs and CIDs for service wasms | -| `timestamp` | string | **Yes** | ISO timestamp of the time when the worker was deployed | -| `chainNetwork` | string | No | DEPRECATED. Blockchain network name that was used when deploying workers Possible values are: `kras`, `dar`, `stage`, `local`. | -| `matched` | boolean | No | Is deal matched | - -### mainnet - -A map of created deals - -#### Properties - -| Property | Type | Required | Description | -|-------------------------------|----------------------------------------|----------|-----------------------------------------------------------------------------------------------------------------------------| -| `Worker_deployed_using_deals` | [object](#worker_deployed_using_deals) | No | Contains data related to your deployment, including, most importantly, deal id, that can be used to resolve workers in aqua | - -#### Worker_deployed_using_deals - -Contains data related to your deployment, including, most importantly, deal id, that can be used to resolve workers in aqua - -##### Properties - -| Property | Type | Required | Description | -|------------------|---------|----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `chainNetworkId` | integer | **Yes** | Blockchain network id that was used when deploying workers | -| `dealIdOriginal` | string | **Yes** | Blockchain transaction id that you get when deploy workers. Can be used in aqua to get worker and host ids. Check out example in the aqua generated in the default template | -| `dealId` | string | **Yes** | Lowercased version of dealIdOriginal without 0x prefix. Currently unused. Was previously used to resolve workers in aqua | -| `definition` | string | **Yes** | CID of uploaded to IPFS App Definition, which contains the data about everything that you are trying to deploy, including spells, service and module configs and CIDs for service wasms | -| `timestamp` | string | **Yes** | ISO timestamp of the time when the worker was deployed | -| `chainNetwork` | string | No | DEPRECATED. Blockchain network name that was used when deploying workers Possible values are: `kras`, `dar`, `stage`, `local`. | -| `matched` | boolean | No | Is deal matched | - -### stage - -A map of created deals - -#### Properties - -| Property | Type | Required | Description | -|-------------------------------|----------------------------------------|----------|-----------------------------------------------------------------------------------------------------------------------------| -| `Worker_deployed_using_deals` | [object](#worker_deployed_using_deals) | No | Contains data related to your deployment, including, most importantly, deal id, that can be used to resolve workers in aqua | - -#### Worker_deployed_using_deals - -Contains data related to your deployment, including, most importantly, deal id, that can be used to resolve workers in aqua - -##### Properties - -| Property | Type | Required | Description | -|------------------|---------|----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `chainNetworkId` | integer | **Yes** | Blockchain network id that was used when deploying workers | -| `dealIdOriginal` | string | **Yes** | Blockchain transaction id that you get when deploy workers. Can be used in aqua to get worker and host ids. Check out example in the aqua generated in the default template | -| `dealId` | string | **Yes** | Lowercased version of dealIdOriginal without 0x prefix. Currently unused. Was previously used to resolve workers in aqua | -| `definition` | string | **Yes** | CID of uploaded to IPFS App Definition, which contains the data about everything that you are trying to deploy, including spells, service and module configs and CIDs for service wasms | -| `timestamp` | string | **Yes** | ISO timestamp of the time when the worker was deployed | -| `chainNetwork` | string | No | DEPRECATED. Blockchain network name that was used when deploying workers Possible values are: `kras`, `dar`, `stage`, `local`. | -| `matched` | boolean | No | Is deal matched | - -### testnet - -A map of created deals - -#### Properties - -| Property | Type | Required | Description | -|-------------------------------|----------------------------------------|----------|-----------------------------------------------------------------------------------------------------------------------------| -| `Worker_deployed_using_deals` | [object](#worker_deployed_using_deals) | No | Contains data related to your deployment, including, most importantly, deal id, that can be used to resolve workers in aqua | - -#### Worker_deployed_using_deals - -Contains data related to your deployment, including, most importantly, deal id, that can be used to resolve workers in aqua - -##### Properties - -| Property | Type | Required | Description | -|------------------|---------|----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `chainNetworkId` | integer | **Yes** | Blockchain network id that was used when deploying workers | -| `dealIdOriginal` | string | **Yes** | Blockchain transaction id that you get when deploy workers. Can be used in aqua to get worker and host ids. Check out example in the aqua generated in the default template | -| `dealId` | string | **Yes** | Lowercased version of dealIdOriginal without 0x prefix. Currently unused. Was previously used to resolve workers in aqua | -| `definition` | string | **Yes** | CID of uploaded to IPFS App Definition, which contains the data about everything that you are trying to deploy, including spells, service and module configs and CIDs for service wasms | -| `timestamp` | string | **Yes** | ISO timestamp of the time when the worker was deployed | -| `chainNetwork` | string | No | DEPRECATED. Blockchain network name that was used when deploying workers Possible values are: `kras`, `dar`, `stage`, `local`. | -| `matched` | boolean | No | Is deal matched | - -## hosts - -Info about directly deployed workers that is stored by environment that you deployed to - -### Properties - -| Property | Type | Required | Description | -|-----------|--------------------|----------|------------------------------------| -| `custom` | [object](#custom) | No | A map of directly deployed workers | -| `local` | [object](#local) | No | A map of directly deployed workers | -| `mainnet` | [object](#mainnet) | No | A map of directly deployed workers | -| `stage` | [object](#stage) | No | A map of directly deployed workers | -| `testnet` | [object](#testnet) | No | A map of directly deployed workers | - -### custom - -A map of directly deployed workers - -#### Properties - -| Property | Type | Required | Description | -|----------------------------------------|-------------------------------------------------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `Worker_deployed_using_direct_hosting` | [object](#worker_deployed_using_direct_hosting) | No | Contains data related to your direct deployment. Most importantly, it contains ids in installation_spells property that can be used to resolve workers in aqua | - -#### Worker_deployed_using_direct_hosting - -Contains data related to your direct deployment. Most importantly, it contains ids in installation_spells property that can be used to resolve workers in aqua - -##### Properties - -| Property | Type | Required | Description | -|-----------------------|----------------------------------|----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `definition` | string | **Yes** | CID of uploaded to IPFS App Definition, which contains the data about everything that you are trying to deploy, including spells, service and module configs and CIDs for service wasms | -| `dummyDealId` | string | **Yes** | random string generated by CLI, used in Nox. You can get worker id from it | -| `installation_spells` | [object](#installation_spells)[] | **Yes** | A list of installation spells | -| `relayId` | string | **Yes** | relay peer id that was used when deploying | -| `timestamp` | string | **Yes** | ISO timestamp of the time when the worker was deployed | - -##### installation_spells - -###### Properties - -| Property | Type | Required | Description | -|-------------|--------|----------|--------------------------------------------------------------------| -| `host_id` | string | **Yes** | Can be used to access worker in aqua: `on s.workerId via s.hostId` | -| `spell_id` | string | **Yes** | id of the installation spell, can be used to e.g. print spell logs | -| `worker_id` | string | **Yes** | Can be used to access worker in aqua: `on s.workerId via s.hostId` | - -### local - -A map of directly deployed workers - -#### Properties - -| Property | Type | Required | Description | -|----------------------------------------|-------------------------------------------------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `Worker_deployed_using_direct_hosting` | [object](#worker_deployed_using_direct_hosting) | No | Contains data related to your direct deployment. Most importantly, it contains ids in installation_spells property that can be used to resolve workers in aqua | - -#### Worker_deployed_using_direct_hosting - -Contains data related to your direct deployment. Most importantly, it contains ids in installation_spells property that can be used to resolve workers in aqua - -##### Properties - -| Property | Type | Required | Description | -|-----------------------|----------------------------------|----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `definition` | string | **Yes** | CID of uploaded to IPFS App Definition, which contains the data about everything that you are trying to deploy, including spells, service and module configs and CIDs for service wasms | -| `dummyDealId` | string | **Yes** | random string generated by CLI, used in Nox. You can get worker id from it | -| `installation_spells` | [object](#installation_spells)[] | **Yes** | A list of installation spells | -| `relayId` | string | **Yes** | relay peer id that was used when deploying | -| `timestamp` | string | **Yes** | ISO timestamp of the time when the worker was deployed | - -##### installation_spells - -###### Properties - -| Property | Type | Required | Description | -|-------------|--------|----------|--------------------------------------------------------------------| -| `host_id` | string | **Yes** | Can be used to access worker in aqua: `on s.workerId via s.hostId` | -| `spell_id` | string | **Yes** | id of the installation spell, can be used to e.g. print spell logs | -| `worker_id` | string | **Yes** | Can be used to access worker in aqua: `on s.workerId via s.hostId` | - -### mainnet - -A map of directly deployed workers - -#### Properties - -| Property | Type | Required | Description | -|----------------------------------------|-------------------------------------------------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `Worker_deployed_using_direct_hosting` | [object](#worker_deployed_using_direct_hosting) | No | Contains data related to your direct deployment. Most importantly, it contains ids in installation_spells property that can be used to resolve workers in aqua | - -#### Worker_deployed_using_direct_hosting - -Contains data related to your direct deployment. Most importantly, it contains ids in installation_spells property that can be used to resolve workers in aqua - -##### Properties - -| Property | Type | Required | Description | -|-----------------------|----------------------------------|----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `definition` | string | **Yes** | CID of uploaded to IPFS App Definition, which contains the data about everything that you are trying to deploy, including spells, service and module configs and CIDs for service wasms | -| `dummyDealId` | string | **Yes** | random string generated by CLI, used in Nox. You can get worker id from it | -| `installation_spells` | [object](#installation_spells)[] | **Yes** | A list of installation spells | -| `relayId` | string | **Yes** | relay peer id that was used when deploying | -| `timestamp` | string | **Yes** | ISO timestamp of the time when the worker was deployed | - -##### installation_spells - -###### Properties - -| Property | Type | Required | Description | -|-------------|--------|----------|--------------------------------------------------------------------| -| `host_id` | string | **Yes** | Can be used to access worker in aqua: `on s.workerId via s.hostId` | -| `spell_id` | string | **Yes** | id of the installation spell, can be used to e.g. print spell logs | -| `worker_id` | string | **Yes** | Can be used to access worker in aqua: `on s.workerId via s.hostId` | - -### stage - -A map of directly deployed workers - -#### Properties - -| Property | Type | Required | Description | -|----------------------------------------|-------------------------------------------------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `Worker_deployed_using_direct_hosting` | [object](#worker_deployed_using_direct_hosting) | No | Contains data related to your direct deployment. Most importantly, it contains ids in installation_spells property that can be used to resolve workers in aqua | - -#### Worker_deployed_using_direct_hosting - -Contains data related to your direct deployment. Most importantly, it contains ids in installation_spells property that can be used to resolve workers in aqua - -##### Properties - -| Property | Type | Required | Description | -|-----------------------|----------------------------------|----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `definition` | string | **Yes** | CID of uploaded to IPFS App Definition, which contains the data about everything that you are trying to deploy, including spells, service and module configs and CIDs for service wasms | -| `dummyDealId` | string | **Yes** | random string generated by CLI, used in Nox. You can get worker id from it | -| `installation_spells` | [object](#installation_spells)[] | **Yes** | A list of installation spells | -| `relayId` | string | **Yes** | relay peer id that was used when deploying | -| `timestamp` | string | **Yes** | ISO timestamp of the time when the worker was deployed | - -##### installation_spells - -###### Properties - -| Property | Type | Required | Description | -|-------------|--------|----------|--------------------------------------------------------------------| -| `host_id` | string | **Yes** | Can be used to access worker in aqua: `on s.workerId via s.hostId` | -| `spell_id` | string | **Yes** | id of the installation spell, can be used to e.g. print spell logs | -| `worker_id` | string | **Yes** | Can be used to access worker in aqua: `on s.workerId via s.hostId` | - -### testnet - -A map of directly deployed workers - -#### Properties - -| Property | Type | Required | Description | -|----------------------------------------|-------------------------------------------------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `Worker_deployed_using_direct_hosting` | [object](#worker_deployed_using_direct_hosting) | No | Contains data related to your direct deployment. Most importantly, it contains ids in installation_spells property that can be used to resolve workers in aqua | - -#### Worker_deployed_using_direct_hosting - -Contains data related to your direct deployment. Most importantly, it contains ids in installation_spells property that can be used to resolve workers in aqua - -##### Properties - -| Property | Type | Required | Description | -|-----------------------|----------------------------------|----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `definition` | string | **Yes** | CID of uploaded to IPFS App Definition, which contains the data about everything that you are trying to deploy, including spells, service and module configs and CIDs for service wasms | -| `dummyDealId` | string | **Yes** | random string generated by CLI, used in Nox. You can get worker id from it | -| `installation_spells` | [object](#installation_spells)[] | **Yes** | A list of installation spells | -| `relayId` | string | **Yes** | relay peer id that was used when deploying | -| `timestamp` | string | **Yes** | ISO timestamp of the time when the worker was deployed | - -##### installation_spells - -###### Properties - -| Property | Type | Required | Description | -|-------------|--------|----------|--------------------------------------------------------------------| -| `host_id` | string | **Yes** | Can be used to access worker in aqua: `on s.workerId via s.hostId` | -| `spell_id` | string | **Yes** | id of the installation spell, can be used to e.g. print spell logs | -| `worker_id` | string | **Yes** | Can be used to access worker in aqua: `on s.workerId via s.hostId` | - diff --git a/packages/cli/package/example.env b/packages/cli/package/example.env index 01441ec12..32f62f2a8 100644 --- a/packages/cli/package/example.env +++ b/packages/cli/package/example.env @@ -13,27 +13,13 @@ # CLI Dependency override examples -# FCLI_V_NOX="fluencelabs/nox:0.4.0" # FCLI_V_CHAIN="docker.fluence.dev/aurora:0.2.11" -# FCLI_V_RUST_TOOLCHAIN="nightly-2023-08-27-x86_64" -# FCLI_V_NPM_FLUENCELABS_AQUA_LIB="0.7.7" -# FCLI_V_NPM_FLUENCELABS_SPELL="0.5.26" -# FCLI_V_CARGO_MARINE="0.17.0" -# FCLI_V_CARGO_MREPL="0.24.0" -# FCLI_V_CARGO_MARINE_RS_SDK="0.10.1" -# FCLI_V_CARGO_MARINE_RS_SDK_TEST="0.11.0" - # environment variables to be used by Fluence CLI maintainers to develop Fluence CLI: # To turn on Countly debugger # DEBUG_COUNTLY="true" -# example of cargo env variables -# CARGO_REGISTRIES_FLUENCE_INDEX="git://crates.fluence.dev/index" -# CARGO_REGISTRIES_FLUENCE_TOKEN="" -# CARGO_REGISTRY_DEFAULT="fluence" - # example of npm env variables # You also need to do `npm login --registry=https://npm.fluence.dev` after that # NPM_CONFIG_REGISTRY="https://npm.fluence.dev" diff --git a/packages/cli/package/package.json b/packages/cli/package/package.json index 150438259..d17575924 100644 --- a/packages/cli/package/package.json +++ b/packages/cli/package/package.json @@ -23,8 +23,8 @@ "/oclif.manifest.json" ], "scripts": { - "before-build": "tsx src/beforeBuild.ts && npm --prefix ./src/aqua-dependencies install", - "build": "shx rm -rf dist && tsc -b && shx cp -r ./src/aqua-dependencies ./dist && shx cp -r ../../cli-connector/dist ./dist/cli-connector", + "before-build": "tsx src/beforeBuild.ts", + "build": "shx rm -rf dist && tsc -b && shx cp -r ../../cli-connector/dist ./dist/cli-connector", "type-check": "tsc --noEmit", "lint": "eslint .", "lint-fix": "eslint . --fix", @@ -33,11 +33,7 @@ "gql-codegen": "graphql-codegen --config ./src/lib/gql/gqlCodegen.ts", "postpack": "shx rm -f oclif.manifest.json", "vitest-provider": "vitest run provider.test.ts", - "vitest-deal-deploy": "vitest run dealDeploy.test.ts", - "vitest-deal-update": "vitest run dealUpdate.test.ts", - "vitest-smoke": "vitest run smoke.test.ts", - "vitest-js-to-aqua": "vitest run jsToAqua.test.ts", - "vitest": "yarn vitest-provider && yarn vitest-deal-deploy && yarn vitest-deal-update && yarn vitest-smoke && yarn vitest-js-to-aqua", + "vitest": "yarn vitest-provider", "pack-linux-x64": "oclif pack tarballs -t \"linux-x64\" --no-xz", "pack-darwin-x64": "oclif pack tarballs -t \"darwin-x64\" --no-xz", "pack-darwin-arm64": "oclif pack tarballs -t \"darwin-arm64\" --no-xz", @@ -47,10 +43,9 @@ "upload-darwin-x64": "oclif upload tarballs -t \"darwin-x64\" --no-xz", "upload-darwin-arm64": "oclif upload tarballs -t \"darwin-arm64\" --no-xz", "upload-win32-x64": "oclif upload tarballs -t \"win32-x64\" --no-xz && oclif upload win --targets \"win32-x64\"", - "download-marine-and-mrepl": "tsx ./test/setup/downloadMarineAndMrepl.ts", "generate-templates": "tsx ./test/setup/generateTemplates.ts", "local-up": "tsx ./test/setup/localUp.ts", - "test": "yarn download-marine-and-mrepl && yarn generate-templates && yarn local-up && yarn vitest", + "test": "yarn generate-templates && yarn local-up && yarn vitest", "circular": "madge --circular ./dist --exclude cli-connector", "on-each-commit": "yarn lint-fix && yarn circular && cd docs/commands && oclif readme --no-aliases && yarn gen-config-docs", "gen-config-docs": "shx rm -rf schemas && shx rm -rf docs/configs && tsx ./src/genConfigDocs.ts", @@ -61,23 +56,17 @@ "whatwg-url": "^14.0.0" }, "dependencies": { - "@fluencelabs/air-beautify-wasm": "0.4.0", - "@fluencelabs/aqua-api": "0.14.11", - "@fluencelabs/aqua-to-js": "0.3.13", "@fluencelabs/deal-ts-clients": "0.22.1", - "@fluencelabs/fluence-network-environment": "1.2.3", - "@fluencelabs/js-client": "0.9.0", - "@fluencelabs/npm-aqua-compiler": "0.0.3", - "@iarna/toml": "2.2.5", + "@kubernetes/client-node": "github:fluencelabs/kubernetes-client-javascript#e72ee00a52fec4eb4a8327632895d888ee504f4d", + "@libp2p/crypto": "4.0.1", + "@libp2p/peer-id-factory": "4.0.5", "@mswjs/interceptors": "0.29.1", - "@multiformats/multiaddr": "12.2.1", "@oclif/color": "1.0.13", "@oclif/core": "4.0.29", "@oclif/plugin-autocomplete": "3.2.6", "@oclif/plugin-help": "6.2.15", "@oclif/plugin-not-found": "3.2.22", "@oclif/plugin-update": "4.6.4", - "@total-typescript/ts-reset": "0.6.1", "ajv": "8.17.1", "ajv-formats": "^3.0.1", "chokidar": "4.0.1", @@ -93,15 +82,10 @@ "inquirer": "9.2.20", "ipfs-http-client": "60.0.1", "lodash-es": "4.17.21", - "lokijs": "1.5.12", "multiformats": "13.3.0", - "node_modules-path": "2.0.8", - "npm": "10.9.0", "parse-duration": "1.1.0", "platform": "1.3.6", - "semver": "7.6.3", "strip-ansi": "^7.1.0", - "tar": "7.4.3", "xbytes": "1.9.1", "yaml": "2.6.0", "yaml-diff-patch": "2.0.0" @@ -113,6 +97,7 @@ "@graphql-codegen/typescript": "^4.1.1", "@graphql-codegen/typescript-graphql-request": "^6.2.0", "@graphql-codegen/typescript-operations": "^4.3.1", + "@total-typescript/ts-reset": "0.6.1", "@tsconfig/node22": "^22.0.0", "@tsconfig/strictest": "^2.0.5", "@types/debug": "4.1.12", @@ -136,6 +121,7 @@ "oclif": "patch:oclif@patch%3Aoclif@npm%253A4.15.9%23~/.yarn/patches/oclif-npm-4.15.9-f51cd140c1.patch%3A%3Aversion=4.15.9&hash=ce7fb7#~/.yarn/patches/oclif-patch-c5b232dd7e.patch", "proper-lockfile": "4.1.2", "shx": "0.3.4", + "tar": "7.4.3", "ts-prune": "0.10.3", "ts-unused-exports": "10.1.0", "tslib": "2.8.0", @@ -156,38 +142,14 @@ ], "topicSeparator": " ", "topics": { - "aqua": { - "description": "Set of convenience commands for converting JSON and YAML into Aqua object literal syntax" - }, - "deal": { - "description": "Manage deals" - }, "default": { "description": "Manage and display default CLI configurations" }, - "dep": { - "description": "Manage project's dependencies" - }, - "key": { - "description": "Manage secret keys, that define peer-ids of both nox and js-client (including the one inside this CLI itself)" - }, "local": { "description": "Manage local fluence environment using docker-compose" }, - "module": { - "description": "Manage project's service modules" - }, "provider": { "description": "Set of commands to be used by providers or you can use them to manage your local environment" - }, - "service": { - "description": "Manage project's services" - }, - "spell": { - "description": "Manage project's spells" - }, - "delegator": { - "description": "Commands that are useful for delegators" } }, "update": { diff --git a/packages/cli/package/src/beforeBuild.ts b/packages/cli/package/src/beforeBuild.ts index 048fa00c5..14f91db0f 100644 --- a/packages/cli/package/src/beforeBuild.ts +++ b/packages/cli/package/src/beforeBuild.ts @@ -16,151 +16,21 @@ */ // @ts-check -import { cp, mkdir, rm, writeFile, readFile } from "node:fs/promises"; +import { cp, mkdir, rm } from "node:fs/promises"; import { join, resolve, dirname } from "node:path"; import { fileURLToPath } from "node:url"; -import { compileFromPath } from "@fluencelabs/aqua-api"; -import aquaToJs from "@fluencelabs/aqua-to-js"; -import { gatherImportsFromNpm } from "@fluencelabs/npm-aqua-compiler"; - -import { versions } from "./versions.js"; - const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const root = resolve(__dirname, ".."); -const WORKSPACE_NODE_MODULES_PATH = resolve(root, "node_modules"); - -const aquaDependenciesDirPath = join(root, "src", "aqua-dependencies"); - -const rustToolchainPath = join(root, "..", "..", "..", "rust-toolchain.toml"); -const toolchainFileContent = await readFile(rustToolchainPath, "utf-8"); - -await writeFile( - rustToolchainPath, - toolchainFileContent.replace(/channel = "(.*)"/, () => { - return `channel = "${versions["rust-toolchain"]}"`; - }), - "utf-8", -); - -await mkdir(aquaDependenciesDirPath, { recursive: true }); - -await writeFile( - join(aquaDependenciesDirPath, "package.json"), - JSON.stringify({ dependencies: versions.npm }, null, 2), - "utf-8", -); - const VERSIONS_DIR_PATH = join(root, "src", "versions"); -const SRC_LIB_PATH = join(root, "src", "lib"); -const COMPILED_AQUA_PATH = join(SRC_LIB_PATH, "compiled-aqua"); - -const COMPILED_AQUA_WITH_TRACING_PATH = join( - SRC_LIB_PATH, - "compiled-aqua-with-tracing", -); - -const COMPILED_INSTALLATION_SPELL_AQUA_PATH = join( - COMPILED_AQUA_PATH, - "installation-spell", -); - -const COMPILED_INSTALLATION_SPELL_AQUA_WITH_TRACING_PATH = join( - COMPILED_AQUA_WITH_TRACING_PATH, - "installation-spell", -); - -const CLI_AQUA_DEPENDENCIES_DIR_PATH = resolve( - join(root, "src", "cli-aqua-dependencies"), -); - -const INSTALLATION_SPELL_DIR_PATH = join( - CLI_AQUA_DEPENDENCIES_DIR_PATH, - "node_modules", - "@fluencelabs", - "installation-spell", -); - -const INSTALLATION_SPELL_AQUA_DIR_PATH = join( - INSTALLATION_SPELL_DIR_PATH, - "src", - "aqua", -); - -const imports = await gatherImportsFromNpm({ - npmProjectDirPath: CLI_AQUA_DEPENDENCIES_DIR_PATH, - aquaToCompileDirPath: INSTALLATION_SPELL_DIR_PATH, -}); - -async function compileInstallationSpellAqua(tracing = false) { - return Promise.all( - ["upload", "cli", "deal_spell", "files", "deploy"].map(async (fileName) => { - const filePath = join( - INSTALLATION_SPELL_AQUA_DIR_PATH, - `${fileName}.aqua`, - ); - - const compilationResult = await compileFromPath({ - filePath, - imports, - tracing, - }); - - if (compilationResult.errors.length !== 0) { - throw new Error(compilationResult.errors.join("\n\n")); - } - - const { sources } = (await aquaToJs(compilationResult, "ts")) ?? {}; - - if (sources === undefined) { - throw new Error( - `File ${filePath} no longer exposes anything. Please expose something from it or remove it from compilation`, - ); - } - - await writeFile( - join( - tracing - ? COMPILED_INSTALLATION_SPELL_AQUA_WITH_TRACING_PATH - : COMPILED_INSTALLATION_SPELL_AQUA_PATH, - `${fileName}.ts`, - ), - sources, - "utf-8", - ); - }), - ); -} await rm(VERSIONS_DIR_PATH, { recursive: true, force: true }); await mkdir(VERSIONS_DIR_PATH, { recursive: true }); await cp("package.json", join(VERSIONS_DIR_PATH, "cli.package.json")); -await cp( - join( - WORKSPACE_NODE_MODULES_PATH, - "@fluencelabs", - "js-client", - "package.json", - ), - join(VERSIONS_DIR_PATH, "js-client.package.json"), -); - await cp( resolve(root, "..", "..", "common", "src", "index.ts"), resolve(root, "src", "common.ts"), ); - -await rm(COMPILED_AQUA_PATH, { recursive: true, force: true }); -await mkdir(COMPILED_INSTALLATION_SPELL_AQUA_PATH, { recursive: true }); - -await mkdir(COMPILED_INSTALLATION_SPELL_AQUA_WITH_TRACING_PATH, { - recursive: true, -}); - -await Promise.all([ - compileInstallationSpellAqua(), - compileInstallationSpellAqua(true), -]); diff --git a/packages/cli/package/src/cli-aqua-dependencies/package-lock.json b/packages/cli/package/src/cli-aqua-dependencies/package-lock.json deleted file mode 100644 index 70b818130..000000000 --- a/packages/cli/package/src/cli-aqua-dependencies/package-lock.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "name": "cli-aqua-dependencies", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "dependencies": { - "@fluencelabs/installation-spell": "0.7.6" - } - }, - "node_modules/@fluencelabs/aqua-ipfs": { - "version": "0.5.31", - "resolved": "https://registry.npmjs.org/@fluencelabs/aqua-ipfs/-/aqua-ipfs-0.5.31.tgz", - "integrity": "sha512-JvxhWFFjpKB07ytmIo8T8Vj2xiPs2AmbTBECKeU1PkP7sYTud5pCDL/+0MiO0S1wNO6HSMXoOJic1Oh0XrTAsg==", - "dependencies": { - "@fluencelabs/aqua-lib": "0.10.1" - } - }, - "node_modules/@fluencelabs/aqua-ipfs/node_modules/@fluencelabs/aqua-lib": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@fluencelabs/aqua-lib/-/aqua-lib-0.10.1.tgz", - "integrity": "sha512-zwg7faVsmUmfiV5EScVs0p+BArzJzFRfgjcigKzoxB5DvQRb64+OSs7Pmt8flSfV4iprPf1RQT9vhsDvdOsK0g==" - }, - "node_modules/@fluencelabs/aqua-lib": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/@fluencelabs/aqua-lib/-/aqua-lib-0.10.2.tgz", - "integrity": "sha512-f3jL1kqHcP+tLpvD+QlVV51ZIptbStXfPSiDO+3SPurmcNWix8JZbQNQ+3t/BjCkHL8aw5r8vuuadPzftuVX/g==" - }, - "node_modules/@fluencelabs/installation-spell": { - "version": "0.7.6", - "resolved": "https://registry.npmjs.org/@fluencelabs/installation-spell/-/installation-spell-0.7.6.tgz", - "integrity": "sha512-fYBrN5TSyCI8K0HHYwtYFrcP7P/nkKzoGUnoHQhNdQeOjSbFiOae23Zjx4aQ2PXWsw05m7T18BeVBUbuGYSHDw==", - "dependencies": { - "@fluencelabs/aqua-ipfs": "0.5.31", - "@fluencelabs/aqua-lib": "0.10.2", - "@fluencelabs/spell": "0.7.6" - }, - "engines": { - "node": ">=18", - "pnpm": ">=8" - } - }, - "node_modules/@fluencelabs/spell": { - "version": "0.7.6", - "resolved": "https://registry.npmjs.org/@fluencelabs/spell/-/spell-0.7.6.tgz", - "integrity": "sha512-xGhB5BxOmel3MNiBJvRPmsHuvXhH0Uaje8WmnCRIskmH1fqQ/Ed7TFTtB3WruDvmBlaQTUqkkzI4JS9h5EZo7A==", - "dependencies": { - "@fluencelabs/aqua-lib": "0.10.1" - }, - "engines": { - "node": ">=18", - "pnpm": ">=8" - } - }, - "node_modules/@fluencelabs/spell/node_modules/@fluencelabs/aqua-lib": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@fluencelabs/aqua-lib/-/aqua-lib-0.10.1.tgz", - "integrity": "sha512-zwg7faVsmUmfiV5EScVs0p+BArzJzFRfgjcigKzoxB5DvQRb64+OSs7Pmt8flSfV4iprPf1RQT9vhsDvdOsK0g==" - } - } -} diff --git a/packages/cli/package/src/cli-aqua-dependencies/package.json b/packages/cli/package/src/cli-aqua-dependencies/package.json deleted file mode 100644 index 12a5e2579..000000000 --- a/packages/cli/package/src/cli-aqua-dependencies/package.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "dependencies": { - "@fluencelabs/installation-spell": "0.7.6" - } -} diff --git a/packages/cli/package/src/commands/air/beautify.ts b/packages/cli/package/src/commands/air/beautify.ts deleted file mode 100644 index 7e405b67e..000000000 --- a/packages/cli/package/src/commands/air/beautify.ts +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import { readFile } from "fs/promises"; - -import { Args } from "@oclif/core"; - -import { BaseCommand } from "../../baseCommand.js"; -import { commandObj } from "../../lib/commandObj.js"; -import { FS_OPTIONS } from "../../lib/const.js"; -import { aliasesText } from "../../lib/helpers/aliasesText.js"; -import { initCli } from "../../lib/lifeCycle.js"; -import { input } from "../../lib/prompt.js"; - -export default class Beautify extends BaseCommand { - static override hiddenAliases = ["air:b"]; - static override description = `Prints AIR script in human-readable Python-like representation. This representation cannot be executed and is intended to be read by mere mortals${aliasesText.apply(this)}`; - static override args = { - PATH: Args.string({ - description: `Path to an AIR file. Must be relative to the current working directory or absolute`, - helpValue: "", - char: "i", - }), - }; - - async run(): Promise { - const { args } = await initCli(this, await this.parse(Beautify)); - - const inputArg = - args.PATH ?? - (await input({ - message: `Enter a path to an AIR file`, - })); - - const air = await readFile(inputArg, FS_OPTIONS); - const { beautify } = await import("@fluencelabs/air-beautify-wasm"); - commandObj.log(beautify(air)); - } -} diff --git a/packages/cli/package/src/commands/aqua.ts b/packages/cli/package/src/commands/aqua.ts deleted file mode 100644 index 9640de094..000000000 --- a/packages/cli/package/src/commands/aqua.ts +++ /dev/null @@ -1,201 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import { isAbsolute, resolve } from "path"; -import { cwd } from "process"; - -import { Command, Flags } from "@oclif/core"; - -import { - resolveCommonAquaCompilationFlags, - type ResolvedCommonAquaCompilationFlags, -} from "../lib/aqua.js"; -import { - hasAquaToCompile, - compileAquaFromFluenceConfig, - compileAquaAndWatch, -} from "../lib/compileAquaAndWatch.js"; -import { initFluenceConfig } from "../lib/configs/project/fluence.js"; -import { - INPUT_FLAG_EXPLANATION, - COMPILE_AQUA_PROPERTY_NAME, - COMMON_AQUA_COMPILATION_FLAGS, - NO_INPUT_FLAG, - FLUENCE_CONFIG_FULL_FILE_NAME, - INPUT_FLAG_NAME, -} from "../lib/const.js"; -import { initCli, exitCli } from "../lib/lifeCycle.js"; -import { validatePath } from "../lib/paths.js"; -import { input, type InputArg } from "../lib/prompt.js"; - -/** - * This command doesn't extend BaseCommand like other commands do because it - * has a --watch flag which should keep cli alive - * This means we have to manually call exitCli() in all other cases before - * the final return statement - */ -export default class Aqua extends Command { - static override description = `Compile aqua defined in '${COMPILE_AQUA_PROPERTY_NAME}' property of ${FLUENCE_CONFIG_FULL_FILE_NAME}${INPUT_FLAG_EXPLANATION}`; - static override examples = ["<%= config.bin %> <%= command.id %>"]; - static override flags = { - names: Flags.string({ - description: `Comma-separated names of the configs from '${COMPILE_AQUA_PROPERTY_NAME}' property of ${FLUENCE_CONFIG_FULL_FILE_NAME} to compile. If not specified, all configs will be compiled`, - char: "n", - }), - ...NO_INPUT_FLAG, - watch: Flags.boolean({ - default: false, - description: "Watch aqua file or folder for changes and recompile", - char: "w", - }), - output: Flags.string({ - description: - "Path to the output directory. Must be relative to the current working directory or absolute. Will be created if it doesn't exists", - helpValue: "", - char: "o", - }), - air: Flags.boolean({ - default: false, - description: "Generate .air file instead of .ts", - exclusive: ["js"], - }), - js: Flags.boolean({ - default: false, - description: "Generate .js file instead of .ts", - exclusive: ["air"], - }), - ...COMMON_AQUA_COMPILATION_FLAGS, - dry: Flags.boolean({ - default: false, - description: "Checks if compilation succeeded, without output", - }), - }; - async run(): Promise { - const { flags } = await initCli(this, await this.parse(Aqua)); - const fluenceConfig = await initFluenceConfig(); - const commonFlags = await resolveCommonAquaCompilationFlags(flags); - - if ( - flags[INPUT_FLAG_NAME] === undefined && - hasAquaToCompile(fluenceConfig) - ) { - await compileAquaFromFluenceConfig({ - imports: commonFlags.imports, - names: flags.names, - dry: flags.dry, - watch: flags.watch, - }); - - await exitCli(); - return; - } - - await compileAquaFromFlags({ ...commonFlags, ...flags }); - } -} - -async function compileAquaFromFlags({ - input, - output, - ...flags -}: ResolvedCommonAquaCompilationFlags & { - input: string | undefined; - output: string | undefined; - dry: boolean; - js: boolean; - air: boolean; - watch: boolean; -}) { - const filePath = await resolveAbsoluteAquaPathFromCwd({ - maybePathFromFlags: input, - inputArg: { - message: `Enter path to an aqua file or an input directory that contains your aqua files`, - flagName: "input", - validate: validatePath, - }, - }); - - const outputPathAbsolute = flags.dry - ? undefined - : await resolveAbsoluteAquaPathFromCwd({ - maybePathFromFlags: output, - inputArg: { - message: - "Enter path to the output directory. Will be created if it doesn't exists (press Enter to use the current working directory)", - flagName: "output", - }, - }); - - const targetType = resolveTargetType(flags); - - await compileAquaAndWatch({ - filePath, - targetType, - outputPathAbsolute, - ...flags, - }); - - if (!flags.watch) { - await exitCli(); - } -} - -type ResolveTargetTypeArgs = { - js: boolean; - air: boolean; -}; - -function resolveTargetType({ - js, - air, -}: ResolveTargetTypeArgs): "ts" | "js" | "air" { - if (js) { - return "js"; - } - - if (air) { - return "air"; - } - - return "ts"; -} - -type ResolveAbsoluteAquaPathArg = { - maybePathFromFlags: string | undefined; - inputArg: InputArg; -}; - -async function resolveAbsoluteAquaPathFromCwd({ - maybePathFromFlags, - inputArg, -}: ResolveAbsoluteAquaPathArg) { - if (maybePathFromFlags !== undefined) { - if (isAbsolute(maybePathFromFlags)) { - return maybePathFromFlags; - } - - return resolve(maybePathFromFlags); - } - - const pathFromUserInput = await input({ ...inputArg, default: cwd() }); - - if (isAbsolute(pathFromUserInput)) { - return pathFromUserInput; - } - - return resolve(pathFromUserInput); -} diff --git a/packages/cli/package/src/commands/aqua/imports.ts b/packages/cli/package/src/commands/aqua/imports.ts deleted file mode 100644 index 2d0289c6d..000000000 --- a/packages/cli/package/src/commands/aqua/imports.ts +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import { BaseCommand } from "../../baseCommand.js"; -import { jsonStringify } from "../../common.js"; -import { commandObj } from "../../lib/commandObj.js"; -import { getAquaImports } from "../../lib/helpers/aquaImports.js"; -import { initCli } from "../../lib/lifeCycle.js"; - -export default class Json extends BaseCommand { - static override description = - "Returns a list of aqua imports that CLI produces"; - - async run(): Promise { - await initCli(this, await this.parse(Json)); - commandObj.log(jsonStringify(await getAquaImports())); - } -} diff --git a/packages/cli/package/src/commands/aqua/json.ts b/packages/cli/package/src/commands/aqua/json.ts deleted file mode 100644 index f849c2a45..000000000 --- a/packages/cli/package/src/commands/aqua/json.ts +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import { Args } from "@oclif/core"; - -import { BaseCommand } from "../../baseCommand.js"; -import { CUSTOM_TYPES_FLAG, USE_F64_FLAG } from "../../lib/const.js"; -import { fileToAqua } from "../../lib/helpers/jsToAqua.js"; -import { initCli } from "../../lib/lifeCycle.js"; - -export default class Json extends BaseCommand { - static override description = - "Infers aqua types for an arbitrary json file, generates valid aqua code with a function call that returns an aqua object literal with the same structure as the json file. For valid generation please refer to aqua documentation https://fluence.dev/docs/aqua-book/language/ to learn about what kind of structures are valid in aqua language and what they translate into"; - static override flags = { - ...USE_F64_FLAG, - ...CUSTOM_TYPES_FLAG, - }; - static override args = { - INPUT: Args.string({ - description: "Path to json file", - }), - OUTPUT: Args.string({ - description: `Path to the output dir`, - }), - }; - - async run(): Promise { - const { args, flags } = await initCli(this, await this.parse(Json)); - - await fileToAqua( - args.INPUT, - args.OUTPUT, - flags.f64, - flags.types, - JSON.parse, - ); - } -} diff --git a/packages/cli/package/src/commands/aqua/yml.ts b/packages/cli/package/src/commands/aqua/yml.ts deleted file mode 100644 index 7fbea52b8..000000000 --- a/packages/cli/package/src/commands/aqua/yml.ts +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import { Args } from "@oclif/core"; - -import { BaseCommand } from "../../baseCommand.js"; -import { CUSTOM_TYPES_FLAG, USE_F64_FLAG } from "../../lib/const.js"; -import { aliasesText } from "../../lib/helpers/aliasesText.js"; -import { fileToAqua } from "../../lib/helpers/jsToAqua.js"; -import { initCli } from "../../lib/lifeCycle.js"; - -export default class Yaml extends BaseCommand { - static override hiddenAliases = ["aqua:yaml"]; - static override description = `Infers aqua types for an arbitrary yaml file, generates valid aqua code with a function call that returns an aqua object literal with the same structure as the yaml file. For valid generation please refer to aqua documentation https://fluence.dev/docs/aqua-book/language/ to learn about what kind of structures are valid in aqua language and what they translate into${aliasesText.apply(this)}`; - static override flags = { - ...USE_F64_FLAG, - ...CUSTOM_TYPES_FLAG, - }; - static override args = { - INPUT: Args.string({ - description: "Path to yaml file", - }), - OUTPUT: Args.string({ - description: `Path to the output dir`, - }), - }; - - async run(): Promise { - const { args, flags } = await initCli(this, await this.parse(Yaml)); - const { parse } = await import("yaml"); - await fileToAqua(args.INPUT, args.OUTPUT, flags.f64, flags.types, parse); - } -} diff --git a/packages/cli/package/src/commands/build.ts b/packages/cli/package/src/commands/build.ts deleted file mode 100644 index b013d705c..000000000 --- a/packages/cli/package/src/commands/build.ts +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import { BaseCommand } from "../baseCommand.js"; -import { commandObj } from "../lib/commandObj.js"; -import { - FLUENCE_CONFIG_FULL_FILE_NAME, - MARINE_BUILD_ARGS_FLAG, - IMPORT_FLAG, - ENV_FLAG, -} from "../lib/const.js"; -import { prepareForDeploy } from "../lib/deployWorkers.js"; -import { ensureFluenceProject } from "../lib/helpers/ensureFluenceProject.js"; -import { initCli } from "../lib/lifeCycle.js"; -import { ensureFluenceEnv } from "../lib/resolveFluenceEnv.js"; - -export default class Build extends BaseCommand { - static override description = `Build all application services, described in ${FLUENCE_CONFIG_FULL_FILE_NAME} and generate aqua interfaces for them`; - static override examples = ["<%= config.bin %> <%= command.id %>"]; - static override flags = { - ...MARINE_BUILD_ARGS_FLAG, - ...IMPORT_FLAG, - ...ENV_FLAG, - }; - async run(): Promise { - const { flags } = await initCli(this, await this.parse(Build), true); - const fluenceConfig = await ensureFluenceProject(); - const fluenceEnv = await ensureFluenceEnv(); - - await prepareForDeploy({ - flags: { - ...flags, - "no-build": false, - }, - fluenceEnv, - isBuildCheck: true, - deploymentNamesString: Object.keys(fluenceConfig.deployments ?? {}).join( - ",", - ), - }); - - const { ensureAquaFileWithWorkerInfo } = await import( - "../lib/deployWorkers.js" - ); - - await ensureAquaFileWithWorkerInfo(); - - commandObj.logToStderr( - `All services and spells built, all aqua files generated and compiled successfully`, - ); - } -} diff --git a/packages/cli/package/src/commands/deal/change-app.ts b/packages/cli/package/src/commands/deal/change-app.ts deleted file mode 100644 index 2135f0b53..000000000 --- a/packages/cli/package/src/commands/deal/change-app.ts +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import { Args } from "@oclif/core"; - -import { BaseCommand } from "../../baseCommand.js"; -import { CHAIN_FLAGS } from "../../lib/const.js"; -import { dealUpdate } from "../../lib/deal.js"; -import { initCli } from "../../lib/lifeCycle.js"; -import { input } from "../../lib/prompt.js"; - -export default class ChangeApp extends BaseCommand { - hidden = true; - static override description = "Change app id in the deal"; - static override flags = { - ...CHAIN_FLAGS, - }; - - static override args = { - "DEAL-ADDRESS": Args.string({ - description: "Deal address", - }), - "NEW-APP-CID": Args.string({ - description: "New app CID for the deal", - }), - }; - - async run(): Promise { - const { args } = await initCli(this, await this.parse(ChangeApp)); - - await dealUpdate({ - dealAddress: - args["DEAL-ADDRESS"] ?? - (await input({ message: "Enter deal address" })), - appCID: - args["NEW-APP-CID"] ?? (await input({ message: "Enter new app CID" })), - }); - } -} diff --git a/packages/cli/package/src/commands/deal/create.ts b/packages/cli/package/src/commands/deal/create.ts deleted file mode 100644 index 95cf86617..000000000 --- a/packages/cli/package/src/commands/deal/create.ts +++ /dev/null @@ -1,110 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import { color } from "@oclif/color"; -import { Flags } from "@oclif/core"; - -import { BaseCommand } from "../../baseCommand.js"; -import { commandObj } from "../../lib/commandObj.js"; -import { CHAIN_FLAGS } from "../../lib/const.js"; -import { dealCreate } from "../../lib/deal.js"; -import { commaSepStrToArr } from "../../lib/helpers/utils.js"; -import { initCli } from "../../lib/lifeCycle.js"; - -export default class Create extends BaseCommand { - hidden = true; - static override description = - "Create your deal with the specified parameters"; - static override flags = { - "app-cid": Flags.string({ - description: "CID of the application that will be deployed", - required: true, - }), - "collateral-per-worker": Flags.string({ - description: "Collateral per worker", - required: true, - }), - "min-workers": Flags.integer({ - description: "Required workers to activate the deal", - required: true, - }), - "target-workers": Flags.integer({ - description: "Max workers in the deal", - required: true, - }), - "max-workers-per-provider": Flags.integer({ - description: "Max workers per provider", - required: true, - }), - "price-per-cu-per-epoch": Flags.string({ - description: "Price per CU per epoch", - required: true, - }), - "initial-balance": Flags.string({ - description: "Initial balance", - required: false, - }), - effectors: Flags.string({ - description: "Comma-separated list of effector to be used in the deal", - }), - whitelist: Flags.string({ - description: "Comma-separated list of whitelisted providers", - exclusive: ["blacklist"], - }), - blacklist: Flags.string({ - description: "Comma-separated list of blacklisted providers", - exclusive: ["whitelist"], - }), - "protocol-version": Flags.integer({ - description: "Protocol version", - }), - "cu-count-per-worker": Flags.integer({ - description: "Compute unit count per worker", - required: true, - }), - ...CHAIN_FLAGS, - }; - - async run(): Promise { - const { flags } = await initCli(this, await this.parse(Create)); - - const dealAddress = await dealCreate({ - cuCountPerWorker: flags["cu-count-per-worker"], - appCID: flags["app-cid"], - minWorkers: flags["min-workers"], - targetWorkers: flags["target-workers"], - maxWorkersPerProvider: flags["max-workers-per-provider"], - pricePerCuPerEpoch: flags["price-per-cu-per-epoch"], - effectors: - flags.effectors === undefined ? [] : commaSepStrToArr(flags.effectors), - initialBalance: flags["initial-balance"], - whitelist: - flags.whitelist === undefined - ? undefined - : commaSepStrToArr(flags.whitelist), - blacklist: - flags.blacklist === undefined - ? undefined - : commaSepStrToArr(flags.blacklist), - protocolVersion: flags["protocol-version"], - }); - - commandObj.logToStderr( - `Deal contract created: ${color.yellow(dealAddress)}`, - ); - } -} diff --git a/packages/cli/package/src/commands/deal/deploy.ts b/packages/cli/package/src/commands/deal/deploy.ts deleted file mode 100644 index be73e7fa5..000000000 --- a/packages/cli/package/src/commands/deal/deploy.ts +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import { BaseCommand } from "../../baseCommand.js"; -import { DEPLOYMENT_NAMES_ARG } from "../../lib/const.js"; -import { - DEPLOY_DESCRIPTION, - DEPLOY_EXAMPLES, - DEPLOY_FLAGS, - deployImpl, -} from "../../lib/deploy.js"; - -export default class Deploy extends BaseCommand { - static override hidden = true; - static override description = DEPLOY_DESCRIPTION; - static override examples = DEPLOY_EXAMPLES; - static override flags = DEPLOY_FLAGS; - static override args = DEPLOYMENT_NAMES_ARG; - async run(): Promise { - await deployImpl.bind(this)(Deploy); - } -} diff --git a/packages/cli/package/src/commands/deal/deposit.ts b/packages/cli/package/src/commands/deal/deposit.ts deleted file mode 100644 index 8503194f9..000000000 --- a/packages/cli/package/src/commands/deal/deposit.ts +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import { Args } from "@oclif/core"; - -import { BaseCommand } from "../../baseCommand.js"; -import { depositToDeal } from "../../lib/chain/depositToDeal.js"; -import { - CHAIN_FLAGS, - DEAL_IDS_FLAG, - DEPLOYMENT_NAMES_ARG, - PT_SYMBOL, -} from "../../lib/const.js"; -import { initCli } from "../../lib/lifeCycle.js"; -import { input } from "../../lib/prompt.js"; - -export default class Deposit extends BaseCommand { - static override description = "Deposit do the deal"; - static override flags = { - ...CHAIN_FLAGS, - ...DEAL_IDS_FLAG, - }; - - static override args = { - AMOUNT: Args.string({ - description: `Amount of ${PT_SYMBOL} tokens to deposit`, - }), - ...DEPLOYMENT_NAMES_ARG, - }; - - async run(): Promise { - const flagsAndArgs = await initCli(this, await this.parse(Deposit)); - - const amount = - flagsAndArgs.args["AMOUNT"] ?? - (await input({ - message: `Enter amount of ${PT_SYMBOL} tokens to deposit`, - })); - - await depositToDeal(flagsAndArgs, amount); - } -} diff --git a/packages/cli/package/src/commands/deal/info.ts b/packages/cli/package/src/commands/deal/info.ts deleted file mode 100644 index 02ad5549c..000000000 --- a/packages/cli/package/src/commands/deal/info.ts +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import { BaseCommand } from "../../baseCommand.js"; -import { printDealInfo } from "../../lib/chain/printDealInfo.js"; -import { - CHAIN_FLAGS, - DEAL_IDS_FLAG, - DEPLOYMENT_NAMES_ARG, -} from "../../lib/const.js"; -import { getDeals } from "../../lib/deal.js"; -import { initCli } from "../../lib/lifeCycle.js"; - -export default class Info extends BaseCommand { - static override description = "Get info about the deal"; - static override flags = { - ...CHAIN_FLAGS, - ...DEAL_IDS_FLAG, - }; - - static override args = { - ...DEPLOYMENT_NAMES_ARG, - }; - - async run(): Promise { - const flagsAndArgs = await initCli(this, await this.parse(Info)); - const deals = await getDeals(flagsAndArgs); - - for (const deal of deals) { - await printDealInfo(deal); - } - } -} diff --git a/packages/cli/package/src/commands/deal/logs.ts b/packages/cli/package/src/commands/deal/logs.ts deleted file mode 100644 index 52ae28b63..000000000 --- a/packages/cli/package/src/commands/deal/logs.ts +++ /dev/null @@ -1,158 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import { color } from "@oclif/color"; -import { Flags } from "@oclif/core"; - -import { BaseCommand } from "../../baseCommand.js"; -import { commandObj } from "../../lib/commandObj.js"; -import type { Get_logs_dealParams } from "../../lib/compiled-aqua/installation-spell/cli.js"; -import { - WORKER_SPELL, - WORKERS_CONFIG_FULL_FILE_NAME, - OFF_AQUA_LOGS_FLAG, - FLUENCE_CLIENT_FLAGS, - TTL_FLAG_NAME, - DIAL_TIMEOUT_FLAG_NAME, - TRACING_FLAG, - DEPLOYMENT_NAMES_ARG, - DEAL_IDS_FLAG, -} from "../../lib/const.js"; -import { getDeals } from "../../lib/deal.js"; -import { - formatAquaLogsHeader, - formatAquaLogs, -} from "../../lib/helpers/formatAquaLogs.js"; -import { stringifyUnknown } from "../../lib/helpers/stringifyUnknown.js"; -import { LOGS_RESOLVE_SUBNET_ERROR_START } from "../../lib/helpers/utils.js"; -import { - disconnectFluenceClient, - initFluenceClient, -} from "../../lib/jsClient.js"; -import { initCli } from "../../lib/lifeCycle.js"; - -export default class Logs extends BaseCommand { - static override description = `Get logs from deployed workers for deals listed in ${WORKERS_CONFIG_FULL_FILE_NAME}`; - static override examples = ["<%= config.bin %> <%= command.id %>"]; - static override flags = { - ...FLUENCE_CLIENT_FLAGS, - ...OFF_AQUA_LOGS_FLAG, - ...TRACING_FLAG, - ...DEAL_IDS_FLAG, - spell: Flags.string({ - description: `Spell name to get logs for`, - helpValue: "", - default: WORKER_SPELL, - }), - }; - static override args = { - ...DEPLOYMENT_NAMES_ARG, - }; - async run(): Promise { - const { flags, args } = await initCli(this, await this.parse(Logs)); - await initFluenceClient(flags); - - const dealIdWorkerNameMap = await getDealIdWorkerNameMap({ args, flags }); - - let logs; - - try { - logs = await getLogsDeal(flags.tracing, [ - Object.values(dealIdWorkerNameMap), - ]); - } catch (e) { - commandObj.error( - `Wasn't able to get logs. You can try increasing --${TTL_FLAG_NAME} and --${DIAL_TIMEOUT_FLAG_NAME}: ${stringifyUnknown( - e, - )}`, - ); - } - - commandObj.log( - logs - .flatMap(({ error, logs, deal_id }) => { - const worker_name = dealIdWorkerNameMap[deal_id]?.worker_name; - - if (typeof error === "string") { - const header = formatAquaLogsHeader({ - worker_name, - deal_id, - }); - - const trimmedError = error.trim(); - - return [ - `${header}${color.red( - trimmedError === "" - ? `${LOGS_RESOLVE_SUBNET_ERROR_START}Unknown error when resolving subnet` - : trimmedError, - )}`, - ]; - } - - return logs.map((l) => { - return formatAquaLogs({ ...l, worker_name }); - }); - }) - .join("\n\n"), - ); - - await disconnectFluenceClient(); - } -} - -async function getDealIdWorkerNameMap( - argsAndFlags: Parameters[0] & { - flags: { spell: string }; - }, -): Promise< - Record -> { - const workersToGetLogsFor = await getDeals(argsAndFlags); - - return Object.fromEntries( - workersToGetLogsFor.map(({ dealId, dealName }) => { - return [ - dealId, - { - deal_id: dealId, - spell_name: argsAndFlags.flags.spell, - worker_name: dealName, - }, - ] as const; - }), - ); -} - -async function getLogsDeal( - tracing: boolean, - getLogDealParams: Get_logs_dealParams, -) { - if (tracing) { - const { get_logs_deal } = await import( - "../../lib/compiled-aqua-with-tracing/installation-spell/cli.js" - ); - - return get_logs_deal(...getLogDealParams); - } - - const { get_logs_deal } = await import( - "../../lib/compiled-aqua/installation-spell/cli.js" - ); - - return get_logs_deal(...getLogDealParams); -} diff --git a/packages/cli/package/src/commands/deal/stop.ts b/packages/cli/package/src/commands/deal/stop.ts deleted file mode 100644 index b3f66a844..000000000 --- a/packages/cli/package/src/commands/deal/stop.ts +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import { color } from "@oclif/color"; - -import { BaseCommand } from "../../baseCommand.js"; -import { commandObj } from "../../lib/commandObj.js"; -import { - DEAL_IDS_FLAG, - CHAIN_FLAGS, - DEPLOYMENT_NAMES_ARG, -} from "../../lib/const.js"; -import { getDeals } from "../../lib/deal.js"; -import { getContracts, sign } from "../../lib/dealClient.js"; -import { initCli } from "../../lib/lifeCycle.js"; - -export default class Stop extends BaseCommand { - static override description = "Stop the deal"; - static override flags = { - ...CHAIN_FLAGS, - ...DEAL_IDS_FLAG, - }; - - static override args = { - ...DEPLOYMENT_NAMES_ARG, - }; - - async run(): Promise { - const flagsAndArgs = await initCli(this, await this.parse(Stop), true); - const deals = await getDeals(flagsAndArgs); - const { contracts } = await getContracts(); - - for (const { dealId, dealName } of deals) { - const deal = contracts.getDeal(dealId); - - await sign({ - title: `Stop the deal ${dealName} (${dealId})`, - method: deal.stop, - args: [], - }); - - commandObj.logToStderr(`Stopped deal: ${color.yellow(dealName)}`); - } - } -} diff --git a/packages/cli/package/src/commands/deal/withdraw.ts b/packages/cli/package/src/commands/deal/withdraw.ts deleted file mode 100644 index 2241ab9d5..000000000 --- a/packages/cli/package/src/commands/deal/withdraw.ts +++ /dev/null @@ -1,82 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import { color } from "@oclif/color"; -import { Args } from "@oclif/core"; - -import { BaseCommand } from "../../baseCommand.js"; -import { ptFormatWithSymbol, ptParse } from "../../lib/chain/currencies.js"; -import { commandObj } from "../../lib/commandObj.js"; -import { - CHAIN_FLAGS, - DEAL_IDS_FLAG, - DEPLOYMENT_NAMES_ARG, - PT_SYMBOL, -} from "../../lib/const.js"; -import { getDeals } from "../../lib/deal.js"; -import { getContracts, sign } from "../../lib/dealClient.js"; -import { initCli } from "../../lib/lifeCycle.js"; -import { input } from "../../lib/prompt.js"; - -export default class Withdraw extends BaseCommand { - static override description = "Withdraw tokens from the deal"; - static override flags = { - ...CHAIN_FLAGS, - ...DEAL_IDS_FLAG, - }; - - static override args = { - AMOUNT: Args.string({ - description: `Amount of ${PT_SYMBOL} tokens to withdraw`, - }), - ...DEPLOYMENT_NAMES_ARG, - }; - - async run(): Promise { - const flagsAndArgs = await initCli(this, await this.parse(Withdraw)); - const { contracts } = await getContracts(); - const deals = await getDeals(flagsAndArgs); - - const amount = - flagsAndArgs.args["AMOUNT"] ?? - (await input({ - message: `Enter amount of ${PT_SYMBOL} tokens to withdraw`, - })); - - const parsedAmount = await ptParse(amount); - - const formattedAmount = color.yellow( - await ptFormatWithSymbol(parsedAmount), - ); - - for (const { dealId, dealName } of deals) { - const deal = contracts.getDeal(dealId); - - await sign({ - title: `Withdraw ${await ptFormatWithSymbol(parsedAmount)} tokens from the deal ${dealName}`, - method: deal.withdraw, - args: [parsedAmount], - }); - - commandObj.logToStderr( - `${formattedAmount} tokens were withdrawn from the deal ${color.yellow( - dealName, - )}`, - ); - } - } -} diff --git a/packages/cli/package/src/commands/deal/workers-add.ts b/packages/cli/package/src/commands/deal/workers-add.ts deleted file mode 100644 index 532dd690c..000000000 --- a/packages/cli/package/src/commands/deal/workers-add.ts +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import { BaseCommand } from "../../baseCommand.js"; -import { - CHAIN_FLAGS, - DEAL_IDS_FLAG, - DEPLOYMENT_NAMES_ARG, -} from "../../lib/const.js"; -import { match, getDeals } from "../../lib/deal.js"; -import { aliasesText } from "../../lib/helpers/aliasesText.js"; -import { initCli } from "../../lib/lifeCycle.js"; - -export default class Match extends BaseCommand { - static override hiddenAliases = ["deal:wa"]; - static override description = `Add missing workers to the deal${aliasesText.apply(this)}`; - static override flags = { - ...CHAIN_FLAGS, - ...DEAL_IDS_FLAG, - }; - - static override args = { - ...DEPLOYMENT_NAMES_ARG, - }; - - async run(): Promise { - const flagsAndArgs = await initCli(this, await this.parse(Match)); - const deals = await getDeals(flagsAndArgs); - - for (const { dealId } of deals) { - await match(dealId); - } - } -} diff --git a/packages/cli/package/src/commands/deal/workers-remove.ts b/packages/cli/package/src/commands/deal/workers-remove.ts deleted file mode 100644 index 126d88c62..000000000 --- a/packages/cli/package/src/commands/deal/workers-remove.ts +++ /dev/null @@ -1,93 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import { color } from "@oclif/color"; -import { Args, Flags } from "@oclif/core"; - -import { BaseCommand } from "../../baseCommand.js"; -import { commandObj } from "../../lib/commandObj.js"; -import { - CHAIN_FLAGS, - CLI_NAME, - WORKERS_CONFIG_FULL_FILE_NAME, -} from "../../lib/const.js"; -import { getDeal } from "../../lib/deal.js"; -import { sign, getContracts } from "../../lib/dealClient.js"; -import { aliasesText } from "../../lib/helpers/aliasesText.js"; -import { commaSepStrToArr } from "../../lib/helpers/utils.js"; -import { initCli } from "../../lib/lifeCycle.js"; -import { input } from "../../lib/prompt.js"; - -export default class WorkersRemove extends BaseCommand { - static override hiddenAliases = ["deal:wr"]; - static override description = `Remove unit from the deal${aliasesText.apply(this)}`; - static override flags = { - ...CHAIN_FLAGS, - "deal-id": Flags.string({ - description: `Deal id. You can get it using '${CLI_NAME} deal info' command`, - }), - name: Flags.string({ - description: `Name of the deployment from ${WORKERS_CONFIG_FULL_FILE_NAME}`, - }), - }; - - static override args = { - "WORKER-IDS": Args.string({ - description: `Comma-separated compute unit ids. You can get them using '${CLI_NAME} deal info' command`, - }), - }; - - async run(): Promise { - const { flags, args } = await initCli( - this, - await this.parse(WorkersRemove), - ); - - const { contracts } = await getContracts(); - const { dealId } = await getDeal({ flags }); - const dealContract = contracts.getDeal(dealId); - - const workerIds = commaSepStrToArr( - args["WORKER-IDS"] ?? - (await input({ - message: "Enter comma-separated worker ids", - validate(v: string) { - return ( - commaSepStrToArr(v).length > 0 || - "Please provide at least one worker id" - ); - }, - })), - ); - - if (workerIds.length === 0) { - commandObj.error("No worker ids provided"); - } - - for (const workerId of workerIds) { - await sign({ - title: `Remove worker ${workerId} from deal`, - method: dealContract.removeWorker, - args: [workerId], - }); - - commandObj.log( - `Worker ${color.yellow(workerId)} was removed from the deal`, - ); - } - } -} diff --git a/packages/cli/package/src/commands/default/env.ts b/packages/cli/package/src/commands/default/env.ts index 422ee2134..8c63ed871 100644 --- a/packages/cli/package/src/commands/default/env.ts +++ b/packages/cli/package/src/commands/default/env.ts @@ -20,11 +20,8 @@ import { color } from "@oclif/color"; import { BaseCommand } from "../../baseCommand.js"; import { commandObj } from "../../lib/commandObj.js"; import { initNewEnvConfig } from "../../lib/configs/project/env/env.js"; -import { initFluenceConfig } from "../../lib/configs/project/fluence.js"; import { ENV_ARG } from "../../lib/const.js"; -import { ensureAquaFileWithWorkerInfo } from "../../lib/deployWorkers.js"; import { initCli } from "../../lib/lifeCycle.js"; -import { updateRelaysJSON } from "../../lib/multiaddres.js"; import { ensureValidFluenceEnv } from "../../lib/resolveFluenceEnv.js"; export default class Env extends BaseCommand { @@ -40,11 +37,6 @@ export default class Env extends BaseCommand { envConfig.fluenceEnv = fluenceEnv; await envConfig.$commit(); - if ((await initFluenceConfig()) !== null) { - await updateRelaysJSON(); - await ensureAquaFileWithWorkerInfo(); - } - commandObj.log( `Successfully set default fluence environment to ${color.yellow( fluenceEnv, diff --git a/packages/cli/package/src/commands/default/peers.ts b/packages/cli/package/src/commands/default/peers.ts deleted file mode 100644 index 77c83ef60..000000000 --- a/packages/cli/package/src/commands/default/peers.ts +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import { BaseCommand } from "../../baseCommand.js"; -import { setChainFlags } from "../../lib/chainFlags.js"; -import { commandObj } from "../../lib/commandObj.js"; -import { ENV_ARG, ENV_ARG_NAME, ENV_FLAG_NAME } from "../../lib/const.js"; -import { initCli } from "../../lib/lifeCycle.js"; -import { resolveDefaultRelays } from "../../lib/multiaddres.js"; - -export default class Peers extends BaseCommand { - static override description = "Print default Fluence network peer addresses"; - static override examples = ["<%= config.bin %> <%= command.id %>"]; - static override args = { - ...ENV_ARG, - }; - async run(): Promise { - const { args } = await initCli(this, await this.parse(Peers)); - setChainFlags({ [ENV_FLAG_NAME]: args[ENV_ARG_NAME] }); - const relays = await resolveDefaultRelays(); - commandObj.log(relays.join("\n")); - } -} diff --git a/packages/cli/package/src/commands/delegator/collateral-add.ts b/packages/cli/package/src/commands/delegator/collateral-add.ts deleted file mode 100644 index c95151c19..000000000 --- a/packages/cli/package/src/commands/delegator/collateral-add.ts +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import { Args } from "@oclif/core"; - -import { BaseCommand } from "../../baseCommand.js"; -import { depositCollateral } from "../../lib/chain/depositCollateral.js"; -import { CC_IDS_FLAG_NAME, CHAIN_FLAGS, FLT_SYMBOL } from "../../lib/const.js"; -import { aliasesText } from "../../lib/helpers/aliasesText.js"; -import { initCli } from "../../lib/lifeCycle.js"; - -export default class CollateralAdd extends BaseCommand { - static override hiddenAliases = ["delegator:ca"]; - static override description = `Add ${FLT_SYMBOL} collateral to capacity commitment${aliasesText.apply(this)}`; - static override flags = { - ...CHAIN_FLAGS, - }; - static override args = { - IDS: Args.string({ - description: "Comma separated capacity commitment IDs", - }), - }; - - async run(): Promise { - const { args } = await initCli(this, await this.parse(CollateralAdd)); - await depositCollateral({ [CC_IDS_FLAG_NAME]: args.IDS }); - } -} diff --git a/packages/cli/package/src/commands/delegator/collateral-withdraw.ts b/packages/cli/package/src/commands/delegator/collateral-withdraw.ts deleted file mode 100644 index b65dad9dc..000000000 --- a/packages/cli/package/src/commands/delegator/collateral-withdraw.ts +++ /dev/null @@ -1,60 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import { Args, Flags } from "@oclif/core"; - -import { BaseCommand } from "../../baseCommand.js"; -import { collateralWithdraw } from "../../lib/chain/commitment.js"; -import { - CC_IDS_FLAG_NAME, - CHAIN_FLAGS, - FINISH_COMMITMENT_FLAG_NAME, - FLT_SYMBOL, -} from "../../lib/const.js"; -import { aliasesText } from "../../lib/helpers/aliasesText.js"; -import { initCli } from "../../lib/lifeCycle.js"; - -export default class CollateralWithdraw extends BaseCommand< - typeof CollateralWithdraw -> { - static override hiddenAliases = ["delegator:cw"]; - static override description = `Withdraw ${FLT_SYMBOL} collateral from capacity commitment${aliasesText.apply(this)}`; - static override flags = { - ...CHAIN_FLAGS, - [FINISH_COMMITMENT_FLAG_NAME]: Flags.boolean({ - description: `Finish capacity commitment after collateral withdrawal`, - default: false, - }), - }; - static override args = { - IDS: Args.string({ - description: "Comma separated capacity commitment IDs", - }), - }; - - async run(): Promise { - const { args, flags } = await initCli( - this, - await this.parse(CollateralWithdraw), - ); - - await collateralWithdraw({ - [CC_IDS_FLAG_NAME]: args.IDS, - [FINISH_COMMITMENT_FLAG_NAME]: flags[FINISH_COMMITMENT_FLAG_NAME], - }); - } -} diff --git a/packages/cli/package/src/commands/delegator/reward-withdraw.ts b/packages/cli/package/src/commands/delegator/reward-withdraw.ts deleted file mode 100644 index dad0747f6..000000000 --- a/packages/cli/package/src/commands/delegator/reward-withdraw.ts +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import { Args } from "@oclif/core"; - -import { BaseCommand } from "../../baseCommand.js"; -import { collateralRewardWithdraw } from "../../lib/chain/commitment.js"; -import { CC_IDS_FLAG_NAME, CHAIN_FLAGS, FLT_SYMBOL } from "../../lib/const.js"; -import { aliasesText } from "../../lib/helpers/aliasesText.js"; -import { initCli } from "../../lib/lifeCycle.js"; - -export default class RewardWithdraw extends BaseCommand { - static override hiddenAliases = ["delegator:rw"]; - static override description = `Withdraw ${FLT_SYMBOL} rewards from capacity commitment${aliasesText.apply(this)}`; - static override flags = { - ...CHAIN_FLAGS, - }; - static override args = { - IDS: Args.string({ - description: "Comma separated capacity commitment IDs", - }), - }; - - async run(): Promise { - const { args } = await initCli(this, await this.parse(RewardWithdraw)); - await collateralRewardWithdraw({ [CC_IDS_FLAG_NAME]: args.IDS }); - } -} diff --git a/packages/cli/package/src/commands/dep/install.ts b/packages/cli/package/src/commands/dep/install.ts deleted file mode 100644 index c25ed59ea..000000000 --- a/packages/cli/package/src/commands/dep/install.ts +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import { Args } from "@oclif/core"; - -import { BaseCommand } from "../../baseCommand.js"; -import { commandObj } from "../../lib/commandObj.js"; -import { initFluenceConfig } from "../../lib/configs/project/fluence.js"; -import { PACKAGE_NAME_AND_VERSION_ARG_NAME } from "../../lib/const.js"; -import { aliasesText } from "../../lib/helpers/aliasesText.js"; -import { startSpinner, stopSpinner } from "../../lib/helpers/spinner.js"; -import { initCli } from "../../lib/lifeCycle.js"; -import { npmInstall, npmInstallAll } from "../../lib/npm.js"; -import { ensureMarineAndMreplDependencies } from "../../lib/rust.js"; - -export default class Install extends BaseCommand { - static override hiddenAliases = ["dep:i"]; - static override description = `Install aqua project dependencies (currently npm is used under the hood for managing aqua dependencies)${aliasesText.apply(this)}`; - static override examples = ["<%= config.bin %> <%= command.id %>"]; - static override args = { - [PACKAGE_NAME_AND_VERSION_ARG_NAME]: Args.string({ - description: - "Valid argument for npm install command. If this argument is omitted all project aqua dependencies will be installed and command will also make sure marine and mrepl are installed", - }), - }; - async run(): Promise { - const { args } = await initCli(this, await this.parse(Install)); - const fluenceConfig = await initFluenceConfig(); - - const packageNameAndVersion = args[PACKAGE_NAME_AND_VERSION_ARG_NAME]; - - if (packageNameAndVersion === undefined) { - if (fluenceConfig !== null) { - startSpinner("Installing aqua dependencies"); - await npmInstallAll(); - stopSpinner(); - commandObj.logToStderr("Aqua dependencies are successfully installed"); - } - - await ensureMarineAndMreplDependencies(); - return; - } - - if (fluenceConfig === null) { - commandObj.error( - "Not a fluence project. Please init fluence project to install specific aqua dependencies", - ); - } - - await npmInstall(packageNameAndVersion); - } -} diff --git a/packages/cli/package/src/commands/dep/reset.ts b/packages/cli/package/src/commands/dep/reset.ts deleted file mode 100644 index 3dc2aa6bc..000000000 --- a/packages/cli/package/src/commands/dep/reset.ts +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import { BaseCommand } from "../../baseCommand.js"; -import { commandObj } from "../../lib/commandObj.js"; -import { aliasesText } from "../../lib/helpers/aliasesText.js"; -import { ensureFluenceProject } from "../../lib/helpers/ensureFluenceProject.js"; -import { initCli } from "../../lib/lifeCycle.js"; -import { npmInstallAll } from "../../lib/npm.js"; -import { versions } from "../../versions.js"; - -export default class Reset extends BaseCommand { - static override hiddenAliases = ["dep:r"]; - static override description = `Reset all project dependencies to recommended versions${aliasesText.apply(this)}`; - static override examples = ["<%= config.bin %> <%= command.id %>"]; - async run(): Promise { - await initCli(this, await this.parse(Reset)); - const fluenceConfig = await ensureFluenceProject(); - - if (fluenceConfig.mreplVersion !== undefined) { - delete fluenceConfig.mreplVersion; - } - - if (fluenceConfig.marineVersion !== undefined) { - delete fluenceConfig.marineVersion; - } - - if (fluenceConfig.rustToolchain !== undefined) { - delete fluenceConfig.rustToolchain; - } - - await fluenceConfig.$commit(); - - fluenceConfig.aquaDependencies = { - ...fluenceConfig.aquaDependencies, - ...versions.npm, - }; - - await npmInstallAll(); - await fluenceConfig.$commit(); - commandObj.log("Successfully reset project dependencies versions"); - } -} diff --git a/packages/cli/package/src/commands/dep/uninstall.ts b/packages/cli/package/src/commands/dep/uninstall.ts deleted file mode 100644 index d13a2259c..000000000 --- a/packages/cli/package/src/commands/dep/uninstall.ts +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import { Args } from "@oclif/core"; - -import { BaseCommand } from "../../baseCommand.js"; -import { commandObj } from "../../lib/commandObj.js"; -import { PACKAGE_NAME } from "../../lib/const.js"; -import { aliasesText } from "../../lib/helpers/aliasesText.js"; -import { initCli } from "../../lib/lifeCycle.js"; -import { npmUninstall } from "../../lib/npm.js"; - -export default class Install extends BaseCommand { - static override hiddenAliases = ["dep:un"]; - static override description = `Uninstall aqua project dependencies (currently npm is used under the hood for managing aqua dependencies)${aliasesText.apply(this)}`; - static override examples = ["<%= config.bin %> <%= command.id %>"]; - static override args = { - [PACKAGE_NAME]: Args.string({ - description: `Aqua dependency name`, - required: true, - }), - }; - - async run(): Promise { - const { args } = await initCli(this, await this.parse(Install), true); - await npmUninstall(args[PACKAGE_NAME]); - commandObj.logToStderr(`Uninstalled ${args[PACKAGE_NAME]} successfully`); - } -} diff --git a/packages/cli/package/src/commands/dep/versions.ts b/packages/cli/package/src/commands/dep/versions.ts deleted file mode 100644 index c33b6dc6b..000000000 --- a/packages/cli/package/src/commands/dep/versions.ts +++ /dev/null @@ -1,123 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import { Flags } from "@oclif/core"; - -import { BaseCommand } from "../../baseCommand.js"; -import internalAquaDependencies from "../../cli-aqua-dependencies/package.json" with { type: "json" }; -import { jsonStringify } from "../../common.js"; -import { commandObj } from "../../lib/commandObj.js"; -import { initFluenceConfig } from "../../lib/configs/project/fluence.js"; -import { CLI_NAME_FULL, JSON_FLAG } from "../../lib/const.js"; -import { aliasesText } from "../../lib/helpers/aliasesText.js"; -import { initCli } from "../../lib/lifeCycle.js"; -import { - getRustToolchainToUse, - getMarineOrMreplVersion, -} from "../../lib/rust.js"; -import CLIPackageJSON from "../../versions/cli.package.json" with { type: "json" }; -import JSClientPackageJSON from "../../versions/js-client.package.json" with { type: "json" }; -import { versions } from "../../versions.js"; - -export default class Versions extends BaseCommand { - static override hiddenAliases = ["dep:v"]; - static override description = `Get versions of all cli dependencies, including aqua, marine, mrepl and internal${aliasesText.apply(this)}`; - static override examples = ["<%= config.bin %> <%= command.id %>"]; - static override flags = { - default: Flags.boolean({ - description: - "Display default npm and cargo dependencies and their versions for current CLI version. Default npm dependencies are always available to be imported in Aqua", - }), - ...JSON_FLAG, - }; - async run(): Promise { - const { flags } = await initCli(this, await this.parse(Versions)); - const { yamlDiffPatch } = await import("yaml-diff-patch"); - - const devDependencies = filterOutNonFluenceDependencies( - CLIPackageJSON.devDependencies, - ); - - const defaultDeps = { - [`${CLI_NAME_FULL} version`]: commandObj.config.version, - "internal aqua dependencies": internalAquaDependencies.dependencies, - "nox version": versions["nox"], - "internal dependencies": filterOutNonFluenceDependencies( - CLIPackageJSON.dependencies, - ), - ...(Object.keys(devDependencies).length === 0 - ? {} - : { - "internal dev dependencies": filterOutNonFluenceDependencies( - CLIPackageJSON.devDependencies, - ), - }), - "js-client dependencies": filterOutNonFluenceDependencies( - JSClientPackageJSON.dependencies, - ), - [RUST_TOOLCHAIN]: versions["rust-toolchain"], - [AQUA_DEPENDENCIES]: versions.npm, - [TOOLS]: { - [MARINE]: versions.cargo.marine, - [MREPL]: versions.cargo.mrepl, - }, - }; - - if (flags.default) { - commandObj.log( - flags.json - ? jsonStringify(defaultDeps) - : yamlDiffPatch("", {}, defaultDeps), - ); - - return; - } - - const fluenceConfig = await initFluenceConfig(); - - const deps = { - ...defaultDeps, - [RUST_TOOLCHAIN]: await getRustToolchainToUse(), - [AQUA_DEPENDENCIES]: - fluenceConfig === null ? versions.npm : fluenceConfig.aquaDependencies, - [TOOLS]: { - [MARINE]: await getMarineOrMreplVersion("marine"), - [MREPL]: await getMarineOrMreplVersion("mrepl"), - }, - }; - - commandObj.log( - flags.json ? jsonStringify(deps) : yamlDiffPatch("", {}, deps), - ); - } -} - -const RUST_TOOLCHAIN = "rust-toolchain"; -const AQUA_DEPENDENCIES = "aqua-dependencies"; -const TOOLS = "tools"; -const MARINE = "marine"; -const MREPL = "mrepl"; - -const filterOutNonFluenceDependencies = ( - dependencies: Record, -) => { - return Object.fromEntries( - Object.entries(dependencies).filter(([dep]) => { - return dep.startsWith("@fluencelabs/"); - }), - ); -}; diff --git a/packages/cli/package/src/commands/deploy.ts b/packages/cli/package/src/commands/deploy.ts deleted file mode 100644 index 18c5456ed..000000000 --- a/packages/cli/package/src/commands/deploy.ts +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import { BaseCommand } from "../baseCommand.js"; -import { DEPLOYMENT_NAMES_ARG } from "../lib/const.js"; -import { - DEPLOY_DESCRIPTION, - DEPLOY_EXAMPLES, - DEPLOY_FLAGS, - deployImpl, -} from "../lib/deploy.js"; - -export default class Deploy extends BaseCommand { - static override description = DEPLOY_DESCRIPTION; - static override examples = DEPLOY_EXAMPLES; - static override flags = DEPLOY_FLAGS; - static override args = DEPLOYMENT_NAMES_ARG; - async run(): Promise { - await deployImpl.bind(this)(Deploy); - } -} diff --git a/packages/cli/package/src/commands/init.ts b/packages/cli/package/src/commands/init.ts deleted file mode 100644 index b98356696..000000000 --- a/packages/cli/package/src/commands/init.ts +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import { Args, Flags } from "@oclif/core"; - -import { BaseCommand } from "../baseCommand.js"; -import { TEMPLATES, ENV_FLAG, NOXES_FLAG } from "../lib/const.js"; -import { ensureTemplate, init } from "../lib/init.js"; -import { initCli } from "../lib/lifeCycle.js"; - -export default class Init extends BaseCommand { - static override description = "Initialize fluence project"; - static override examples = ["<%= config.bin %> <%= command.id %>"]; - static override flags = { - template: Flags.string({ - description: `Template to use for the project. One of: ${TEMPLATES.join( - ", ", - )}`, - char: "t", - }), - ...ENV_FLAG, - ...NOXES_FLAG, - }; - static override args = { - path: Args.string({ - description: "Project path", - }), - }; - async run(): Promise { - const { flags, args } = await initCli(this, await this.parse(Init)); - - await init({ - ...flags, - maybeProjectPath: args.path, - template: await ensureTemplate({ - templateOrUnknown: flags.template, - }), - }); - } -} diff --git a/packages/cli/package/src/commands/key/default.ts b/packages/cli/package/src/commands/key/default.ts deleted file mode 100644 index df67b20f7..000000000 --- a/packages/cli/package/src/commands/key/default.ts +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import { Args, Flags } from "@oclif/core"; - -import { BaseCommand } from "../../baseCommand.js"; -import { initCli } from "../../lib/lifeCycle.js"; -import { setDefaultSecretKey } from "../../lib/secretKeys.js"; - -export default class Default extends BaseCommand { - static override description = "Set default key-pair for user or project"; - static override examples = ["<%= config.bin %> <%= command.id %>"]; - static override flags = { - user: Flags.boolean({ - default: false, - description: - "Set default key-pair for current user instead of current project", - }), - }; - static override args = { - name: Args.string({ - description: "Key-pair name", - }), - }; - async run(): Promise { - const { args, flags } = await initCli(this, await this.parse(Default)); - await setDefaultSecretKey({ isUser: flags.user, name: args.name }); - } -} diff --git a/packages/cli/package/src/commands/key/new.ts b/packages/cli/package/src/commands/key/new.ts deleted file mode 100644 index bcfb67ed7..000000000 --- a/packages/cli/package/src/commands/key/new.ts +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import { Args, Flags } from "@oclif/core"; - -import { BaseCommand } from "../../baseCommand.js"; -import { - PROJECT_SECRETS_FULL_CONFIG_FILE_NAME, - USER_SECRETS_CONFIG_FULL_FILE_NAME, -} from "../../lib/const.js"; -import { createSecretKey } from "../../lib/keyPairs.js"; -import { initCli } from "../../lib/lifeCycle.js"; -import { resolveUserOrProjectConfig } from "../../lib/secretKeys.js"; - -export default class New extends BaseCommand { - static override description = `Generate key-pair and store it in ${USER_SECRETS_CONFIG_FULL_FILE_NAME} or ${PROJECT_SECRETS_FULL_CONFIG_FILE_NAME}`; - static override examples = ["<%= config.bin %> <%= command.id %>"]; - static override flags = { - user: Flags.boolean({ - default: false, - description: - "Generate key-pair for current user instead of generating key-pair for current project", - }), - default: Flags.boolean({ - default: false, - description: "Set new key-pair as default for current project or user", - }), - }; - static override args = { - name: Args.string({ - description: "Key-pair name", - }), - }; - async run(): Promise { - const { args, flags } = await initCli(this, await this.parse(New)); - - await createSecretKey({ - isUser: flags.user, - name: args.name, - userOrProjectConfig: await resolveUserOrProjectConfig(flags.user), - }); - } -} diff --git a/packages/cli/package/src/commands/key/remove.ts b/packages/cli/package/src/commands/key/remove.ts deleted file mode 100644 index 16a156711..000000000 --- a/packages/cli/package/src/commands/key/remove.ts +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import { Args, Flags } from "@oclif/core"; - -import { BaseCommand } from "../../baseCommand.js"; -import { - PROJECT_SECRETS_FULL_CONFIG_FILE_NAME, - USER_SECRETS_CONFIG_FULL_FILE_NAME, -} from "../../lib/const.js"; -import { initCli } from "../../lib/lifeCycle.js"; -import { removeSecretKey } from "../../lib/secretKeys.js"; - -export default class Remove extends BaseCommand { - static override description = `Remove key-pair from ${USER_SECRETS_CONFIG_FULL_FILE_NAME} or ${PROJECT_SECRETS_FULL_CONFIG_FILE_NAME}`; - static override examples = ["<%= config.bin %> <%= command.id %>"]; - static override flags = { - user: Flags.boolean({ - default: false, - description: - "Remove key-pair from current user instead of removing key-pair from current project", - }), - }; - static override args = { - name: Args.string({ - description: "Key-pair name", - }), - }; - async run(): Promise { - const { args, flags } = await initCli(this, await this.parse(Remove)); - await removeSecretKey({ name: args.name, isUser: flags.user }); - } -} diff --git a/packages/cli/package/src/commands/local/up.ts b/packages/cli/package/src/commands/local/up.ts index b13a53c68..e49e1fc1a 100644 --- a/packages/cli/package/src/commands/local/up.ts +++ b/packages/cli/package/src/commands/local/up.ts @@ -24,26 +24,23 @@ import { BaseCommand } from "../../baseCommand.js"; import { LOCAL_NET_DEFAULT_WALLET_KEY } from "../../common.js"; import { createCommitments } from "../../lib/chain/commitment.js"; import { depositCollateral } from "../../lib/chain/depositCollateral.js"; -import { distributeToNox } from "../../lib/chain/distributeToNox.js"; +import { distributeToPeer } from "../../lib/chain/distributeToNox.js"; import { createOffers } from "../../lib/chain/offer/offer.js"; import { registerProvider } from "../../lib/chain/providerInfo.js"; import { setChainFlags } from "../../lib/chainFlags.js"; import { ensureDockerComposeConfig } from "../../lib/configs/project/dockerCompose.js"; import { initNewEnvConfig } from "../../lib/configs/project/env/env.js"; -import { initFluenceConfig } from "../../lib/configs/project/fluence.js"; -import { initNewWorkersConfig } from "../../lib/configs/project/workers.js"; import { DOCKER_COMPOSE_FLAGS, OFFER_FLAG_NAME, ALL_FLAG_VALUE, DOCKER_COMPOSE_FULL_FILE_NAME, - NOXES_FLAG, + PEERS_FLAG, PRIV_KEY_FLAG, PROVIDER_CONFIG_FULL_FILE_NAME, type FluenceEnv, PRIV_KEY_FLAG_NAME, } from "../../lib/const.js"; -import { ensureAquaFileWithWorkerInfo } from "../../lib/deployWorkers.js"; import { dockerCompose } from "../../lib/dockerCompose.js"; import { initCli } from "../../lib/lifeCycle.js"; import { @@ -55,7 +52,7 @@ export default class Up extends BaseCommand { static override description = `Run ${DOCKER_COMPOSE_FULL_FILE_NAME} using docker compose and set up provider using all the offers from the 'offers' section in ${PROVIDER_CONFIG_FULL_FILE_NAME} config using default wallet key ${LOCAL_NET_DEFAULT_WALLET_KEY}`; static override examples = ["<%= config.bin %> <%= command.id %>"]; static override flags = { - ...NOXES_FLAG, + ...PEERS_FLAG, timeout: Flags.integer({ description: "Timeout in seconds for attempting to register local network on local peers", @@ -125,17 +122,6 @@ export default class Up extends BaseCommand { try { await rm(await ensureProviderArtifactsConfigPath()); } catch {} - - const workersConfig = await initNewWorkersConfig(); - - if (workersConfig.deals !== undefined) { - delete workersConfig.deals.local; - await workersConfig.$commit(); - - if ((await initFluenceConfig()) !== null) { - await ensureAquaFileWithWorkerInfo(); - } - } } await ensureDockerComposeConfig(); @@ -160,7 +146,7 @@ export default class Up extends BaseCommand { } const allOffers = { [OFFER_FLAG_NAME]: ALL_FLAG_VALUE }; - await distributeToNox({ ...flags, ...allOffers, amount: "10" }); + await distributeToPeer({ ...flags, ...allOffers, amount: "10" }); await registerProvider(); await createOffers({ force: true, ...allOffers }); await createCommitments({ ...flags, ...allOffers }); diff --git a/packages/cli/package/src/commands/module/add.ts b/packages/cli/package/src/commands/module/add.ts deleted file mode 100644 index 50e75df5f..000000000 --- a/packages/cli/package/src/commands/module/add.ts +++ /dev/null @@ -1,150 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import assert from "node:assert"; -import { relative } from "node:path"; -import { cwd } from "node:process"; - -import { color } from "@oclif/color"; -import { Args, Flags } from "@oclif/core"; - -import { BaseCommand } from "../../baseCommand.js"; -import { commandObj } from "../../lib/commandObj.js"; -import { initFluenceConfig } from "../../lib/configs/project/fluence.js"; -import { initReadonlyModuleConfig } from "../../lib/configs/project/module.js"; -import { ensureServiceConfig } from "../../lib/configs/project/service.js"; -import { - FLUENCE_CONFIG_FULL_FILE_NAME, - MODULE_CONFIG_FULL_FILE_NAME, - SERVICE_CONFIG_FULL_FILE_NAME, -} from "../../lib/const.js"; -import { isUrl } from "../../lib/helpers/downloadFile.js"; -import { initCli } from "../../lib/lifeCycle.js"; -import { input } from "../../lib/prompt.js"; -import { hasKey } from "../../lib/typeHelpers.js"; - -const PATH_OR_URL = "PATH | URL"; - -export default class Add extends BaseCommand { - static override description = `Add module to ${SERVICE_CONFIG_FULL_FILE_NAME}`; - static override examples = ["<%= config.bin %> <%= command.id %>"]; - static override flags = { - name: Flags.string({ - description: "Override module name", - helpValue: "", - }), - service: Flags.directory({ - description: `Service name from ${FLUENCE_CONFIG_FULL_FILE_NAME} or path to the service config or directory that contains ${SERVICE_CONFIG_FULL_FILE_NAME}`, - helpValue: "", - }), - }; - static override args = { - [PATH_OR_URL]: Args.string({ - description: "Path to a module or url to .tar.gz archive", - }), - }; - async run(): Promise { - const { args, flags } = await initCli(this, await this.parse(Add)); - - const modulePathOrUrl = - args[PATH_OR_URL] ?? - (await input({ - message: "Enter path to a module or url to .tar.gz archive", - })); - - const moduleConfig = await initReadonlyModuleConfig(modulePathOrUrl, cwd()); - - if (moduleConfig === null) { - return commandObj.error( - `${color.yellow( - MODULE_CONFIG_FULL_FILE_NAME, - )} not found for ${modulePathOrUrl}`, - ); - } - - const fluenceConfig = await initFluenceConfig(); - - const serviceNameOrPath = - flags.service ?? - (await input({ - message: - fluenceConfig === null - ? `Enter path to the service directory` - : `Enter service name from ${color.yellow( - fluenceConfig.$getPath(), - )} or path to the service directory`, - })); - - let serviceOrServiceDirPathOrUrl = serviceNameOrPath; - - if (hasKey(serviceNameOrPath, fluenceConfig?.services)) { - const serviceGet = fluenceConfig.services[serviceNameOrPath]?.get; - assert(typeof serviceGet === "string"); - serviceOrServiceDirPathOrUrl = serviceGet; - } - - if (isUrl(serviceOrServiceDirPathOrUrl)) { - return commandObj.error( - `Can't modify downloaded service ${color.yellow( - serviceOrServiceDirPathOrUrl, - )}`, - ); - } - - const serviceConfig = await ensureServiceConfig( - serviceOrServiceDirPathOrUrl, - ); - - const validateModuleName = (name: string): true | string => { - return ( - !(name in serviceConfig.modules) || - `You already have ${color.yellow(name)} in ${color.yellow( - serviceConfig.$getPath(), - )}` - ); - }; - - let moduleName = flags.name ?? moduleConfig.name; - const moduleNameValidity = validateModuleName(moduleName); - - if (moduleNameValidity !== true) { - this.warn(moduleNameValidity); - - moduleName = await input({ - message: `Enter another name for module`, - validate: validateModuleName, - }); - } - - serviceConfig.modules = { - ...serviceConfig.modules, - [moduleName]: { - get: isUrl(modulePathOrUrl) - ? modulePathOrUrl - : relative(serviceConfig.$getDirPath(), moduleConfig.$getPath()), - }, - }; - - await serviceConfig.$commit(); - - commandObj.log( - `Added ${color.yellow(moduleName)} to ${color.yellow( - serviceConfig.$getPath(), - )}`, - ); - } -} diff --git a/packages/cli/package/src/commands/module/build.ts b/packages/cli/package/src/commands/module/build.ts deleted file mode 100644 index 3375df25f..000000000 --- a/packages/cli/package/src/commands/module/build.ts +++ /dev/null @@ -1,76 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import { cwd } from "node:process"; - -import { color } from "@oclif/color"; -import { Args } from "@oclif/core"; - -import { BaseCommand } from "../../baseCommand.js"; -import { buildModules } from "../../lib/buildModules.js"; -import { commandObj } from "../../lib/commandObj.js"; -import { initReadonlyModuleConfig } from "../../lib/configs/project/module.js"; -import { - MARINE_BUILD_ARGS_FLAG, - MARINE_BUILD_ARGS_FLAG_NAME, - MODULE_CONFIG_FULL_FILE_NAME, -} from "../../lib/const.js"; -import { initCli } from "../../lib/lifeCycle.js"; -import { initMarineCli } from "../../lib/marineCli.js"; -import { input } from "../../lib/prompt.js"; - -const PATH = "PATH"; - -export default class Build extends BaseCommand { - static override description = `Build module`; - static override examples = ["<%= config.bin %> <%= command.id %>"]; - static override flags = { - ...MARINE_BUILD_ARGS_FLAG, - }; - static override args = { - [PATH]: Args.string({ - description: "Path to a module", - }), - }; - async run(): Promise { - const { args, flags } = await initCli(this, await this.parse(Build)); - - const modulePath = - args[PATH] ?? - (await input({ - message: "Enter path to a module", - })); - - const moduleConfig = await initReadonlyModuleConfig(modulePath, cwd()); - - if (moduleConfig === null) { - return commandObj.error( - `${color.yellow( - MODULE_CONFIG_FULL_FILE_NAME, - )} not found for ${modulePath}`, - ); - } - - const marineCli = await initMarineCli(); - - await buildModules( - [moduleConfig], - marineCli, - flags[MARINE_BUILD_ARGS_FLAG_NAME], - ); - } -} diff --git a/packages/cli/package/src/commands/module/new.ts b/packages/cli/package/src/commands/module/new.ts deleted file mode 100644 index e7d8b33bc..000000000 --- a/packages/cli/package/src/commands/module/new.ts +++ /dev/null @@ -1,127 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import { join, relative } from "path"; - -import { color } from "@oclif/color"; -import { Args, Flags } from "@oclif/core"; - -import { BaseCommand } from "../../baseCommand.js"; -import { commandObj } from "../../lib/commandObj.js"; -import { initFluenceConfig } from "../../lib/configs/project/fluence.js"; -import { ensureServiceConfig } from "../../lib/configs/project/service.js"; -import { generateNewModule } from "../../lib/generateNewModule.js"; -import { isUrl } from "../../lib/helpers/downloadFile.js"; -import { initCli } from "../../lib/lifeCycle.js"; -import { ensureModulesDir } from "../../lib/paths.js"; -import { input } from "../../lib/prompt.js"; - -export default class New extends BaseCommand { - static override description = "Create new marine module template"; - static override examples = ["<%= config.bin %> <%= command.id %>"]; - static override flags = { - path: Flags.string({ - description: "Path to module dir (default: src/modules)", - helpValue: "", - }), - service: Flags.string({ - description: - "Name or relative path to the service to add the created module to", - helpValue: "", - }), - }; - static override args = { - name: Args.string({ - description: "Module name", - }), - }; - async run(): Promise { - const { args, flags } = await initCli(this, await this.parse(New)); - - if (typeof args.name === "string") { - const moduleNameValidity = validateModuleName(args.name); - - if (moduleNameValidity !== true) { - commandObj.warn(moduleNameValidity); - args.name = undefined; - } - } - - const moduleName = - args.name ?? - (await input({ - message: "Enter module name", - validate: validateModuleName, - })); - - const pathToModulesDir = flags.path ?? (await ensureModulesDir()); - const pathToModuleDir = join(pathToModulesDir, moduleName); - const fluenceConfig = await initFluenceConfig(); - - const serviceName = - flags.service === undefined - ? undefined - : fluenceConfig?.services?.[flags.service] === undefined - ? undefined - : flags.service; - - await generateNewModule(pathToModuleDir, serviceName); - - commandObj.log( - `Successfully generated template for new module at ${color.yellow( - pathToModuleDir, - )}`, - ); - - if (flags.service === undefined) { - return; - } - - if (isUrl(flags.service)) { - return commandObj.error( - "Can't update service by URL. Please, specify service name or path to the service config", - ); - } - - const serviceConfig = await ensureServiceConfig(flags.service); - - serviceConfig.modules[moduleName] = { - get: relative(serviceConfig.$getDirPath(), pathToModuleDir), - }; - - await serviceConfig.$commit(); - - commandObj.log( - `Added module ${color.yellow( - pathToModuleDir, - )} to service ${serviceConfig.$getPath()}`, - ); - } -} - -const validateModuleName = (name: string): string | true => { - if ( - name.length === 0 || - name.includes(" ") || - name.includes("\\") || - name.includes("/") - ) { - return "Module name cannot be empty, contain spaces or slashes"; - } - - return true; -}; diff --git a/packages/cli/package/src/commands/module/pack.ts b/packages/cli/package/src/commands/module/pack.ts deleted file mode 100644 index 87efbf43c..000000000 --- a/packages/cli/package/src/commands/module/pack.ts +++ /dev/null @@ -1,93 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import { cwd } from "node:process"; - -import { color } from "@oclif/color"; -import { Args, Flags } from "@oclif/core"; - -import { BaseCommand } from "../../baseCommand.js"; -import { commandObj } from "../../lib/commandObj.js"; -import { initReadonlyModuleConfig } from "../../lib/configs/project/module.js"; -import { - MARINE_BUILD_ARGS_FLAG, - MARINE_BUILD_ARGS_FLAG_NAME, - MODULE_CONFIG_FULL_FILE_NAME, -} from "../../lib/const.js"; -import { packModule } from "../../lib/helpers/packModule.js"; -import { initCli } from "../../lib/lifeCycle.js"; -import { initMarineCli } from "../../lib/marineCli.js"; -import { input } from "../../lib/prompt.js"; - -const PATH = "PATH"; - -export default class Pack extends BaseCommand { - static override description = `Pack module into tar.gz archive`; - static override examples = ["<%= config.bin %> <%= command.id %>"]; - static override flags = { - ...MARINE_BUILD_ARGS_FLAG, - destination: Flags.string({ - description: - "Path to a directory where you want archive to be saved. Default: current directory", - char: "d", - }), - "binding-crate": Flags.string({ - description: "Path to a directory with rust binding crate", - char: "b", - }), - }; - static override args = { - [PATH]: Args.string({ - description: "Path to a module", - }), - }; - async run(): Promise { - const { args, flags } = await initCli(this, await this.parse(Pack)); - - const modulePath = - args[PATH] ?? - (await input({ - message: "Enter path to a module", - })); - - const moduleConfig = await initReadonlyModuleConfig(modulePath, cwd()); - - if (moduleConfig === null) { - return commandObj.error( - `${color.yellow( - MODULE_CONFIG_FULL_FILE_NAME, - )} not found for ${modulePath}`, - ); - } - - const marineCli = await initMarineCli(); - - await packModule({ - moduleConfig, - marineCli, - marineBuildArgs: flags[MARINE_BUILD_ARGS_FLAG_NAME], - bindingCrate: flags["binding-crate"], - destination: - flags.destination ?? - (await input({ - message: - "Enter path to a directory where you want archive to be saved. Default: current directory", - default: cwd(), - })), - }); - } -} diff --git a/packages/cli/package/src/commands/module/remove.ts b/packages/cli/package/src/commands/module/remove.ts deleted file mode 100644 index e3a3b0cef..000000000 --- a/packages/cli/package/src/commands/module/remove.ts +++ /dev/null @@ -1,182 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import assert from "node:assert"; -import { cwd } from "node:process"; - -import { color } from "@oclif/color"; -import { Args, Flags } from "@oclif/core"; - -import { BaseCommand } from "../../baseCommand.js"; -import { commandObj } from "../../lib/commandObj.js"; -import { initFluenceConfig } from "../../lib/configs/project/fluence.js"; -import { isValidServiceModules } from "../../lib/configs/project/service.js"; -import { - ensureServiceConfig, - type ServiceConfigReadonly, -} from "../../lib/configs/project/service.js"; -import { - FLUENCE_CONFIG_FULL_FILE_NAME, - SERVICE_CONFIG_FULL_FILE_NAME, -} from "../../lib/const.js"; -import { - getModuleAbsolutePath, - isUrl, -} from "../../lib/helpers/downloadFile.js"; -import { removeProperties } from "../../lib/helpers/utils.js"; -import { initCli } from "../../lib/lifeCycle.js"; -import { input } from "../../lib/prompt.js"; -import { hasKey } from "../../lib/typeHelpers.js"; - -const NAME_OR_PATH_OR_URL = "NAME | PATH | URL"; - -export default class Remove extends BaseCommand { - static override description = `Remove module from ${SERVICE_CONFIG_FULL_FILE_NAME}`; - static override examples = ["<%= config.bin %> <%= command.id %>"]; - static override flags = { - service: Flags.directory({ - description: `Service name from ${FLUENCE_CONFIG_FULL_FILE_NAME} or path to the service directory`, - helpValue: "", - }), - }; - static override args = { - [NAME_OR_PATH_OR_URL]: Args.string({ - description: `Module name from ${SERVICE_CONFIG_FULL_FILE_NAME}, path to a module or url to .tar.gz archive`, - }), - }; - async run(): Promise { - const { args, flags } = await initCli(this, await this.parse(Remove)); - const fluenceConfig = await initFluenceConfig(); - - const serviceNameOrPath = - flags.service ?? - (await input({ - message: - fluenceConfig === null - ? `Enter path to the service directory` - : `Enter service name from ${color.yellow( - fluenceConfig.$getPath(), - )} or path to the service directory`, - })); - - let serviceOrServiceDirPathOrUrl = serviceNameOrPath; - - if (hasKey(serviceNameOrPath, fluenceConfig?.services)) { - const serviceGet = fluenceConfig.services[serviceNameOrPath]?.get; - assert(typeof serviceGet === "string"); - serviceOrServiceDirPathOrUrl = serviceGet; - } - - if (isUrl(serviceOrServiceDirPathOrUrl)) { - return commandObj.error( - `Can't modify downloaded service ${color.yellow( - serviceOrServiceDirPathOrUrl, - )}`, - ); - } - - const serviceConfig = await ensureServiceConfig( - serviceOrServiceDirPathOrUrl, - ); - - const nameOrPathOrUrl = - args[NAME_OR_PATH_OR_URL] ?? - (await input({ - message: `Enter module name from ${color.yellow( - serviceConfig.$getPath(), - )}, path to a module or url to .tar.gz archive`, - })); - - const moduleNameToRemove = await getModuleNameToRemove( - nameOrPathOrUrl, - serviceConfig, - ); - - const newModules = removeProperties(serviceConfig.modules, ([name]) => { - return name === moduleNameToRemove; - }); - - if (!isValidServiceModules(newModules)) { - return commandObj.error( - `Each service must have a facade module, if you want to change it either override it in ${color.yellow( - FLUENCE_CONFIG_FULL_FILE_NAME, - )} or replace it manually in ${serviceConfig.$getPath()}`, - ); - } - - serviceConfig.modules = newModules; - await serviceConfig.$commit(); - - commandObj.log( - `Removed module ${color.yellow(nameOrPathOrUrl)} from ${color.yellow( - serviceConfig.$getPath(), - )}`, - ); - } -} - -const getModuleNameToRemove = async ( - nameOrPathOrUrl: string, - serviceConfig: ServiceConfigReadonly, -): Promise => { - if (nameOrPathOrUrl in serviceConfig.modules) { - return nameOrPathOrUrl; - } - - const serviceModulesAbsolutePathsWithNames = await Promise.all( - Object.entries(serviceConfig.modules).map(async ([name, { get }]) => { - return [ - name, - await getModuleAbsolutePath(get, serviceConfig.$getDirPath()), - ] as const; - }), - ); - - const absolutePathRelativeToService = await getModuleAbsolutePath( - nameOrPathOrUrl, - serviceConfig.$getDirPath(), - ); - - let [moduleNameToRemove] = - serviceModulesAbsolutePathsWithNames.find(([, absolutePath]) => { - return absolutePath === absolutePathRelativeToService; - }) ?? []; - - if (moduleNameToRemove !== undefined) { - return moduleNameToRemove; - } - - const absolutePathRelativeToCwd = await getModuleAbsolutePath( - nameOrPathOrUrl, - cwd(), - ); - - [moduleNameToRemove] = - serviceModulesAbsolutePathsWithNames.find(([, absolutePath]) => { - return absolutePath === absolutePathRelativeToCwd; - }) ?? []; - - if (moduleNameToRemove !== undefined) { - return moduleNameToRemove; - } - - return commandObj.error( - `There is no module ${color.yellow(nameOrPathOrUrl)} in ${color.yellow( - serviceConfig.$getPath(), - )}`, - ); -}; diff --git a/packages/cli/package/src/commands/provider/cc-create.ts b/packages/cli/package/src/commands/provider/cc-create.ts index 14ee6a693..e303a4279 100644 --- a/packages/cli/package/src/commands/provider/cc-create.ts +++ b/packages/cli/package/src/commands/provider/cc-create.ts @@ -17,7 +17,7 @@ import { BaseCommand } from "../../baseCommand.js"; import { createCommitments } from "../../lib/chain/commitment.js"; -import { NOX_NAMES_FLAG, CHAIN_FLAGS, OFFER_FLAG } from "../../lib/const.js"; +import { CHAIN_FLAGS, PEER_AND_OFFER_NAMES_FLAGS } from "../../lib/const.js"; import { aliasesText } from "../../lib/helpers/aliasesText.js"; import { initCli } from "../../lib/lifeCycle.js"; @@ -28,8 +28,7 @@ export default class CreateCommitment extends BaseCommand< static override description = `Create Capacity commitment${aliasesText.apply(this)}`; static override flags = { ...CHAIN_FLAGS, - ...NOX_NAMES_FLAG, - ...OFFER_FLAG, + ...PEER_AND_OFFER_NAMES_FLAGS, }; async run(): Promise { diff --git a/packages/cli/package/src/commands/provider/deploy.ts b/packages/cli/package/src/commands/provider/deploy.ts new file mode 100644 index 000000000..f97acd927 --- /dev/null +++ b/packages/cli/package/src/commands/provider/deploy.ts @@ -0,0 +1,155 @@ +/** + * Fluence CLI + * Copyright (C) 2024 Fluence DAO + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import { readFile } from "node:fs/promises"; +import { isAbsolute, resolve } from "node:path"; + +import type k8s from "@kubernetes/client-node"; +import { color } from "@oclif/color"; + +import { BaseCommand } from "../../baseCommand.js"; +import { commandObj } from "../../lib/commandObj.js"; +import { ensureReadonlyProviderConfig } from "../../lib/configs/project/provider/provider.js"; +import { CHAIN_FLAGS, PEER_AND_OFFER_NAMES_FLAGS } from "../../lib/const.js"; +import { splitErrorsAndResults } from "../../lib/helpers/utils.js"; +import { initCli } from "../../lib/lifeCycle.js"; +import { projectRootDir } from "../../lib/paths.js"; +import { resolveComputePeersByNames } from "../../lib/resolveComputePeersByNames.js"; + +export default class Deploy extends BaseCommand { + static override description = "Deploy manifests"; + static override examples = ["<%= config.bin %> <%= command.id %>"]; + static override flags = { + ...CHAIN_FLAGS, + ...PEER_AND_OFFER_NAMES_FLAGS, + }; + async run(): Promise { + const { flags } = await initCli(this, await this.parse(Deploy)); + const computePeers = await resolveComputePeersByNames(flags); + + const [invalidPeers, peers] = splitErrorsAndResults( + computePeers, + ({ kubeconfigPath, ipSupplies, name, ...restPeer }) => { + return kubeconfigPath === undefined || ipSupplies.length === 0 + ? { + error: [ + kubeconfigPath === undefined && + `computePeer ${color.yellow(name)} must have a ${color.yellow("kubeconfigPath")} property`, + ipSupplies.length === 0 && + `computePeer ${color.yellow(name)} must have a ${color.yellow("resources.ip.supply")} property, which must be a non-empty array`, + ] + .filter((err) => { + return typeof err === "string"; + }) + .join("\n"), + } + : { result: { name, ipSupplies, kubeconfigPath, ...restPeer } }; + }, + ); + + if (invalidPeers.length > 0) { + const providerConfig = await ensureReadonlyProviderConfig(); + + commandObj.error( + `Invalid config at ${providerConfig.$getPath()}:\n${invalidPeers.join("\n")}`, + ); + } + + for (const { kubeconfigPath, name, manifestPath } of peers) { + await kubectlApply(kubeconfigPath, manifestPath, name); + } + } +} + +/** + * This function comes from https://github.com/kubernetes-client/javascript + * Replicate the functionality of `kubectl apply`. That is, create the resources defined in the `specFile` if they do + * not exist, patch them if they do exist. + * + * @param specPath File system path to a YAML Kubernetes spec. + * @return Array of resources created + */ +async function kubectlApply( + kubeconfigPath: string, + specPath: string, + peerName: string, +): Promise { + const kubeconfigAbsolutePath = isAbsolute(kubeconfigPath) + ? kubeconfigPath + : resolve(projectRootDir, kubeconfigPath); + + commandObj.log( + `Applying manifest for computePeer ${peerName}\nKubeconfig: ${kubeconfigAbsolutePath}\nManifest: ${specPath}`, + ); + + const k8s = await import("@kubernetes/client-node"); + const kc = new k8s.KubeConfig(kubeconfigAbsolutePath); + kc.loadFromFile(kubeconfigAbsolutePath); + + /* eslint-disable */ + const client = k8s.KubernetesObjectApi.makeApiClient(kc); + + const specString = await readFile(specPath, "utf8"); + const specs: k8s.KubernetesObject[] = k8s.loadAllYaml(specString); + + const validSpecs = specs.filter((s) => { + return s && s.kind && s.metadata; + }); + + const created: k8s.KubernetesObject[] = []; + + for (const spec of validSpecs) { + // this is to convince the old version of TypeScript that metadata exists even though we already filtered specs + // without metadata out + spec.metadata = spec.metadata || {}; + spec.metadata.annotations = spec.metadata.annotations || {}; + + delete spec.metadata.annotations[ + "kubectl.kubernetes.io/last-applied-configuration" + ]; + + spec.metadata.annotations[ + "kubectl.kubernetes.io/last-applied-configuration" + ] = JSON.stringify(spec); + + try { + // try to get the resource, if it does not exist an error will be thrown and we will end up in the catch + // block. + // @ts-expect-error + await client.read(spec); + // we got the resource, so it exists, so patch it + // + // Note that this could fail if the spec refers to a custom resource. For custom resources you may need + // to specify a different patch merge strategy in the content-type header. + // + // See: https://github.com/kubernetes/kubernetes/issues/97423 + const response = await client.patch(spec); + created.push(response.body); + } catch (err) { + // if the resource doesnt exist then create it + if (err instanceof k8s.HttpError && err.statusCode === 404) { + const response = await client.create(spec); + created.push(response.body); + } else { + throw err; + } + } + } + + return created; + /* eslint-enable */ +} diff --git a/packages/cli/package/src/commands/provider/gen.ts b/packages/cli/package/src/commands/provider/gen.ts index 4b08949e4..5719e25bf 100644 --- a/packages/cli/package/src/commands/provider/gen.ts +++ b/packages/cli/package/src/commands/provider/gen.ts @@ -22,7 +22,7 @@ import { color } from "@oclif/color"; import { Flags } from "@oclif/core"; import { BaseCommand } from "../../baseCommand.js"; -import { withdrawFromNox } from "../../lib/chain/distributeToNox.js"; +import { withdrawFromPeer } from "../../lib/chain/distributeToNox.js"; import { commandObj } from "../../lib/commandObj.js"; import { ensureComputerPeerConfigs } from "../../lib/configs/project/provider/provider.js"; import { @@ -35,20 +35,21 @@ import { ALL_FLAG_VALUE, MAX_TOKEN_AMOUNT_KEYWORD, CLI_NAME, + PEER_NAMES_FLAG_NAME, } from "../../lib/const.js"; import { stringifyUnknown } from "../../lib/helpers/stringifyUnknown.js"; import { pathExists } from "../../lib/helpers/utils.js"; import { initCli } from "../../lib/lifeCycle.js"; import { getFluenceSecretsDir, - ensureFluenceConfigsDir, ensureProviderSecretsConfigPath, getFluenceBackupsDir, ensureDir, + ensureK8sManifestsDir, } from "../../lib/paths.js"; import { confirm } from "../../lib/prompt.js"; -const RESET_NOX_SECRETS_FLAG_NAME = "reset-nox-secrets"; +const RESET_PEER_SECRETS_FLAG_NAME = "reset-peer-secrets"; const NO_WITHDRAW_FLAG_NAME = "no-withdraw"; export default class Gen extends BaseCommand { @@ -56,12 +57,12 @@ export default class Gen extends BaseCommand { static override examples = ["<%= config.bin %> <%= command.id %>"]; static override flags = { ...CHAIN_FLAGS, - [RESET_NOX_SECRETS_FLAG_NAME]: Flags.boolean({ - description: `Withdraw remaining tokens from your noxes, backup nox secrets from ${DOT_FLUENCE_DIR_NAME}/${PROVIDER_SECRETS_CONFIG_FULL_FILE_NAME} and ${DOT_FLUENCE_DIR_NAME}/${SECRETS_DIR_NAME} (if they exist) to ${DOT_FLUENCE_DIR_NAME}/${BACKUPS_DIR_NAME} and generate new ones`, + [RESET_PEER_SECRETS_FLAG_NAME]: Flags.boolean({ + description: `Withdraw remaining tokens from your peers, backup peer secrets from ${DOT_FLUENCE_DIR_NAME}/${PROVIDER_SECRETS_CONFIG_FULL_FILE_NAME} and ${DOT_FLUENCE_DIR_NAME}/${SECRETS_DIR_NAME} (if they exist) to ${DOT_FLUENCE_DIR_NAME}/${BACKUPS_DIR_NAME} and generate new ones`, default: false, }), [NO_WITHDRAW_FLAG_NAME]: Flags.boolean({ - description: `Is used only when --${RESET_NOX_SECRETS_FLAG_NAME} flag is present. Will not withdraw tokens from noxes (if you don't need it or it fails for some reason)`, + description: `Is used only when --${RESET_PEER_SECRETS_FLAG_NAME} flag is present. Will not withdraw tokens from peers (if you don't need it or it fails for some reason)`, default: false, }), }; @@ -76,28 +77,28 @@ export default class Gen extends BaseCommand { ); if ( - flags[RESET_NOX_SECRETS_FLAG_NAME] && + flags[RESET_PEER_SECRETS_FLAG_NAME] && (await confirm({ - message: `Are you sure you want to backup nox secrets ${color.yellow(providerSecretsConfigPath)} and ${color.yellow(fluenceSecretsDir)} (if they exist) to ${color.yellow(backupDirPath)} and generate new ones`, - default: flags[RESET_NOX_SECRETS_FLAG_NAME], + message: `Are you sure you want to backup peer secrets ${color.yellow(providerSecretsConfigPath)} and ${color.yellow(fluenceSecretsDir)} (if they exist) to ${color.yellow(backupDirPath)} and generate new ones`, + default: flags[RESET_PEER_SECRETS_FLAG_NAME], })) ) { if ( !flags[NO_WITHDRAW_FLAG_NAME] && (await confirm({ message: - "Do you want to withdraw remaining tokens from your noxes before continuing", + "Do you want to withdraw remaining tokens from your peers before continuing", default: !flags[NO_WITHDRAW_FLAG_NAME], })) ) { try { - await withdrawFromNox({ - "nox-names": ALL_FLAG_VALUE, + await withdrawFromPeer({ + [PEER_NAMES_FLAG_NAME]: ALL_FLAG_VALUE, amount: MAX_TOKEN_AMOUNT_KEYWORD, }); } catch (e) { return commandObj.error( - `Failed to withdraw tokens from noxes. Try using ${color.yellow(`${CLI_NAME} provider tokens-withdraw`)} command with specific nox names and amounts or don't withdraw anything if you don't need to by using --${NO_WITHDRAW_FLAG_NAME} flag. Error: ${stringifyUnknown(e)}`, + `Failed to withdraw tokens from peers. Try using ${color.yellow(`${CLI_NAME} provider tokens-withdraw`)} command with specific peer names and amounts or don't withdraw anything if you don't need to by using --${NO_WITHDRAW_FLAG_NAME} flag. Error: ${stringifyUnknown(e)}`, ); } } @@ -112,7 +113,7 @@ export default class Gen extends BaseCommand { await ensureComputerPeerConfigs(); commandObj.logToStderr( - `Config.toml files for nox are generated at:\n${await ensureFluenceConfigsDir()}\n\nsecrets are generated at:\n${getFluenceSecretsDir()}`, + `Secrets are generated at:\n${getFluenceSecretsDir()}\n\nManifest files are generated at:\n${await ensureK8sManifestsDir()}`, ); } } diff --git a/packages/cli/package/src/commands/provider/info.ts b/packages/cli/package/src/commands/provider/info.ts index 0fc12102d..7045c6475 100644 --- a/packages/cli/package/src/commands/provider/info.ts +++ b/packages/cli/package/src/commands/provider/info.ts @@ -23,12 +23,12 @@ import { getProviderInfo } from "../../lib/chain/providerInfo.js"; import { commandObj } from "../../lib/commandObj.js"; import { initNewProviderArtifactsConfig } from "../../lib/configs/project/providerArtifacts/providerArtifacts.js"; import { - NOX_NAMES_FLAG, CHAIN_FLAGS, JSON_FLAG, ADDRESS_FLAG, ADDRESS_FLAG_NAME, PRIV_KEY_FLAG_NAME, + PEER_AND_OFFER_NAMES_FLAGS, } from "../../lib/const.js"; import { aliasesText } from "../../lib/helpers/aliasesText.js"; import { initCli } from "../../lib/lifeCycle.js"; @@ -37,10 +37,10 @@ import { ensureFluenceEnv } from "../../lib/resolveFluenceEnv.js"; export default class Info extends BaseCommand { static override hiddenAliases = ["provider:i"]; - static override description = `Print nox signing wallets and peer ids${aliasesText.apply(this)}`; + static override description = `Print peer signing wallets and peer ids${aliasesText.apply(this)}`; static override flags = { ...CHAIN_FLAGS, - ...NOX_NAMES_FLAG, + ...PEER_AND_OFFER_NAMES_FLAGS, ...JSON_FLAG, ...ADDRESS_FLAG, }; @@ -56,7 +56,7 @@ export default class Info extends BaseCommand { )), computePeers: computePeers.map(({ name, peerId, walletAddress }) => { return { - nox: name, + peer: name, peerId, wallet: walletAddress, }; diff --git a/packages/cli/package/src/commands/provider/init.ts b/packages/cli/package/src/commands/provider/init.ts index 1838f54d3..1a1e88116 100644 --- a/packages/cli/package/src/commands/provider/init.ts +++ b/packages/cli/package/src/commands/provider/init.ts @@ -15,8 +15,9 @@ * along with this program. If not, see . */ +import { writeFile } from "node:fs/promises"; + import { color } from "@oclif/color"; -import { Flags } from "@oclif/core"; import { BaseCommand } from "../../baseCommand.js"; import { commandObj } from "../../lib/commandObj.js"; @@ -26,20 +27,18 @@ import { } from "../../lib/configs/project/provider/provider.js"; import { CHAIN_FLAGS, - NOXES_FLAG, + PEERS_FLAG, PROVIDER_CONFIG_FULL_FILE_NAME, + RECOMMENDED_GITIGNORE_CONTENT, } from "../../lib/const.js"; import { initCli } from "../../lib/lifeCycle.js"; +import { getGitignorePath } from "../../lib/paths.js"; export default class Init extends BaseCommand { static override description = `Init provider config. Creates a ${PROVIDER_CONFIG_FULL_FILE_NAME} file`; static override flags = { - ...NOXES_FLAG, + ...PEERS_FLAG, ...CHAIN_FLAGS, - "no-vm": Flags.boolean({ - description: `Generate ${PROVIDER_CONFIG_FULL_FILE_NAME} without vm configuration`, - default: false, - }), }; async run(): Promise { @@ -57,6 +56,8 @@ export default class Init extends BaseCommand { providerConfig = await initNewProviderConfig(flags); + await writeFile(getGitignorePath(), RECOMMENDED_GITIGNORE_CONTENT, "utf8"); + commandObj.logToStderr( `Provider config is at ${color.yellow(providerConfig.$getPath())}`, ); diff --git a/packages/cli/package/src/commands/provider/tokens-distribute.ts b/packages/cli/package/src/commands/provider/tokens-distribute.ts index 98dd1edc8..136045c6b 100644 --- a/packages/cli/package/src/commands/provider/tokens-distribute.ts +++ b/packages/cli/package/src/commands/provider/tokens-distribute.ts @@ -18,12 +18,11 @@ import { Flags } from "@oclif/core"; import { BaseCommand } from "../../baseCommand.js"; -import { distributeToNox } from "../../lib/chain/distributeToNox.js"; +import { distributeToPeer } from "../../lib/chain/distributeToNox.js"; import { CHAIN_FLAGS, FLT_SYMBOL, - NOX_NAMES_FLAG, - OFFER_FLAG, + PEER_AND_OFFER_NAMES_FLAGS, } from "../../lib/const.js"; import { aliasesText } from "../../lib/helpers/aliasesText.js"; import { initCli } from "../../lib/lifeCycle.js"; @@ -32,18 +31,17 @@ export default class TokensDistribute extends BaseCommand< typeof TokensDistribute > { static override hiddenAliases = ["provider:td"]; - static override description = `Distribute ${FLT_SYMBOL} tokens to noxes${aliasesText.apply(this)}`; + static override description = `Distribute ${FLT_SYMBOL} tokens to peers${aliasesText.apply(this)}`; static override flags = { ...CHAIN_FLAGS, - ...NOX_NAMES_FLAG, - ...OFFER_FLAG, + ...PEER_AND_OFFER_NAMES_FLAGS, amount: Flags.string({ - description: `Amount of ${FLT_SYMBOL} tokens to distribute to noxes`, + description: `Amount of ${FLT_SYMBOL} tokens to distribute to peers`, }), }; async run(): Promise { const { flags } = await initCli(this, await this.parse(TokensDistribute)); - await distributeToNox(flags); + await distributeToPeer(flags); } } diff --git a/packages/cli/package/src/commands/provider/tokens-withdraw.ts b/packages/cli/package/src/commands/provider/tokens-withdraw.ts index ba50dcf8b..27ba9d32d 100644 --- a/packages/cli/package/src/commands/provider/tokens-withdraw.ts +++ b/packages/cli/package/src/commands/provider/tokens-withdraw.ts @@ -18,12 +18,12 @@ import { Flags } from "@oclif/core"; import { BaseCommand } from "../../baseCommand.js"; -import { withdrawFromNox } from "../../lib/chain/distributeToNox.js"; +import { withdrawFromPeer } from "../../lib/chain/distributeToNox.js"; import { CHAIN_FLAGS, FLT_SYMBOL, MAX_TOKEN_AMOUNT_KEYWORD, - NOX_NAMES_FLAG, + PEER_AND_OFFER_NAMES_FLAGS, } from "../../lib/const.js"; import { aliasesText } from "../../lib/helpers/aliasesText.js"; import { initCli } from "../../lib/lifeCycle.js"; @@ -32,17 +32,17 @@ const AMOUNT_FLAG_NAME = "amount"; export default class TokensWithdraw extends BaseCommand { static override hiddenAliases = ["provider:tw"]; - static override description = `Withdraw ${FLT_SYMBOL} tokens from noxes${aliasesText.apply(this)}`; + static override description = `Withdraw ${FLT_SYMBOL} tokens from peers${aliasesText.apply(this)}`; static override flags = { ...CHAIN_FLAGS, - ...NOX_NAMES_FLAG, + ...PEER_AND_OFFER_NAMES_FLAGS, [AMOUNT_FLAG_NAME]: Flags.string({ - description: `Amount of ${FLT_SYMBOL} tokens to withdraw from noxes. Use --${AMOUNT_FLAG_NAME} ${MAX_TOKEN_AMOUNT_KEYWORD} to withdraw maximum possible amount`, + description: `Amount of ${FLT_SYMBOL} tokens to withdraw from peers. Use --${AMOUNT_FLAG_NAME} ${MAX_TOKEN_AMOUNT_KEYWORD} to withdraw maximum possible amount`, }), }; async run(): Promise { const { flags } = await initCli(this, await this.parse(TokensWithdraw)); - await withdrawFromNox(flags); + await withdrawFromPeer(flags); } } diff --git a/packages/cli/package/src/commands/run.ts b/packages/cli/package/src/commands/run.ts deleted file mode 100644 index 000729553..000000000 --- a/packages/cli/package/src/commands/run.ts +++ /dev/null @@ -1,500 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import { readFile } from "node:fs/promises"; -import { resolve } from "node:path"; -// import { performance, PerformanceObserver } from "node:perf_hooks"; - -import type { CompileFuncCallFromPathArgs } from "@fluencelabs/aqua-api"; -import type { js2aqua } from "@fluencelabs/js-client"; -import { color } from "@oclif/color"; -import { Flags } from "@oclif/core"; -import type { JSONSchemaType } from "ajv"; - -import { BaseCommand } from "../baseCommand.js"; -import { jsonStringify } from "../common.js"; -import { validationErrorToString, ajv } from "../lib/ajvInstance.js"; -import { - resolveAquaConfig, - compileFunctionCall, - resolveCommonAquaCompilationFlags, -} from "../lib/aqua.js"; -import type { ResolvedCommonAquaCompilationFlags } from "../lib/aqua.js"; -import { commandObj } from "../lib/commandObj.js"; -import { initFluenceConfig } from "../lib/configs/project/fluence.js"; -import { - COMPILE_AQUA_PROPERTY_NAME, - INPUT_FLAG_NAME, - FS_OPTIONS, - OFF_AQUA_LOGS_FLAG, - FLUENCE_CLIENT_FLAGS, - COMMON_AQUA_COMPILATION_FLAGS, - type FluenceClientFlags, - FLUENCE_CONFIG_FULL_FILE_NAME, - INPUT_FLAG_EXPLANATION, -} from "../lib/const.js"; -import { splitErrorsAndResults } from "../lib/helpers/utils.js"; -import { disconnectFluenceClient, initFluenceClient } from "../lib/jsClient.js"; -import { initCli } from "../lib/lifeCycle.js"; -import { projectRootDir } from "../lib/paths.js"; -import { input } from "../lib/prompt.js"; - -// const perfObserver = new PerformanceObserver((items) => { -// items.getEntries().forEach((entry) => { -// console.log(entry); -// }); -// }); - -// perfObserver.observe({ entryTypes: ["measure"], buffered: true }); -// performance.mark("whole-start"); - -const FUNC_FLAG_NAME = "func"; -const FUNC_SHORT_FLAG_NAME = "f"; -// const ON_FLAG_NAME = "on"; -const DATA_FLAG_NAME = "data"; -const FUNC_CALL_EXAMPLE = 'funcName("stringArg")'; - -export default class Run extends BaseCommand { - static override description = `Run the first aqua function CLI is able to find and compile among all aqua files specified in '${COMPILE_AQUA_PROPERTY_NAME}' property of ${FLUENCE_CONFIG_FULL_FILE_NAME} file${INPUT_FLAG_EXPLANATION}`; - static override examples = [ - `<%= config.bin %> <%= command.id %> -${FUNC_SHORT_FLAG_NAME} '${FUNC_CALL_EXAMPLE}'`, - ]; - static override flags = { - data: Flags.string({ - description: - "JSON in { [argumentName]: argumentValue } format. You can call a function using these argument names like this: -f 'myFunc(argumentName)'. Arguments in this flag override arguments in the --data-path flag", - helpValue: "", - }), - "data-path": Flags.file({ - description: - "Path to a JSON file in { [argumentName]: argumentValue } format. You can call a function using these argument names like this: -f 'myFunc(argumentName)'. Arguments in this flag can be overridden using --data flag", - helpValue: "", - }), - quiet: Flags.boolean({ - default: false, - description: - "Print only execution result. Overrides all --log-level-* flags", - }), - // TODO: DXJ-207 - // [ON_FLAG_NAME]: Flags.string({ - // description: "PeerId of a peer where you want to run the function", - // helpValue: "", - // }), - [FUNC_FLAG_NAME]: Flags.string({ - char: FUNC_SHORT_FLAG_NAME, - description: `Function call. Example: ${FUNC_CALL_EXAMPLE}`, - helpValue: "", - }), - "print-air": Flags.boolean({ - default: false, - description: "Prints generated AIR code instead of function execution", - exclusive: ["print-beautified-air"], - }), - "print-beautified-air": Flags.boolean({ - default: false, - description: "Prints beautified AIR code instead of function execution", - char: "b", - exclusive: ["print-air"], - }), - ...OFF_AQUA_LOGS_FLAG, - ...FLUENCE_CLIENT_FLAGS, - ...COMMON_AQUA_COMPILATION_FLAGS, - }; - async run(): Promise { - const { flags } = await initCli(this, await this.parse(Run)); - - if (flags.quiet) { - commandObj.log = () => {}; - commandObj.logToStderr = () => {}; - } - - // if (typeof flags[ON_FLAG_NAME] === "string") { - // const onPeerConst = `ON_PEER = "${flags[ON_FLAG_NAME]}"`; - - // flags.const = - // flags.const === undefined - // ? [onPeerConst] - // : [...flags.const, onPeerConst]; - // } - - const compileFuncCallArgs = await ensureCompileFuncCallArgs({ - aquaPathFromFlags: flags.input, - aquaCompilationFlags: await resolveCommonAquaCompilationFlags(flags), - }); - - const [funcCall, runData] = await Promise.all([ - flags.func === undefined - ? input({ - message: `Enter a function call that you want to execute. Example: ${color.yellow( - FUNC_CALL_EXAMPLE, - )}`, - flagName: FUNC_FLAG_NAME, - }) - : Promise.resolve(flags.func), - getRunData(flags), - ]); - - const result = await fluenceRun({ - ...flags, - compileFuncCallArgs, - runData, - funcCall, - }); - - const stringResult = - typeof result === "string" ? result : jsonStringify(result); - - // If `--quite` flag is used then commandObj.log does nothing - // So we use console.log here instead - // eslint-disable-next-line no-console - console.log(stringResult); - - await disconnectFluenceClient(); - - // performance.mark("whole-end"); - // performance.measure("whole", "whole-start", "whole-end"); - } -} - -type EnsureAquaPathArg = { - aquaPathFromFlags: string | undefined; - aquaCompilationFlags: ResolvedCommonAquaCompilationFlags; -}; - -async function ensureCompileFuncCallArgs({ - aquaPathFromFlags, - aquaCompilationFlags, -}: EnsureAquaPathArg): Promise< - Omit[] -> { - const fluenceConfig = await initFluenceConfig(); - - if (typeof aquaPathFromFlags === "string") { - return [{ ...aquaCompilationFlags, filePath: resolve(aquaPathFromFlags) }]; - } - - const { compileAqua } = fluenceConfig ?? {}; - - if (compileAqua !== undefined) { - return Object.values(compileAqua).map((config) => { - const resolvedAquaConfig = resolveAquaConfig( - config, - aquaCompilationFlags.imports, - ); - - return { - ...resolvedAquaConfig, - filePath: resolve(projectRootDir, resolvedAquaConfig.filePath), - }; - }); - } - - return [ - { - ...aquaCompilationFlags, - filePath: resolve( - await input({ - message: "Enter path to the input file", - flagName: INPUT_FLAG_NAME, - }), - ), - }, - ]; -} - -type RunData = Record[0]>; - -const runDataSchema: JSONSchemaType = { - type: "object", - required: [], -}; - -const validateRunData = ajv.compile(runDataSchema); - -const getRunData = async (flags: { - data: string | undefined; - "data-path": string | undefined; -}): Promise => { - let runData: RunData = {}; - const { data, "data-path": dataPath } = flags; - - if (typeof dataPath === "string") { - let data: string; - - try { - data = await readFile(dataPath, FS_OPTIONS); - } catch { - commandObj.error( - `Can't read ${color.yellow(dataPath)}: No such file or directory`, - ); - } - - let parsedData: unknown; - - try { - parsedData = JSON.parse(data); - } catch { - commandObj.error(`Unable to parse ${color.yellow(dataPath)}`); - } - - if (!validateRunData(parsedData)) { - commandObj.error( - `Invalid ${color.yellow(dataPath)}: ${await validationErrorToString( - validateRunData.errors, - )}`, - ); - } - - runData = parsedData; - } - - if (typeof data === "string") { - let parsedData: unknown; - - try { - parsedData = JSON.parse(data); - } catch { - commandObj.error(`Unable to parse --${DATA_FLAG_NAME}`); - } - - if (!validateRunData(parsedData)) { - commandObj.error( - `Invalid --${DATA_FLAG_NAME}: ${await validationErrorToString( - validateRunData.errors, - )}`, - ); - } - - runData = { ...runData, ...parsedData }; - } - - const dataString = JSON.stringify(runData); - return dataString === "{}" ? undefined : runData; -}; - -type RunArgs = { - compileFuncCallArgs: Omit[]; - funcCall: string; - runData: RunData | undefined; - "print-air": boolean; - "print-beautified-air": boolean; -} & FluenceClientFlags; - -async function fluenceRun(args: RunArgs) { - const compilationResults = await Promise.all( - args.compileFuncCallArgs.map((compileFunctionCallArgs) => { - return compileFunctionCall({ - ...compileFunctionCallArgs, - data: args.runData, - funcCall: args.funcCall, - }); - }), - ); - - const [firstSuccessfulCompilationResult] = compilationResults.flatMap( - ([, successfulCompilationResults]) => { - return successfulCompilationResults; - }, - ); - - if (firstSuccessfulCompilationResult === undefined) { - const functionNotFoundPaths: string[] = []; - - const errors = compilationResults - .flatMap(([err]) => { - return err; - }) - // extract function not found errors - .map(({ aquaFilePath, compilationResult }) => { - const [functionNotFound, restErrors] = splitErrorsAndResults( - compilationResult.errors, - (error) => { - if ( - error.startsWith("There is no function") && - error.includes("or it is not exported") - ) { - return { error: aquaFilePath }; - } - - return { result: error }; - }, - ); - - functionNotFoundPaths.push(...functionNotFound); - - return { - aquaFilePath, - errors: restErrors, - }; - }) - .filter(({ errors }) => { - return errors.length !== 0; - }) - .map(({ aquaFilePath, errors }) => { - return `${color.yellow(aquaFilePath)}\n\n${errors.join("\n")}`; - }); - - const functionName = args.funcCall.split("(")[0]; - - const functionNotFoundErrorText = - functionNotFoundPaths.length === 0 - ? "" - : `\n\nChecked the following files and function ${color.yellow( - functionName, - )} is not exported from them:\n\n${color.yellow( - functionNotFoundPaths.join("\n"), - )}`; - - const restErrorsText = - errors.length === 0 - ? "" - : `\n\nFound some errors when trying to compile:\n\n${color.yellow( - errors.join("\n\n"), - )}`; - - commandObj.error( - `Can't find function ${color.yellow( - functionName, - )}${functionNotFoundErrorText}${restErrorsText}`, - ); - } - - const functionCall = - firstSuccessfulCompilationResult.compilationResult.functionCall; - - if (args["print-air"]) { - commandObj.log(functionCall.script); - return; - } - - if (args["print-beautified-air"]) { - const { beautify } = await import("@fluencelabs/air-beautify-wasm"); - commandObj.log(beautify(functionCall.script)); - return; - } - - const { Fluence, callAquaFunction, js2aqua, aqua2js } = await import( - "@fluencelabs/js-client" - ); - - const schema = functionCall.funcDef; - - // TODO: remove this after DXJ-535 is done - const argsExpectedFromFuncFlag = - schema.arrow.domain.tag === "nil" ? {} : schema.arrow.domain.fields; - - const runDataWithSchemas = Object.entries(args.runData ?? {}) - .map(([argName, argValue]) => { - const argSchema = argsExpectedFromFuncFlag[argName]; - return [argName, argValue, argSchema] as const; - }) - // if user passes some args that he didn't mention in -f flag - we ignore them - .filter( - ( - arg, - ): arg is [ - (typeof arg)[0], - (typeof arg)[1], - NonNullable<(typeof arg)[2]>, - ] => { - return arg[2] !== undefined; - }, - ); - - const [missingArgsErrors] = splitErrorsAndResults( - Object.keys(argsExpectedFromFuncFlag), - (argNameFromFuncFlag) => { - const isArgMissing = !runDataWithSchemas.some( - ([argNameFromDataFlags]) => { - return argNameFromFuncFlag === argNameFromDataFlags; - }, - ); - - if (isArgMissing) { - return { - error: `You are using argument ${color.yellow( - argNameFromFuncFlag, - )} in the ${color.yellow( - args.funcCall, - )} call, but you didn't pass the value for this argument using ${color.yellow( - "--data", - )} or ${color.yellow("--data-path")} flags`, - }; - } - - return { result: argNameFromFuncFlag }; - }, - ); - - if (missingArgsErrors.length > 0) { - commandObj.error(missingArgsErrors.join("\n")); - } - - const [runDataErrors, runDataResults] = splitErrorsAndResults( - runDataWithSchemas, - ([argName, argValue, argSchema]) => { - if (argSchema.tag === "arrow") { - return { - error: `Argument ${color.yellow( - argName, - )} is a function. Currently it can't be passed to ${color.yellow( - "fluence run", - )}. We suggest you two wrap this function in your aqua code so there is no need to pass a callback to it`, - }; - } - - return { - result: [ - argName, - js2aqua(argValue, argSchema, { path: [argName] }), - ] as const, - }; - }, - ); - - if (runDataErrors.length > 0) { - commandObj.error(runDataErrors.join("\n")); - } - - await initFluenceClient(args); - const { codomain } = functionCall.funcDef.arrow; - const returnTypeVoid = codomain.tag === "nil" || codomain.items.length === 0; - - commandObj.logToStderr( - `Running ${color.yellow(args.funcCall)} from ${color.yellow( - firstSuccessfulCompilationResult.aquaFilePath, - )}\n`, - ); - - const result = await callAquaFunction({ - script: functionCall.script, - config: {}, - peer: Fluence.getClient(), - args: Object.fromEntries(runDataResults), - fireAndForget: returnTypeVoid, - }); - - const returnSchema = - (schema.arrow.codomain.tag === "unlabeledProduct" && - schema.arrow.codomain.items.length === 1 - ? schema.arrow.codomain.items[0] - : undefined) ?? schema.arrow.codomain; - - return aqua2js(result, returnSchema, { - path: [`${functionCall.funcDef.functionName}ReturnValue`], - }); -} diff --git a/packages/cli/package/src/commands/service/add.ts b/packages/cli/package/src/commands/service/add.ts deleted file mode 100644 index 4a540f1fe..000000000 --- a/packages/cli/package/src/commands/service/add.ts +++ /dev/null @@ -1,98 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import { isAbsolute, resolve } from "node:path"; -import { cwd } from "node:process"; - -import { Args, Flags } from "@oclif/core"; - -import { BaseCommand } from "../../baseCommand.js"; -import { ensureValidServiceName, addService } from "../../lib/addService.js"; -import { commandObj } from "../../lib/commandObj.js"; -import { initReadonlyServiceConfig } from "../../lib/configs/project/service.js"; -import { - FLUENCE_CONFIG_FULL_FILE_NAME, - MARINE_BUILD_ARGS_FLAG, -} from "../../lib/const.js"; -import { - AQUA_NAME_REQUIREMENTS, - isUrl, -} from "../../lib/helpers/downloadFile.js"; -import { initCli } from "../../lib/lifeCycle.js"; -import { initMarineCli } from "../../lib/marineCli.js"; -import { input } from "../../lib/prompt.js"; - -const PATH_OR_URL = "PATH | URL"; - -export default class Add extends BaseCommand { - static override description = `Add service to ${FLUENCE_CONFIG_FULL_FILE_NAME}`; - static override examples = ["<%= config.bin %> <%= command.id %>"]; - static override flags = { - name: Flags.string({ - description: `Override service name (${AQUA_NAME_REQUIREMENTS})`, - helpValue: "", - }), - ...MARINE_BUILD_ARGS_FLAG, - }; - static override args = { - [PATH_OR_URL]: Args.string({ - description: "Path to a service or url to .tar.gz archive", - }), - }; - async run(): Promise { - const { args, flags } = await initCli(this, await this.parse(Add), true); - - const serviceOrServiceDirPathOrUrl = - args[PATH_OR_URL] ?? - (await input({ message: "Enter service path or url" })); - - const serviceConfig = await initReadonlyServiceConfig( - serviceOrServiceDirPathOrUrl, - cwd(), - ); - - if (serviceConfig === null) { - commandObj.error( - `No service config found at ${serviceOrServiceDirPathOrUrl}`, - ); - } - - const serviceName = await ensureValidServiceName( - flags.name ?? serviceConfig.name, - ); - - const marineCli = await initMarineCli(); - - await addService({ - serviceName, - absolutePathOrUrl: resolveServicePathOrUrl(serviceOrServiceDirPathOrUrl), - marineCli, - marineBuildArgs: flags["marine-build-args"], - }); - } -} - -const resolveServicePathOrUrl = (serviceOrServiceDirPathOrUrl: string) => { - if ( - isUrl(serviceOrServiceDirPathOrUrl) || - isAbsolute(serviceOrServiceDirPathOrUrl) - ) { - return serviceOrServiceDirPathOrUrl; - } - - return resolve(serviceOrServiceDirPathOrUrl); -}; diff --git a/packages/cli/package/src/commands/service/new.ts b/packages/cli/package/src/commands/service/new.ts deleted file mode 100644 index 533f1987a..000000000 --- a/packages/cli/package/src/commands/service/new.ts +++ /dev/null @@ -1,86 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import { join, relative, resolve } from "node:path"; - -import { color } from "@oclif/color"; -import { Args, Flags } from "@oclif/core"; - -import { BaseCommand } from "../../baseCommand.js"; -import { addService, ensureValidServiceName } from "../../lib/addService.js"; -import { commandObj } from "../../lib/commandObj.js"; -import { initFluenceConfig } from "../../lib/configs/project/fluence.js"; -import { initNewReadonlyServiceConfig } from "../../lib/configs/project/service.js"; -import { generateNewModule } from "../../lib/generateNewModule.js"; -import { AQUA_NAME_REQUIREMENTS } from "../../lib/helpers/downloadFile.js"; -import { initCli } from "../../lib/lifeCycle.js"; -import { initMarineCli } from "../../lib/marineCli.js"; -import { ensureServicesDir } from "../../lib/paths.js"; -import { input } from "../../lib/prompt.js"; - -export default class New extends BaseCommand { - static override description = "Create new marine service template"; - static override examples = ["<%= config.bin %> <%= command.id %>"]; - static override flags = { - path: Flags.string({ - description: "Path to services dir (default: src/services)", - helpValue: "", - }), - }; - static override args = { - name: Args.string({ - description: `Unique service name (${AQUA_NAME_REQUIREMENTS})`, - }), - }; - async run(): Promise { - const { args, flags } = await initCli(this, await this.parse(New)); - const fluenceConfig = await initFluenceConfig(); - - const serviceName = await ensureValidServiceName( - args.name ?? (await input({ message: "Enter service name" })), - ); - - const absoluteServicePath = resolve( - join(flags.path ?? (await ensureServicesDir()), serviceName), - ); - - const pathToModuleDir = join(absoluteServicePath, serviceName); - await generateNewModule(pathToModuleDir, serviceName); - - await initNewReadonlyServiceConfig( - absoluteServicePath, - relative(absoluteServicePath, pathToModuleDir), - serviceName, - ); - - commandObj.log( - `Successfully generated template for new service at ${color.yellow( - absoluteServicePath, - )}`, - ); - - if (fluenceConfig !== null) { - const marineCli = await initMarineCli(); - - await addService({ - marineCli, - serviceName, - absolutePathOrUrl: absoluteServicePath, - }); - } - } -} diff --git a/packages/cli/package/src/commands/service/remove.ts b/packages/cli/package/src/commands/service/remove.ts deleted file mode 100644 index 4e259a13b..000000000 --- a/packages/cli/package/src/commands/service/remove.ts +++ /dev/null @@ -1,176 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import { cwd } from "node:process"; - -import { color } from "@oclif/color"; -import { Args } from "@oclif/core"; - -import { BaseCommand } from "../../baseCommand.js"; -import { commandObj } from "../../lib/commandObj.js"; -import { - isFluenceConfigWithServices, - type FluenceConfigWithServices, -} from "../../lib/configs/project/fluence.js"; -import { FLUENCE_CONFIG_FULL_FILE_NAME } from "../../lib/const.js"; -import { getServiceAbsolutePath } from "../../lib/helpers/downloadFile.js"; -import { ensureFluenceProject } from "../../lib/helpers/ensureFluenceProject.js"; -import { removeProperties } from "../../lib/helpers/utils.js"; -import { initCli } from "../../lib/lifeCycle.js"; -import { projectRootDir } from "../../lib/paths.js"; -import { input } from "../../lib/prompt.js"; - -const NAME_OR_PATH_OR_URL = "NAME | PATH | URL"; - -export default class Remove extends BaseCommand { - static override description = `Remove service from ${FLUENCE_CONFIG_FULL_FILE_NAME} services property and from all of the workers`; - static override examples = ["<%= config.bin %> <%= command.id %>"]; - static override args = { - [NAME_OR_PATH_OR_URL]: Args.string({ - description: `Service name from ${FLUENCE_CONFIG_FULL_FILE_NAME}, path to a service or url to .tar.gz archive`, - }), - }; - async run(): Promise { - const { args } = await initCli(this, await this.parse(Remove), true); - const fluenceConfig = await ensureFluenceProject(); - - const nameOrPathOrUrl = - args[NAME_OR_PATH_OR_URL] ?? - (await input({ - message: `Enter service name from ${color.yellow( - fluenceConfig.$getPath(), - )}, path to a service or url to .tar.gz archive`, - })); - - if (!isFluenceConfigWithServices(fluenceConfig)) { - return commandObj.error( - `There are no services in ${color.yellow(fluenceConfig.$getPath())}`, - ); - } - - const serviceNameToRemove = await getServiceNameToRemove( - nameOrPathOrUrl, - fluenceConfig, - ); - - fluenceConfig.services = removeProperties( - fluenceConfig.services, - ([name]) => { - return name !== serviceNameToRemove; - }, - ); - - if (fluenceConfig.deployments !== undefined) { - fluenceConfig.deployments = Object.fromEntries( - Object.entries(fluenceConfig.deployments).map( - ([workerName, worker]) => { - return [ - workerName, - { - ...worker, - ...(worker.services === undefined - ? {} - : { - services: worker.services.filter((service) => { - return service !== serviceNameToRemove; - }), - }), - }, - ]; - }, - ), - ); - } - - if (fluenceConfig.hosts !== undefined) { - fluenceConfig.hosts = Object.fromEntries( - Object.entries(fluenceConfig.hosts).map(([workerName, worker]) => { - return [ - workerName, - { - ...worker, - ...(worker.services === undefined - ? {} - : { - services: worker.services.filter((service) => { - return service !== serviceNameToRemove; - }), - }), - }, - ]; - }), - ); - } - - await fluenceConfig.$commit(); - - commandObj.log( - `Removed service ${color.yellow(nameOrPathOrUrl)} from ${color.yellow( - fluenceConfig.$getPath(), - )}`, - ); - } -} - -async function getServiceNameToRemove( - nameOrPathOrUrl: string, - fluenceConfig: FluenceConfigWithServices, -): Promise { - if (nameOrPathOrUrl in fluenceConfig.services) { - return nameOrPathOrUrl; - } - - const servicesAbsolutePathsWithNames = await Promise.all( - Object.entries(fluenceConfig.services).map(async ([name, { get }]) => { - return [name, await getServiceAbsolutePath(get, projectRootDir)] as const; - }), - ); - - const absolutePathRelativeToService = await getServiceAbsolutePath( - nameOrPathOrUrl, - projectRootDir, - ); - - let [moduleNameToRemove] = - servicesAbsolutePathsWithNames.find(([, absolutePath]) => { - return absolutePath === absolutePathRelativeToService; - }) ?? []; - - if (moduleNameToRemove !== undefined) { - return moduleNameToRemove; - } - - const absolutePathRelativeToCwd = await getServiceAbsolutePath( - nameOrPathOrUrl, - cwd(), - ); - - [moduleNameToRemove] = - servicesAbsolutePathsWithNames.find(([, absolutePath]) => { - return absolutePath === absolutePathRelativeToCwd; - }) ?? []; - - if (moduleNameToRemove !== undefined) { - return moduleNameToRemove; - } - - return commandObj.error( - `There is no service ${color.yellow(nameOrPathOrUrl)} in ${color.yellow( - fluenceConfig.$getPath(), - )}`, - ); -} diff --git a/packages/cli/package/src/commands/service/repl.ts b/packages/cli/package/src/commands/service/repl.ts deleted file mode 100644 index 3838f2d1d..000000000 --- a/packages/cli/package/src/commands/service/repl.ts +++ /dev/null @@ -1,183 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import { spawn } from "node:child_process"; -import { cwd } from "node:process"; - -import { color } from "@oclif/color"; -import { Args, Command } from "@oclif/core"; - -import { resolveSingleServiceModuleConfigsAndBuild } from "../../lib/build.js"; -import { commandObj, isInteractive } from "../../lib/commandObj.js"; -import { - initReadonlyFluenceConfig, - initFluenceConfig, -} from "../../lib/configs/project/fluence.js"; -import { initReadonlyServiceConfig } from "../../lib/configs/project/service.js"; -import { - FLUENCE_CONFIG_FULL_FILE_NAME, - MARINE_BUILD_ARGS_FLAG, - NO_INPUT_FLAG, - SEPARATOR, -} from "../../lib/const.js"; -import { haltCountly } from "../../lib/countly.js"; -import { getModuleWasmPath } from "../../lib/helpers/downloadFile.js"; -import { updateAquaServiceInterfaceFile } from "../../lib/helpers/generateServiceInterface.js"; -import { startSpinner, stopSpinner } from "../../lib/helpers/spinner.js"; -import { exitCli, initCli } from "../../lib/lifeCycle.js"; -import { ensureMreplPath } from "../../lib/marineCli.js"; -import { initMarineCli } from "../../lib/marineCli.js"; -import { projectRootDir } from "../../lib/paths.js"; -import { input, list } from "../../lib/prompt.js"; - -const NAME_OR_PATH_OR_URL = "NAME | PATH | URL"; - -/** - * This command doesn't extend BaseCommand like other commands do because it - * spawns a separate repl process which should keep cli alive - * This means we have to manually call exitCli() in all other cases before - * the final return statement - */ -export default class REPL extends Command { - static override description = - "Open service inside repl (downloads and builds modules if necessary)"; - static override examples = ["<%= config.bin %> <%= command.id %>"]; - static override flags = { - ...NO_INPUT_FLAG, - ...MARINE_BUILD_ARGS_FLAG, - }; - static override args = { - [NAME_OR_PATH_OR_URL]: Args.string({ - description: `Service name from ${FLUENCE_CONFIG_FULL_FILE_NAME}, path to a service or url to .tar.gz archive`, - }), - }; - async run(): Promise { - const { args, flags } = await initCli(this, await this.parse(REPL)); - - const nameOrPathOrUrl = - args[NAME_OR_PATH_OR_URL] ?? (await promptForNamePathOrUrl()); - - const { serviceName, serviceConfig } = - await ensureServiceConfig(nameOrPathOrUrl); - - const marineCli = await initMarineCli(); - - startSpinner("Making sure service and modules are downloaded and built"); - - const { facadeModuleConfig, fluenceServiceConfigTomlPath } = - await resolveSingleServiceModuleConfigsAndBuild({ - serviceName, - serviceConfig, - marineCli, - marineBuildArgs: flags["marine-build-args"], - }); - - const fluenceConfig = await initFluenceConfig(); - - const isServiceListedInFluenceConfig = - fluenceConfig?.services?.[nameOrPathOrUrl] !== undefined; - - if (isServiceListedInFluenceConfig) { - await updateAquaServiceInterfaceFile( - { [nameOrPathOrUrl]: getModuleWasmPath(facadeModuleConfig) }, - marineCli, - ); - } - - stopSpinner(); - - if (!isInteractive) { - await exitCli(); - return; - } - - const mreplPath = await ensureMreplPath(); - - commandObj.logToStderr(`Service config for repl was generated at: ${fluenceServiceConfigTomlPath} -${SEPARATOR}Execute ${color.yellow( - "help", - )} inside repl to see available commands. -Current service is: ${color.yellow(facadeModuleConfig.name)} -Call ${facadeModuleConfig.name} service functions in repl like this: - -${color.yellow( - `call ${facadeModuleConfig.name} [, ]`, -)}${SEPARATOR}`); - - await haltCountly(); - - spawn(mreplPath, [fluenceServiceConfigTomlPath], { - stdio: "inherit", - }); - } -} - -async function ensureServiceConfig(nameOrPathOrUrl: string) { - const fluenceConfig = await initReadonlyFluenceConfig(); - - const serviceConfigFromFluenceYAML = - fluenceConfig?.services?.[nameOrPathOrUrl]; - - const servicePathOrUrl = serviceConfigFromFluenceYAML?.get ?? nameOrPathOrUrl; - const isServiceFromFluenceYAML = serviceConfigFromFluenceYAML !== undefined; - - const serviceConfig = await initReadonlyServiceConfig( - servicePathOrUrl, - isServiceFromFluenceYAML ? projectRootDir : cwd(), - ); - - if (serviceConfig === null) { - stopSpinner(color.red("error")); - return commandObj.error( - `No service config found at ${color.yellow(servicePathOrUrl)}`, - ); - } - - const serviceName = isServiceFromFluenceYAML - ? nameOrPathOrUrl - : serviceConfig.name; - - return { serviceName, serviceConfig }; -} - -const ENTER_PATH_OR_URL_MSG = - "Enter path to a service or url to .tar.gz archive"; - -async function promptForNamePathOrUrl(): Promise { - const fluenceConfig = await initFluenceConfig(); - const serviceNames = Object.keys(fluenceConfig?.services ?? {}); - - const selected = await list({ - message: "Select service", - options: - serviceNames.length === 0 ? [] : [...serviceNames, ENTER_PATH_OR_URL_MSG], - oneChoiceMessage(s) { - return `Do you want to select ${color.yellow(s)} service`; - }, - onNoChoices() { - return ENTER_PATH_OR_URL_MSG; - }, - }); - - if (selected === ENTER_PATH_OR_URL_MSG) { - return input({ - message: ENTER_PATH_OR_URL_MSG, - }); - } - - return selected; -} diff --git a/packages/cli/package/src/commands/spell/build.ts b/packages/cli/package/src/commands/spell/build.ts deleted file mode 100644 index 0d5cd66db..000000000 --- a/packages/cli/package/src/commands/spell/build.ts +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import { color } from "@oclif/color"; -import { Args } from "@oclif/core"; - -import { BaseCommand } from "../../baseCommand.js"; -import { commandObj } from "../../lib/commandObj.js"; -import { FLUENCE_CONFIG_FULL_FILE_NAME, IMPORT_FLAG } from "../../lib/const.js"; -import { compileSpells } from "../../lib/deployWorkers.js"; -import { ensureFluenceProject } from "../../lib/helpers/ensureFluenceProject.js"; -import { commaSepStrToArr } from "../../lib/helpers/utils.js"; -import { initCli } from "../../lib/lifeCycle.js"; - -export default class Build extends BaseCommand { - static override description = - "Check spells aqua is able to compile without any errors"; - static override examples = ["<%= config.bin %> <%= command.id %>"]; - static override flags = { - ...IMPORT_FLAG, - }; - static override args = { - "SPELL-NAMES": Args.string({ - description: `Comma separated names of spells to build. Example: "spell1,spell2" (by default all spells from 'spells' property in ${FLUENCE_CONFIG_FULL_FILE_NAME} will be built)`, - }), - }; - async run(): Promise { - const { args, flags } = await initCli(this, await this.parse(Build), true); - const fluenceConfig = await ensureFluenceProject(); - - const spellNames = commaSepStrToArr( - args["SPELL-NAMES"] ?? Object.keys(fluenceConfig.spells ?? {}).join(","), - ); - - if (spellNames.length > 0) { - await compileSpells(flags.import, spellNames); - - commandObj.log( - `Compiled ${color.yellow(spellNames.join(", "))} successfully`, - ); - } - } -} diff --git a/packages/cli/package/src/commands/spell/new.ts b/packages/cli/package/src/commands/spell/new.ts deleted file mode 100644 index 966d973cb..000000000 --- a/packages/cli/package/src/commands/spell/new.ts +++ /dev/null @@ -1,178 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import assert from "assert"; -import { access, mkdir, writeFile } from "fs/promises"; -import { join, relative } from "path"; - -import { color } from "@oclif/color"; -import { Args, Flags } from "@oclif/core"; - -import { BaseCommand } from "../../baseCommand.js"; -import { commandObj, isInteractive } from "../../lib/commandObj.js"; -import { initFluenceConfig } from "../../lib/configs/project/fluence.js"; -import { initNewReadonlySpellConfig } from "../../lib/configs/project/spell.js"; -import { - FS_OPTIONS, - getSpellAquaFileContent, - SPELL_AQUA_FILE_NAME, -} from "../../lib/const.js"; -import { initCli } from "../../lib/lifeCycle.js"; -import { ensureSpellsDir, projectRootDir } from "../../lib/paths.js"; -import { checkboxes, input } from "../../lib/prompt.js"; - -export default class New extends BaseCommand { - static override description = "Create a new spell template"; - static override examples = ["<%= config.bin %> <%= command.id %>"]; - static override flags = { - path: Flags.string({ - description: "Path to spells dir (default: src/spells)", - helpValue: "", - }), - }; - static override args = { - name: Args.string({ - description: "Spell name", - }), - }; - async run(): Promise { - const { args, flags } = await initCli(this, await this.parse(New)); - const fluenceConfig = await initFluenceConfig(); - const pathToSpellsDir = flags.path ?? (await ensureSpellsDir()); - - function getPathToSpellDir(spellName: string) { - return join(pathToSpellsDir, spellName); - } - - async function validateSpellName(spellName: string) { - if (fluenceConfig?.spells?.[spellName] !== undefined) { - return `Spell ${color.yellow(spellName)} already exists in ${fluenceConfig.$getPath()}`; - } - - const pathToSpellDir = getPathToSpellDir(spellName); - - try { - await access(pathToSpellDir); - return `There is already a file or directory at ${color.yellow(pathToSpellDir)}`; - } catch {} - - return true; - } - - const spellName = - args.name ?? - (await input({ - message: "Enter spell name", - validate(spellName: string) { - return validateSpellName(spellName); - }, - })); - - const spellNameValidity = await validateSpellName(spellName); - - if (spellNameValidity !== true) { - return commandObj.error(spellNameValidity); - } - - const pathToSpellDir = join(pathToSpellsDir, spellName); - await generateNewSpell(pathToSpellDir, spellName); - - commandObj.log( - `Successfully generated template for new spell at ${color.yellow( - pathToSpellDir, - )}`, - ); - - if (fluenceConfig === null) { - return; - } - - if (fluenceConfig.spells === undefined) { - fluenceConfig.spells = {}; - } - - fluenceConfig.spells[spellName] = { - get: relative(projectRootDir, pathToSpellDir), - }; - - await fluenceConfig.$commit(); - - const deployments = Object.keys(fluenceConfig.deployments ?? {}); - - if (!isInteractive || deployments.length === 0) { - return; - } - - const deploymentNames = await checkboxes({ - message: `If you want to add spell ${color.yellow(spellName)} to some of the deployments - please select them or press enter to continue`, - options: deployments, - oneChoiceMessage(deploymentName) { - return `Do you want to add spell ${color.yellow(spellName)} to deployment ${color.yellow(deploymentName)}`; - }, - onNoChoices(): Array { - return []; - }, - }); - - if (deploymentNames.length === 0) { - commandObj.logToStderr( - `No deployments selected. You can add it manually later at ${fluenceConfig.$getPath()}`, - ); - - return; - } - - deploymentNames.forEach((deploymentName) => { - assert( - fluenceConfig.deployments !== undefined, - "Unreachable. It's checked above that fluenceConfig.deployments is not undefined", - ); - - const deployment = fluenceConfig.deployments[deploymentName]; - - assert( - deployment !== undefined, - "Unreachable. deploymentName is guaranteed to exist in fluenceConfig.deployments", - ); - - fluenceConfig.deployments[deploymentName] = { - ...deployment, - spells: [...(deployment.spells ?? []), spellName], - }; - }); - - await fluenceConfig.$commit(); - - commandObj.log( - `Added spell ${color.yellow(spellName)} to deployments:\n${color.yellow( - deploymentNames.join("\n"), - )}`, - ); - } -} - -async function generateNewSpell(pathToSpellDir: string, spellName: string) { - await mkdir(pathToSpellDir, { recursive: true }); - - await writeFile( - join(pathToSpellDir, SPELL_AQUA_FILE_NAME), - getSpellAquaFileContent(spellName), - FS_OPTIONS, - ); - - await initNewReadonlySpellConfig(pathToSpellDir); -} diff --git a/packages/cli/package/src/commands/workers/deploy.ts b/packages/cli/package/src/commands/workers/deploy.ts deleted file mode 100644 index b8d20bd55..000000000 --- a/packages/cli/package/src/commands/workers/deploy.ts +++ /dev/null @@ -1,162 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import { color } from "@oclif/color"; -import { Args } from "@oclif/core"; - -import { BaseCommand } from "../../baseCommand.js"; -import { commandObj } from "../../lib/commandObj.js"; -import type { Upload_deployArgConfig } from "../../lib/compiled-aqua/installation-spell/cli.js"; -import { initNewWorkersConfig } from "../../lib/configs/project/workers.js"; -import { - OFF_AQUA_LOGS_FLAG, - FLUENCE_CONFIG_FULL_FILE_NAME, - FLUENCE_CLIENT_FLAGS, - IMPORT_FLAG, - NO_BUILD_FLAG, - TRACING_FLAG, - MARINE_BUILD_ARGS_FLAG, -} from "../../lib/const.js"; -import { - disconnectFluenceClient, - initFluenceClient, -} from "../../lib/jsClient.js"; -import { initCli } from "../../lib/lifeCycle.js"; -import { doRegisterIpfsClient } from "../../lib/localServices/ipfs.js"; -import { ensureFluenceEnv } from "../../lib/resolveFluenceEnv.js"; - -export default class Deploy extends BaseCommand { - static override hidden = true; - static override description = `Deploy workers to hosts, described in 'hosts' property in ${FLUENCE_CONFIG_FULL_FILE_NAME}`; - static override examples = ["<%= config.bin %> <%= command.id %>"]; - static override flags = { - ...OFF_AQUA_LOGS_FLAG, - ...FLUENCE_CLIENT_FLAGS, - ...IMPORT_FLAG, - ...NO_BUILD_FLAG, - ...TRACING_FLAG, - ...MARINE_BUILD_ARGS_FLAG, - }; - static override args = { - "WORKER-NAMES": Args.string({ - description: `Comma separated names of workers to deploy. Example: "worker1,worker2" (by default all workers from 'hosts' property in ${FLUENCE_CONFIG_FULL_FILE_NAME} are deployed)`, - }), - }; - async run(): Promise { - const { flags, args } = await initCli(this, await this.parse(Deploy), true); - const workersConfig = await initNewWorkersConfig(); - - const { ensureAquaFileWithWorkerInfo, prepareForDeploy } = await import( - "../../lib/deployWorkers.js" - ); - - await initFluenceClient(flags); - await doRegisterIpfsClient(); - const { Fluence } = await import("@fluencelabs/js-client"); - const relayId = Fluence.getClient().getRelayPeerId(); - const initPeerId = Fluence.getClient().getPeerId(); - const fluenceEnv = await ensureFluenceEnv(); - - const uploadDeployArg = await prepareForDeploy({ - deploymentNamesString: args["WORKER-NAMES"], - fluenceEnv, - initPeerId, - flags, - }); - - const uploadDeployResult = await uploadDeploy( - flags.tracing, - uploadDeployArg, - ); - - const timestamp = new Date().toISOString(); - - const { newDeployedWorkers, infoToPrint } = - uploadDeployResult.workers.reduce<{ - newDeployedWorkers: Exclude; - infoToPrint: Record< - string, - Array<{ - workerId: string; - hostId: string; - }> - >; - }>( - (acc, { name, dummy_deal_id: dummyDealId, ...worker }) => { - return { - newDeployedWorkers: { - ...acc.newDeployedWorkers, - [name]: { ...worker, timestamp, relayId, dummyDealId }, - }, - infoToPrint: { - ...acc.infoToPrint, - [name]: worker.installation_spells.map( - ({ host_id, worker_id }) => { - return { - hostId: host_id, - workerId: worker_id, - }; - }, - ), - }, - }; - }, - { newDeployedWorkers: {}, infoToPrint: {} }, - ); - - workersConfig.hosts = { - ...workersConfig.hosts, - [fluenceEnv]: { - ...(workersConfig.hosts?.[fluenceEnv] ?? {}), - ...newDeployedWorkers, - }, - }; - - await workersConfig.$commit(); - await ensureAquaFileWithWorkerInfo(); - const { yamlDiffPatch } = await import("yaml-diff-patch"); - - commandObj.log( - `\n\n${color.yellow("Success!")}\n\nrelay: ${relayId}\n\n${yamlDiffPatch( - "", - {}, - { "deployed workers": infoToPrint }, - )}`, - ); - - await disconnectFluenceClient(); - } -} - -async function uploadDeploy( - tracing: boolean, - uploadArg: Upload_deployArgConfig, -) { - if (tracing) { - const { upload_deploy } = await import( - "../../lib/compiled-aqua-with-tracing/installation-spell/cli.js" - ); - - return upload_deploy(uploadArg); - } - - const { upload_deploy } = await import( - "../../lib/compiled-aqua/installation-spell/cli.js" - ); - - return upload_deploy(uploadArg); -} diff --git a/packages/cli/package/src/commands/workers/logs.ts b/packages/cli/package/src/commands/workers/logs.ts deleted file mode 100644 index 7c953fdf1..000000000 --- a/packages/cli/package/src/commands/workers/logs.ts +++ /dev/null @@ -1,214 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import { color } from "@oclif/color"; -import { Args, Flags } from "@oclif/core"; - -import { BaseCommand } from "../../baseCommand.js"; -import { commandObj } from "../../lib/commandObj.js"; -import type { Get_logsArgApp_workers } from "../../lib/compiled-aqua/installation-spell/cli.js"; -import { initNewWorkersConfigReadonly } from "../../lib/configs/project/workers.js"; -import { - WORKERS_CONFIG_FULL_FILE_NAME, - OFF_AQUA_LOGS_FLAG, - FLUENCE_CLIENT_FLAGS, - TTL_FLAG_NAME, - DIAL_TIMEOUT_FLAG_NAME, - TRACING_FLAG, - type FluenceEnv, -} from "../../lib/const.js"; -import { formatAquaLogs } from "../../lib/helpers/formatAquaLogs.js"; -import { stringifyUnknown } from "../../lib/helpers/stringifyUnknown.js"; -import { commaSepStrToArr } from "../../lib/helpers/utils.js"; -import { - disconnectFluenceClient, - initFluenceClient, -} from "../../lib/jsClient.js"; -import { initCli } from "../../lib/lifeCycle.js"; -import { input } from "../../lib/prompt.js"; -import { ensureFluenceEnv } from "../../lib/resolveFluenceEnv.js"; - -export default class Logs extends BaseCommand { - static override hidden = true; - static override description = `Get logs from deployed workers for hosts listed in ${WORKERS_CONFIG_FULL_FILE_NAME}`; - static override examples = ["<%= config.bin %> <%= command.id %>"]; - static override flags = { - ...FLUENCE_CLIENT_FLAGS, - ...OFF_AQUA_LOGS_FLAG, - "worker-id": Flags.string({ - description: "Worker id", - helpValue: "", - }), - "host-id": Flags.string({ - description: "Host id", - helpValue: "", - }), - "spell-id": Flags.string({ - description: "Spell id", - helpValue: "", - default: "worker-spell", - }), - ...TRACING_FLAG, - }; - static override args = { - "WORKER-NAMES": Args.string({ - description: `Worker names to get logs for (by default all worker names from 'hosts' property of ${WORKERS_CONFIG_FULL_FILE_NAME})`, - }), - }; - async run(): Promise { - const { flags, args } = await initCli(this, await this.parse(Logs)); - await initFluenceClient(flags); - const fluenceEnv = await ensureFluenceEnv(); - - const logsArg = await getLogsArg({ - maybeWorkerNamesString: args["WORKER-NAMES"], - maybeWorkerId: flags["worker-id"], - maybeHostId: flags["host-id"], - spellId: flags["spell-id"], - fluenceEnv, - }); - - let logs; - - try { - logs = await getLogs(flags.tracing, logsArg); - } catch (e) { - commandObj.error( - `Wasn't able to get logs. You can try increasing --${TTL_FLAG_NAME} and --${DIAL_TIMEOUT_FLAG_NAME}: ${stringifyUnknown( - e, - )}`, - ); - } - - commandObj.logToStderr( - logs - .map((logs) => { - return formatAquaLogs(logs); - }) - .join("\n\n"), - ); - - await disconnectFluenceClient(); - } -} - -type GetLogsArgArg = { - maybeWorkerNamesString: string | undefined; - maybeWorkerId: string | undefined; - maybeHostId: string | undefined; - spellId: string; - fluenceEnv: FluenceEnv; -}; - -const getLogsArg = async ({ - maybeWorkerNamesString, - maybeWorkerId, - maybeHostId, - spellId, - fluenceEnv, -}: GetLogsArgArg): Promise => { - if (maybeWorkerId !== undefined || maybeHostId !== undefined) { - const workerId = - maybeWorkerId ?? (await input({ message: "Enter worker id" })); - - const hostId = maybeHostId ?? (await input({ message: "Enter host id" })); - - return { - workers: [ - { - definition: "", - installation_spells: [ - { - worker_id: workerId, - host_id: hostId, - spell_id: spellId, - }, - ], - name: "", - dummy_deal_id: "", - }, - ], - }; - } - - const workersConfig = await initNewWorkersConfigReadonly(); - - const hosts = - workersConfig.hosts?.[fluenceEnv] ?? - commandObj.error( - `No deployed workers found at ${color.yellow( - `hosts.${fluenceEnv}`, - )} in ${color.yellow(workersConfig.$getPath())}`, - ); - - const workerNamesSet = Object.keys(hosts); - - const workersToGetLogsFor = - maybeWorkerNamesString === undefined - ? workerNamesSet - : commaSepStrToArr(maybeWorkerNamesString); - - if (workersToGetLogsFor.length === 0) { - commandObj.error( - `No worker names provided. Please provide worker names to get logs for`, - ); - } - - const workerNamesNotFoundInWorkersConfig = workersToGetLogsFor.filter( - (workerName) => { - return !workerNamesSet.includes(workerName); - }, - ); - - if (workerNamesNotFoundInWorkersConfig.length !== 0) { - commandObj.error( - `Wasn't able to find workers ${workerNamesNotFoundInWorkersConfig - .map((workerName) => { - return color.yellow(workerName); - }) - .join(", ")} in ${color.yellow( - workersConfig.$getPath(), - )} please check the spelling and try again`, - ); - } - - return { - workers: Object.entries(hosts) - .filter(([name]) => { - return workersToGetLogsFor.includes(name); - }) - .map(([name, { dummyDealId, ...config }]) => { - return { name, dummy_deal_id: dummyDealId, ...config }; - }), - }; -}; - -async function getLogs(tracing: boolean, logsArg: Get_logsArgApp_workers) { - if (tracing) { - const { get_logs } = await import( - "../../lib/compiled-aqua-with-tracing/installation-spell/cli.js" - ); - - return get_logs(logsArg); - } - - const { get_logs } = await import( - "../../lib/compiled-aqua/installation-spell/cli.js" - ); - - return get_logs(logsArg); -} diff --git a/packages/cli/package/src/commands/workers/remove.ts b/packages/cli/package/src/commands/workers/remove.ts deleted file mode 100644 index 430063140..000000000 --- a/packages/cli/package/src/commands/workers/remove.ts +++ /dev/null @@ -1,165 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import { color } from "@oclif/color"; -import { Args } from "@oclif/core"; -import isEmpty from "lodash-es/isEmpty.js"; - -import { BaseCommand } from "../../baseCommand.js"; -import { commandObj } from "../../lib/commandObj.js"; -import type { RemoveArgWorkers } from "../../lib/compiled-aqua/installation-spell/deploy.js"; -import { initNewWorkersConfig } from "../../lib/configs/project/workers.js"; -import { - OFF_AQUA_LOGS_FLAG, - FLUENCE_CLIENT_FLAGS, - TRACING_FLAG, - WORKERS_CONFIG_FULL_FILE_NAME, -} from "../../lib/const.js"; -import { commaSepStrToArr } from "../../lib/helpers/utils.js"; -import { - disconnectFluenceClient, - initFluenceClient, -} from "../../lib/jsClient.js"; -import { initCli } from "../../lib/lifeCycle.js"; -import { ensureFluenceEnv } from "../../lib/resolveFluenceEnv.js"; - -export default class Remove extends BaseCommand { - static override hidden = true; - static override description = `Remove workers from hosts, described in 'hosts' property in ${WORKERS_CONFIG_FULL_FILE_NAME}`; - static override examples = ["<%= config.bin %> <%= command.id %>"]; - static override flags = { - ...OFF_AQUA_LOGS_FLAG, - ...FLUENCE_CLIENT_FLAGS, - ...TRACING_FLAG, - }; - static override args = { - "WORKER-NAMES": Args.string({ - description: `Comma separated names of workers to remove. Example: "worker1,worker2" (by default all workers from 'hosts' property in ${WORKERS_CONFIG_FULL_FILE_NAME} are removed)`, - }), - }; - async run(): Promise { - const { flags, args } = await initCli(this, await this.parse(Remove), true); - const workersConfig = await initNewWorkersConfig(); - - const { ensureAquaFileWithWorkerInfo } = await import( - "../../lib/deployWorkers.js" - ); - - await initFluenceClient(flags); - const { Fluence } = await import("@fluencelabs/js-client"); - const relayId = Fluence.getClient().getRelayPeerId(); - const fluenceEnv = await ensureFluenceEnv(); - - const deployedWorkersForEnv = - workersConfig.hosts?.[fluenceEnv] ?? - commandObj.error( - `No deployed workers found at ${color.yellow( - `hosts.${fluenceEnv}`, - )} in ${color.yellow(workersConfig.$getPath())}`, - ); - - const workersToRemove = - args["WORKER-NAMES"] === undefined - ? Object.keys(deployedWorkersForEnv) - : commaSepStrToArr(args["WORKER-NAMES"]); - - if (workersToRemove.length === 0) { - return commandObj.error("No worker names provided"); - } - - const deployedWorkersForEnvArr = Object.entries(deployedWorkersForEnv); - - const removeArg: RemoveArgWorkers = { - workers: deployedWorkersForEnvArr - .filter(([workerName]) => { - return workersToRemove.includes(workerName); - }) - .map(([name, worker]): RemoveArgWorkers["workers"][number] => { - return { - definition: worker.definition, - name, - dummy_deal_id: worker.dummyDealId, - installation_spells: worker.installation_spells, - }; - }), - }; - - const removeResult = await remove(flags.tracing, removeArg); - - const newHosts = Object.fromEntries( - deployedWorkersForEnvArr - .map(([name, { installation_spells: prevInstSp, ...rest }]) => { - const currentWorkerResult = removeResult.find((r) => { - return r.name === name; - }); - - const removedWorkerIds = currentWorkerResult?.worker_ids ?? []; - - const notRemovedWorkers = prevInstSp.filter(({ worker_id }) => { - const workerRemovedSuccessfully = - removedWorkerIds.includes(worker_id); - - return !workerRemovedSuccessfully; - }); - - return [ - name, - { installation_spells: notRemovedWorkers, ...rest }, - ] as const; - }) - .filter(([, { installation_spells }]) => { - return installation_spells.length > 0; - }), - ); - - workersConfig.hosts = newHosts; - - if (isEmpty(workersConfig.hosts)) { - delete workersConfig.hosts; - } - - await workersConfig.$commit(); - await ensureAquaFileWithWorkerInfo(); - const { yamlDiffPatch } = await import("yaml-diff-patch"); - - commandObj.log( - `\n\n${color.yellow("Success!")}\n\nrelay: ${relayId}\n\n${yamlDiffPatch( - "", - {}, - { removed: removeResult }, - )}`, - ); - - await disconnectFluenceClient(); - } -} - -async function remove(tracing: boolean, removeArg: RemoveArgWorkers) { - if (tracing) { - const { remove } = await import( - "../../lib/compiled-aqua-with-tracing/installation-spell/deploy.js" - ); - - return remove(removeArg); - } - - const { remove } = await import( - "../../lib/compiled-aqua/installation-spell/deploy.js" - ); - - return remove(removeArg); -} diff --git a/packages/cli/package/src/commands/workers/upload.ts b/packages/cli/package/src/commands/workers/upload.ts deleted file mode 100644 index be3a8b6da..000000000 --- a/packages/cli/package/src/commands/workers/upload.ts +++ /dev/null @@ -1,95 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import { Args } from "@oclif/core"; - -import { BaseCommand } from "../../baseCommand.js"; -import { jsonStringify } from "../../common.js"; -import { commandObj } from "../../lib/commandObj.js"; -import type { Upload_deployArgConfig } from "../../lib/compiled-aqua/installation-spell/cli.js"; -import { - OFF_AQUA_LOGS_FLAG, - FLUENCE_CONFIG_FULL_FILE_NAME, - FLUENCE_CLIENT_FLAGS, - IMPORT_FLAG, - NO_BUILD_FLAG, - TRACING_FLAG, - MARINE_BUILD_ARGS_FLAG, -} from "../../lib/const.js"; -import { - disconnectFluenceClient, - initFluenceClient, -} from "../../lib/jsClient.js"; -import { initCli } from "../../lib/lifeCycle.js"; -import { doRegisterIpfsClient } from "../../lib/localServices/ipfs.js"; -import { ensureFluenceEnv } from "../../lib/resolveFluenceEnv.js"; - -export default class Upload extends BaseCommand { - static override hidden = true; - static override description = `Upload workers to hosts, described in 'hosts' property in ${FLUENCE_CONFIG_FULL_FILE_NAME}`; - static override examples = ["<%= config.bin %> <%= command.id %>"]; - static override flags = { - ...FLUENCE_CLIENT_FLAGS, - ...OFF_AQUA_LOGS_FLAG, - ...IMPORT_FLAG, - ...NO_BUILD_FLAG, - ...TRACING_FLAG, - ...MARINE_BUILD_ARGS_FLAG, - }; - static override args = { - "WORKER-NAMES": Args.string({ - description: `Names of workers to deploy (by default all workers from 'hosts' property in ${FLUENCE_CONFIG_FULL_FILE_NAME} are deployed)`, - }), - }; - async run(): Promise { - const { flags, args } = await initCli(this, await this.parse(Upload), true); - await initFluenceClient(flags); - await doRegisterIpfsClient(); - const { Fluence } = await import("@fluencelabs/js-client"); - const initPeerId = Fluence.getClient().getPeerId(); - - const { prepareForDeploy } = await import("../../lib/deployWorkers.js"); - const fluenceEnv = await ensureFluenceEnv(); - - const uploadArg = await prepareForDeploy({ - deploymentNamesString: args["WORKER-NAMES"], - fluenceEnv, - initPeerId, - flags, - }); - - const uploadResult = await upload(flags.tracing, uploadArg); - commandObj.log(jsonStringify(uploadResult.workers)); - await disconnectFluenceClient(); - } -} - -async function upload(tracing: boolean, uploadArg: Upload_deployArgConfig) { - if (tracing) { - const { upload_workers } = await import( - "../../lib/compiled-aqua-with-tracing/installation-spell/upload.js" - ); - - return upload_workers(uploadArg); - } - - const { upload_workers } = await import( - "../../lib/compiled-aqua/installation-spell/upload.js" - ); - - return upload_workers(uploadArg); -} diff --git a/packages/cli/package/src/genConfigDocs.ts b/packages/cli/package/src/genConfigDocs.ts index 4dd9d3997..2a9d316fb 100644 --- a/packages/cli/package/src/genConfigDocs.ts +++ b/packages/cli/package/src/genConfigDocs.ts @@ -22,25 +22,14 @@ import { join } from "node:path"; import { jsonStringify } from "./common.js"; import { addTitleDescriptionAndVersionToSchemas } from "./lib/configs/initConfigNew.js"; import { options as envOptions } from "./lib/configs/project/env/env.js"; -import { fluenceSchema } from "./lib/configs/project/fluence.js"; -import { moduleSchema } from "./lib/configs/project/module.js"; import { options as providerOptions } from "./lib/configs/project/provider/provider.js"; import { options as providerArtifactsOptions } from "./lib/configs/project/providerArtifacts/providerArtifacts.js"; import { options as providerSecretsOptions } from "./lib/configs/project/providerSecrets/providerSecrets.js"; -import { serviceSchema } from "./lib/configs/project/service.js"; -import { spellSchema } from "./lib/configs/project/spell.js"; -import { workersSchema } from "./lib/configs/project/workers.js"; import { options as userConfigOptions } from "./lib/configs/user/config/config.js"; import { - FLUENCE_CONFIG_FILE_NAME, JSON_EXT, - MODULE_CONFIG_FILE_NAME, SCHEMAS_DIR_NAME, - SERVICE_CONFIG_FILE_NAME, USER_CONFIG_FILE_NAME, - WORKERS_CONFIG_FILE_NAME, - SPELL_CONFIG_FILE_NAME, - FS_OPTIONS, YAML_EXT, CLI_NAME_FULL, PROVIDER_CONFIG_FILE_NAME, @@ -68,17 +57,12 @@ function getLatestConfigOptionsSchema< } const configsInfo = Object.entries({ - [FLUENCE_CONFIG_FILE_NAME]: fluenceSchema, [PROVIDER_CONFIG_FILE_NAME]: getLatestConfigOptionsSchema( await addTitleDescriptionAndVersionToSchemas(providerOptions), ), [PROVIDER_SECRETS_CONFIG_FILE_NAME]: getLatestConfigOptionsSchema( await addTitleDescriptionAndVersionToSchemas(providerSecretsOptions), ), - [MODULE_CONFIG_FILE_NAME]: moduleSchema, - [SERVICE_CONFIG_FILE_NAME]: serviceSchema, - [SPELL_CONFIG_FILE_NAME]: spellSchema, - [WORKERS_CONFIG_FILE_NAME]: workersSchema, [USER_CONFIG_FILE_NAME]: getLatestConfigOptionsSchema( await addTitleDescriptionAndVersionToSchemas(userConfigOptions), ), @@ -102,7 +86,7 @@ await mkdir(DOCS_CONFIGS_DIR_PATH, { recursive: true }); await Promise.all( configsInfo.map(({ schemaPath, schema }): Promise => { - return writeFile(schemaPath, jsonStringify(schema), FS_OPTIONS); + return writeFile(schemaPath, jsonStringify(schema), "utf8"); }), ); @@ -114,7 +98,7 @@ await Promise.all( args: ["-schema", schemaPath], }); - await writeFile(join(DOCS_CONFIGS_DIR_PATH, docFileName), md, FS_OPTIONS); + await writeFile(join(DOCS_CONFIGS_DIR_PATH, docFileName), md, "utf8"); }), ); @@ -136,7 +120,7 @@ ${configsInfo .join("\n\n")}`, ); -const commandsContent = await readFile(DOCS_COMMANDS_PATH, FS_OPTIONS); +const commandsContent = await readFile(DOCS_COMMANDS_PATH, "utf8"); await writeFile( DOCS_COMMANDS_PATH, diff --git a/packages/cli/package/src/lib/addService.ts b/packages/cli/package/src/lib/addService.ts deleted file mode 100644 index a90eba511..000000000 --- a/packages/cli/package/src/lib/addService.ts +++ /dev/null @@ -1,271 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import assert from "node:assert"; -import { relative } from "node:path"; - -import { color } from "@oclif/color"; - -import { resolveSingleServiceModuleConfigsAndBuild } from "./build.js"; -import { commandObj, isInteractive } from "./commandObj.js"; -import { initFluenceConfig } from "./configs/project/fluence.js"; -import { initReadonlyModuleConfig } from "./configs/project/module.js"; -import { - FACADE_MODULE_NAME, - initReadonlyServiceConfig, -} from "./configs/project/service.js"; -import { DEFAULT_DEPLOYMENT_NAME } from "./const.js"; -import { - AQUA_NAME_REQUIREMENTS, - getModuleWasmPath, - validateAquaName, -} from "./helpers/downloadFile.js"; -import { ensureFluenceProject } from "./helpers/ensureFluenceProject.js"; -import { updateAquaServiceInterfaceFile } from "./helpers/generateServiceInterface.js"; -import type { MarineCLI } from "./marineCli.js"; -import { projectRootDir } from "./paths.js"; -import { input, checkboxes } from "./prompt.js"; - -export async function ensureValidServiceName(serviceName: string) { - const validateServiceName = await createServiceNameValidator(); - const serviceNameValidity = validateServiceName(serviceName); - - if (typeof serviceNameValidity === "string") { - serviceName = await input({ - message: serviceNameValidity, - validate: validateServiceName, - }); - } - - return serviceName; -} - -async function createServiceNameValidator() { - const fluenceConfig = await initFluenceConfig(); - - return function validateServiceName(serviceName: string): true | string { - if ( - fluenceConfig !== null && - serviceName in (fluenceConfig.services ?? {}) - ) { - return `Service with name ${color.yellow( - serviceName, - )} already exists in ${fluenceConfig.$getPath()}. Please enter another name`; - } - - const validity = validateAquaName(serviceName); - - if (typeof validity === "string") { - return validity; - } - - return true; - }; -} - -type AddServiceArg = { - serviceName: string; - absolutePathOrUrl: string; - marineCli: MarineCLI; - marineBuildArgs?: string | undefined; - isATemplateInitStep?: boolean; -}; - -export async function addService({ - serviceName: serviceNameFromArgs, - absolutePathOrUrl, - marineCli, - marineBuildArgs, - isATemplateInitStep = false, -}: AddServiceArg): Promise { - const fluenceConfig = await ensureFluenceProject(); - let serviceName = serviceNameFromArgs; - - if (fluenceConfig.services === undefined) { - fluenceConfig.services = {}; - } - - const validateServiceName = (name: string): true | string => { - const aquaNameValidity = validateAquaName(name); - - if (typeof aquaNameValidity === "string") { - return aquaNameValidity; - } - - return ( - !(name in (fluenceConfig.services ?? {})) || - `You already have ${color.yellow(name)} in ${color.yellow( - fluenceConfig.$getPath(), - )}` - ); - }; - - const serviceNameValidity = validateServiceName(serviceName); - - if (serviceNameValidity !== true) { - commandObj.warn(serviceNameValidity); - - serviceName = await input({ - message: `Enter another name for the service (${AQUA_NAME_REQUIREMENTS})`, - validate: validateServiceName, - }); - } - - fluenceConfig.services = { - ...fluenceConfig.services, - [serviceName]: { - get: relative(projectRootDir, absolutePathOrUrl), - }, - }; - - const serviceConfig = await initReadonlyServiceConfig( - absolutePathOrUrl, - projectRootDir, - ); - - if (serviceConfig === null) { - return commandObj.error( - `Service config not found at ${color.yellow(absolutePathOrUrl)}`, - ); - } - - const facadeModuleConfig = await initReadonlyModuleConfig( - serviceConfig.modules[FACADE_MODULE_NAME].get, - serviceConfig.$getDirPath(), - ); - - assert( - facadeModuleConfig !== null, - "Unreachable. Facade module is always present in service config", - ); - - await fluenceConfig.$commit(); - - if (!isATemplateInitStep) { - commandObj.logToStderr( - `Added ${color.yellow(serviceName)} to ${color.yellow( - fluenceConfig.$getPath(), - )}`, - ); - } - - await addServiceToDeployment({ isATemplateInitStep, serviceName }); - - await resolveSingleServiceModuleConfigsAndBuild({ - serviceName, - serviceConfig, - marineCli, - marineBuildArgs, - }); - - await updateAquaServiceInterfaceFile( - { - [serviceName]: getModuleWasmPath(facadeModuleConfig), - }, - marineCli, - ); - - return serviceName; -} - -type AddServiceToDeploymentArgs = { - isATemplateInitStep: boolean; - serviceName: string; -}; - -async function addServiceToDeployment({ - isATemplateInitStep, - serviceName, -}: AddServiceToDeploymentArgs) { - const fluenceConfig = await ensureFluenceProject(); - const deployments = Object.keys(fluenceConfig.deployments ?? {}); - - if (deployments.length === 0) { - return; - } - - if (isATemplateInitStep) { - if (fluenceConfig.deployments === undefined) { - return; - } - - const defaultDeployment = - fluenceConfig.deployments[DEFAULT_DEPLOYMENT_NAME]; - - if (defaultDeployment === undefined) { - return; - } - - fluenceConfig.deployments[DEFAULT_DEPLOYMENT_NAME] = { - ...defaultDeployment, - services: [...(defaultDeployment.services ?? []), serviceName], - }; - - await fluenceConfig.$commit(); - return; - } - - if (!isInteractive) { - return; - } - - const deploymentNames = await checkboxes({ - message: `If you want to add service ${color.yellow(serviceName)} to some of the deployments - please select them or press enter to continue`, - options: deployments, - oneChoiceMessage(deploymentName) { - return `Do you want to add service ${color.yellow(serviceName)} to deployment ${color.yellow(deploymentName)}`; - }, - onNoChoices(): Array { - return []; - }, - }); - - if (deploymentNames.length === 0) { - commandObj.logToStderr( - `No deployments selected. You can add it manually later at ${fluenceConfig.$getPath()}`, - ); - - return; - } - - deploymentNames.forEach((deploymentName) => { - assert( - fluenceConfig.deployments !== undefined, - "Unreachable. It's checked above that fluenceConfig.deployments is not undefined", - ); - - const deployment = fluenceConfig.deployments[deploymentName]; - - assert( - deployment !== undefined, - "Unreachable. deploymentName is guaranteed to exist in fluenceConfig.deployments", - ); - - fluenceConfig.deployments[deploymentName] = { - ...deployment, - services: [...(deployment.services ?? []), serviceName], - }; - }); - - await fluenceConfig.$commit(); - - commandObj.log( - `Added service ${color.yellow(serviceName)} to deployments: ${color.yellow( - deploymentNames.join(", "), - )}`, - ); -} diff --git a/packages/cli/package/src/lib/aqua.ts b/packages/cli/package/src/lib/aqua.ts deleted file mode 100644 index ad095a086..000000000 --- a/packages/cli/package/src/lib/aqua.ts +++ /dev/null @@ -1,404 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import assert from "node:assert"; -import { mkdir, readdir, stat, writeFile } from "node:fs/promises"; -import { extname, join, parse, resolve } from "node:path"; - -import type { - CompileFromPathArgs, - CompileFuncCallFromPathArgs, -} from "@fluencelabs/aqua-api"; -import type { GatherImportsResult } from "@fluencelabs/npm-aqua-compiler"; -import { color } from "@oclif/color"; - -import CLIPackageJSON from "../versions/cli.package.json" with { type: "json" }; - -import { commandObj } from "./commandObj.js"; -import type { - CompileAquaConfig, - Constants, -} from "./configs/project/fluence.js"; -import { LOG_LEVEL_COMPILER_FLAG_NAME } from "./const.js"; -import { - type CommonAquaCompilationFlags, - FS_OPTIONS, - JS_EXT, - TS_EXT, - CLI_NAME, - AQUA_LOG_LEVELS, - aquaLogLevelsString, - isAquaLogLevel, - type AquaLogLevel, -} from "./const.js"; -import { getAquaImports } from "./helpers/aquaImports.js"; -import { boolToStr, numToStr } from "./helpers/typesafeStringify.js"; -import { splitErrorsAndResults } from "./helpers/utils.js"; -import { projectRootDir } from "./paths.js"; -import { list } from "./prompt.js"; -import type { Required } from "./typeHelpers.js"; - -const getAquaFilesRecursively = async (dirPath: string): Promise => { - const files = await readdir(dirPath); - - const pathsWithStats = await Promise.all( - files.map(async (fileName) => { - const filePath = join(dirPath, fileName); - const stats = await stat(filePath); - return [filePath, stats] as const; - }), - ); - - return ( - await Promise.all( - pathsWithStats - .filter(([path, stats]) => { - return stats.isDirectory() || extname(path).toLowerCase() === ".aqua"; - }) - .map(([path, stats]): Promise => { - return stats.isDirectory() - ? getAquaFilesRecursively(path) - : Promise.resolve([path]); - }), - ) - ).flat(); -}; - -const writeFileAndMakeSureDirExists = async ( - filePath: string, - dataPromise: string | Promise, -) => { - const data = await dataPromise; - - if (data === undefined) { - return; - } - - const dirPath = parse(filePath).dir; - await mkdir(dirPath, { recursive: true }); - await writeFile(filePath, data, FS_OPTIONS); -}; - -let hasLoggedCompilerVersion = false; - -export async function importAquaCompiler() { - if (!hasLoggedCompilerVersion) { - commandObj.logToStderr( - color.blue( - `Using aqua compiler version: ${CLIPackageJSON.dependencies["@fluencelabs/aqua-api"]}`, - ), - ); - - hasLoggedCompilerVersion = true; - } - - return import("@fluencelabs/aqua-api"); -} - -export type CompileToFilesArgs = CompileFromPathArgs & { - outputPathAbsolute: string | undefined; - dry?: boolean; -}; - -export async function compileToFiles({ - outputPathAbsolute, - targetType, - dry = false, - ...compileArgs -}: CompileToFilesArgs): Promise { - const isInputPathADirectory = ( - await stat(compileArgs.filePath) - ).isDirectory(); - - const { compileFromPath } = await importAquaCompiler(); - - const [resultsWithErrors, compilationResultsWithFilePaths] = - await compileDirOrFile(compileFromPath, compileArgs); - - if (resultsWithErrors.length !== 0) { - return commandObj.error( - resultsWithErrors - .map(({ compilationResult, aquaFilePath }) => { - const possiblyForgotToInstallDependenciesText = - compilationResult.errors.some((e) => { - return e.includes("Cannot resolve imports"); - }) - ? possiblyForgotToInstallDependenciesError - : ""; - - return `${color.yellow( - aquaFilePath, - )}\n\n${compilationResult.errors.join( - "\n", - )}${possiblyForgotToInstallDependenciesText}`; - }) - .join("\n\n"), - ); - } - - if (compilationResultsWithFilePaths.length === 0) { - return commandObj.error(`No aqua files found at ${compileArgs.filePath}`); - } - - if (dry) { - return; - } - - assert( - typeof outputPathAbsolute === "string", - `Unreachable. outputPath type is "${typeof outputPathAbsolute}", but it should have been of type "string", because it's not dry run`, - ); - - await mkdir(outputPathAbsolute, { recursive: true }); - - const inputDirPath = isInputPathADirectory - ? compileArgs.filePath - : parse(compileArgs.filePath).dir; - - const aquaToJs = (await import("@fluencelabs/aqua-to-js")).default; - - await Promise.all( - compilationResultsWithFilePaths.flatMap( - ({ compilationResult, aquaFilePath }) => { - const parsedPath = parse(aquaFilePath); - const fileNameWithoutExt = parsedPath.name; - const dirPath = parsedPath.dir; - - const finalOutputDirPath = dirPath.replace( - inputDirPath, - outputPathAbsolute, - ); - - if (targetType === "ts") { - return [ - writeFileAndMakeSureDirExists( - join(finalOutputDirPath, `${fileNameWithoutExt}.${TS_EXT}`), - aquaToJs(compilationResult, "ts").then((r) => { - return r?.sources; - }), - ), - ]; - } - - if (targetType === "js") { - const aquaToJsPromise = aquaToJs(compilationResult, "js"); - - return [ - writeFileAndMakeSureDirExists( - join(finalOutputDirPath, `${fileNameWithoutExt}.${JS_EXT}`), - aquaToJsPromise.then((r) => { - return r?.sources; - }), - ), - writeFileAndMakeSureDirExists( - join(finalOutputDirPath, `${fileNameWithoutExt}.d.${TS_EXT}`), - aquaToJsPromise.then((r) => { - return r?.types; - }), - ), - ]; - } - - return Object.entries(compilationResult.functions).map( - ([name, { script }]) => { - return writeFileAndMakeSureDirExists( - join(finalOutputDirPath, `${fileNameWithoutExt}.${name}.air`), - script, - ); - }, - ); - }, - ), - ); -} - -export async function compileFunctionCall(args: CompileFuncCallFromPathArgs) { - const { compileAquaCallFromPath } = await importAquaCompiler(); - return compileDirOrFile(compileAquaCallFromPath, args); -} - -// TODO: think about the way to improve this https://github.com/fluencelabs/cli/pull/743#discussion_r1475857293 -async function compileDirOrFile< - T extends { filePath: string }, - U extends { errors: string[] }, ->(fn: (args: T) => Promise, compileArgs: T) { - const isDir = (await stat(compileArgs.filePath)).isDirectory(); - - const compilationResultsWithFilePaths = isDir - ? await Promise.all( - (await getAquaFilesRecursively(compileArgs.filePath)).map( - async (aquaFilePath) => { - return { - compilationResult: await fn({ - ...compileArgs, - filePath: aquaFilePath, - }), - aquaFilePath, - }; - }, - ), - ) - : [ - { - compilationResult: await fn(compileArgs), - aquaFilePath: compileArgs.filePath, - }, - ]; - - return splitErrorsAndResults(compilationResultsWithFilePaths, (result) => { - return result.compilationResult.errors.length === 0 - ? { result } - : { error: result }; - }); -} - -const possiblyForgotToInstallDependenciesError = ` --------------------------------------------------------------------------------- - - -Also make sure you installed dependencies using ${color.yellow( - `${CLI_NAME} dep i`, -)} - - --------------------------------------------------------------------------------- -`; - -const CONST_SEPARATOR = " = "; - -function formatConstantsFromFlags( - constants: string[] | undefined = [], -): string[] | undefined { - const [errors, results] = splitErrorsAndResults(constants, (c) => { - if (!c.includes("=")) { - return { - error: c, - }; - } - - return { - result: c - .split("=") - .map((s) => { - return s.trim(); - }) - .join(CONST_SEPARATOR), - }; - }); - - if (errors.length !== 0) { - commandObj.error( - `Invalid --const flag values: ${color.yellow( - errors.join(", "), - )}. Must be in format = `, - ); - } - - return results; -} - -function formatConstantsFromConfig(constants: Constants) { - return Object.entries(constants).map(([name, value]) => { - const val = - typeof value === "string" - ? `"${value}"` - : typeof value === "number" - ? numToStr(value) - : boolToStr(value); - - return `${name}${CONST_SEPARATOR}${val}`; - }); -} - -// Required is used to make sure that we didn't forget to appropriately handle all aqua args -export type ResolvedAquaConfig = Required; - -export function resolveAquaConfig( - compileAquaConfig: CompileAquaConfig, - imports: CompileFromPathArgs["imports"], -): ResolvedAquaConfig { - return { - imports, - filePath: resolve(projectRootDir, compileAquaConfig.input), - targetType: compileAquaConfig.target, - logLevel: compileAquaConfig.logLevel, - noRelay: compileAquaConfig.noRelay, - noXor: compileAquaConfig.noXor, - tracing: compileAquaConfig.tracing, - noEmptyResponse: compileAquaConfig.noEmptyResponse, - constants: - compileAquaConfig.constants === undefined - ? undefined - : formatConstantsFromConfig(compileAquaConfig.constants), - }; -} - -// Required is used to make sure that we didn't forget to appropriately handle all aqua args -export type ResolvedCommonAquaCompilationFlags = Required< - Omit & { - imports: GatherImportsResult; - } ->; - -export async function resolveCommonAquaCompilationFlags( - flags: CommonAquaCompilationFlags & { quiet?: boolean }, -): Promise { - return { - imports: await getAquaImports(flags.import), - constants: formatConstantsFromFlags(flags.const), - logLevel: await resolveAquaLogLevel( - flags["log-level-compiler"], - flags["quiet"] ?? false, - ), - noRelay: flags["no-relay"], - noXor: flags["no-xor"], - tracing: flags.tracing, - noEmptyResponse: flags["no-empty-response"], - }; -} - -async function resolveAquaLogLevel( - maybeAquaLogLevel: string | undefined, - isQuite: boolean, -): Promise { - if (isQuite) { - return "off"; - } - - if (maybeAquaLogLevel === undefined) { - return undefined; - } - - if (isAquaLogLevel(maybeAquaLogLevel)) { - return maybeAquaLogLevel; - } - - commandObj.warn( - `Invalid --${LOG_LEVEL_COMPILER_FLAG_NAME} flag value: '${maybeAquaLogLevel}' ${aquaLogLevelsString}`, - ); - - return list({ - message: "Select a valid compiler log level", - oneChoiceMessage() { - throw new Error("Unreachable"); - }, - onNoChoices() { - throw new Error("Unreachable"); - }, - options: [...AQUA_LOG_LEVELS], - }); -} diff --git a/packages/cli/package/src/lib/build.ts b/packages/cli/package/src/lib/build.ts deleted file mode 100644 index 01e88bc31..000000000 --- a/packages/cli/package/src/lib/build.ts +++ /dev/null @@ -1,365 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import assert from "node:assert"; - -import { color } from "@oclif/color"; - -import type { - FluenceConfigReadonly, - OverrideModules, -} from "../lib/configs/project/fluence.js"; -import { - initReadonlyModuleConfig, - type ModuleConfigReadonly, -} from "../lib/configs/project/module.js"; -import { - FACADE_MODULE_NAME, - initReadonlyServiceConfig, - type ServiceModuleV0, - type ServiceConfigReadonly, -} from "../lib/configs/project/service.js"; -import { - MODULE_CONFIG_FULL_FILE_NAME, - SERVICE_CONFIG_FULL_FILE_NAME, -} from "../lib/const.js"; -import { - getUrlOrAbsolutePath, - getModuleWasmPath, - isUrl, -} from "../lib/helpers/downloadFile.js"; -import type { MarineCLI } from "../lib/marineCli.js"; - -import { buildModules } from "./buildModules.js"; -import { commandObj } from "./commandObj.js"; -import { initFluenceConfig } from "./configs/project/fluence.js"; -import { overrideModule } from "./deployWorkers.js"; -import { updateAquaServiceInterfaceFile } from "./helpers/generateServiceInterface.js"; -import { genServiceConfigToml } from "./helpers/serviceConfigToml.js"; -import { startSpinner, stopSpinner } from "./helpers/spinner.js"; -import { projectRootDir } from "./paths.js"; - -type ModuleNameAndConfigDefinedInService = { - moduleName: string; - moduleConfig: ServiceModuleV0; -}; -type ServiceInfoWithUnresolvedModuleConfigs = { - serviceName: string; - serviceDirPath: string; - moduleNamesAndConfigsDefinedInService: Array; -}; - -type ServiceInfo = Omit< - ServiceInfoWithUnresolvedModuleConfigs, - "moduleNamesAndConfigsDefinedInService" | "serviceDirPath" -> & { - moduleConfigs: Array; -}; - -type ResolveServiceInfosArg = { - fluenceConfig: FluenceConfigReadonly; -}; - -const resolveServiceInfos = async ({ - fluenceConfig, -}: ResolveServiceInfosArg): Promise< - ServiceInfoWithUnresolvedModuleConfigs[] -> => { - type ServiceConfigPromises = Promise<{ - serviceName: string; - serviceConfig: ServiceConfigReadonly; - }>; - - const services = Object.entries(fluenceConfig.services ?? []); - - const hasSomeServiceURL = services.some(([, { get }]) => { - return isUrl(get); - }); - - if (hasSomeServiceURL) { - startSpinner("Making sure all services are downloaded"); - } - - const serviceConfigs = await Promise.all( - services.map(async ([serviceName, { get }]): ServiceConfigPromises => { - return { - serviceName, - serviceConfig: - (await initReadonlyServiceConfig(get, projectRootDir)) ?? - commandObj.error( - `Service ${color.yellow(serviceName)} must have ${color.yellow( - SERVICE_CONFIG_FULL_FILE_NAME, - )}. ${ - isUrl(get) - ? `Not able to find it after downloading and decompressing ${color.yellow( - get, - )}` - : `Not able to find it at ${color.yellow(get)}` - }`, - ), - }; - }), - ); - - if (hasSomeServiceURL) { - stopSpinner(); - } - - return Promise.all( - serviceConfigs.map(({ serviceName, serviceConfig }) => { - return { - serviceName, - serviceDirPath: serviceConfig.$getDirPath(), - moduleNamesAndConfigsDefinedInService: - getModuleNamesAndConfigsDefinedInServices(serviceConfig.modules), - }; - }), - ); -}; - -type BuildArg = ResolveServiceInfosArg & { - marineCli: MarineCLI; - marineBuildArgs: string | undefined; -}; - -export async function build({ - marineCli, - marineBuildArgs, - ...resolveDeployInfosArg -}: BuildArg): Promise> { - const serviceInfos = await resolveServiceInfos(resolveDeployInfosArg); - - const setOfAllModuleUrlsOrAbsolutePaths = new Set( - serviceInfos.flatMap( - ({ - moduleNamesAndConfigsDefinedInService: modules, - serviceDirPath, - }): Array => { - return modules.map(({ moduleConfig: { get } }): string => { - return getUrlOrAbsolutePath(get, serviceDirPath); - }); - }, - ), - ); - - const mapOfModuleConfigs = new Map( - await Promise.all( - [...setOfAllModuleUrlsOrAbsolutePaths].map( - async ( - moduleAbsolutePathOrUrl, - ): Promise<[string, ModuleConfigReadonly]> => { - const maybeModuleConfig = await initReadonlyModuleConfig( - moduleAbsolutePathOrUrl, - ); - - if (maybeModuleConfig === null) { - return commandObj.error( - `Module at: ${color.yellow( - moduleAbsolutePathOrUrl, - )} doesn't have ${color.yellow(MODULE_CONFIG_FULL_FILE_NAME)}`, - ); - } - - return [moduleAbsolutePathOrUrl, maybeModuleConfig]; - }, - ), - ), - ); - - if (serviceInfos.length > 0) { - startSpinner("Making sure all services are built"); - - await buildModules( - [...mapOfModuleConfigs.values()], - marineCli, - marineBuildArgs, - ); - - stopSpinner(); - } - - const serviceNamePathToFacadeMap: Record = {}; - - const serviceInfoWithModuleConfigs = serviceInfos.map( - ({ - moduleNamesAndConfigsDefinedInService: modules, - serviceDirPath, - ...rest - }): ServiceInfo => { - const moduleConfigs = modules.map( - ({ - moduleConfig: { get, ...overrides }, - }): ModuleConfigReadonly & { wasmPath: string } => { - const moduleConfig = mapOfModuleConfigs.get( - getUrlOrAbsolutePath(get, serviceDirPath), - ); - - if (moduleConfig === undefined) { - throw new Error( - `Unreachable. Wasn't able to find module config for ${get}`, - ); - } - - const overriddenModuleConfig = { ...moduleConfig, ...overrides }; - - return { - ...overriddenModuleConfig, - wasmPath: getModuleWasmPath(overriddenModuleConfig), - }; - }, - ); - - const facadeModuleConfig = moduleConfigs.at(-1); - - assert( - facadeModuleConfig !== undefined, - "Unreachable. Each service must have at least one module", - ); - - serviceNamePathToFacadeMap[rest.serviceName] = - facadeModuleConfig.wasmPath; - - return { - moduleConfigs, - ...rest, - }; - }, - ); - - await updateAquaServiceInterfaceFile(serviceNamePathToFacadeMap, marineCli); - return serviceInfoWithModuleConfigs; -} - -async function resolveSingleServiceModuleConfigs( - serviceConfig: ServiceConfigReadonly, - overridesFromFluenceYAMLMap: OverrideModules | undefined, -) { - const { [FACADE_MODULE_NAME]: facadeModule, ...restModules } = - serviceConfig.modules; - - const serviceConfigDirPath = serviceConfig.$getDirPath(); - - const allModuleConfigs = await Promise.all([ - overrideModuleConfig( - FACADE_MODULE_NAME, - facadeModule, - overridesFromFluenceYAMLMap, - serviceConfigDirPath, - ), - ...Object.entries(restModules).map(async ([name, moduleConfig]) => { - return overrideModuleConfig( - name, - moduleConfig, - overridesFromFluenceYAMLMap, - serviceConfigDirPath, - ); - }), - ]); - - const [facadeModuleConfig, ...restModuleConfigs] = allModuleConfigs; - - return { - facadeModuleConfig, - allModuleConfigs: [...restModuleConfigs, facadeModuleConfig], - }; -} - -async function overrideModuleConfig( - name: string, - { get, ...overridesFromServiceYAML }: ServiceModuleV0, - overridesFromFluenceYAMLMap: OverrideModules | undefined, - serviceConfigDirPath: string, -) { - const overridesFromFluenceYAML = overridesFromFluenceYAMLMap?.[name]; - - const moduleConfig = await initReadonlyModuleConfig( - get, - serviceConfigDirPath, - ); - - if (moduleConfig === null) { - return commandObj.error(`Cant find module config at ${color.yellow(get)}`); - } - - return overrideModule( - moduleConfig, - overridesFromServiceYAML, - overridesFromFluenceYAML, - ); -} - -type ResolveSingleServiceModuleConfigsAndBuildArgs = { - serviceName: string; - serviceConfig: ServiceConfigReadonly; - marineCli: MarineCLI; - marineBuildArgs: string | undefined; -}; - -export async function resolveSingleServiceModuleConfigsAndBuild({ - serviceName, - serviceConfig, - marineCli, - marineBuildArgs, -}: ResolveSingleServiceModuleConfigsAndBuildArgs) { - const fluenceConfig = await initFluenceConfig(); - - const maybeOverridesFromFluenceCOnfig = - fluenceConfig?.services?.[serviceConfig.name]?.overrideModules; - - const { facadeModuleConfig, allModuleConfigs } = - await resolveSingleServiceModuleConfigs( - serviceConfig, - maybeOverridesFromFluenceCOnfig, - ); - - await buildModules(allModuleConfigs, marineCli, marineBuildArgs); - - const fluenceServiceConfigTomlPath = await genServiceConfigToml( - serviceName, - serviceConfig, - fluenceConfig?.services?.[serviceName], - allModuleConfigs, - ); - - return { facadeModuleConfig, fluenceServiceConfigTomlPath }; -} - -type GetModuleNamesAndConfigsDefinedInServicesArg = { - facade: ServiceModuleV0; -} & Record; - -const getModuleNamesAndConfigsDefinedInServices = ( - serviceConfigModules: GetModuleNamesAndConfigsDefinedInServicesArg, -): ModuleNameAndConfigDefinedInService[] => { - const { [FACADE_MODULE_NAME]: facadeModule, ...otherModules } = - serviceConfigModules; - - return [ - ...Object.entries(otherModules).map( - ([moduleName, moduleConfig]): ModuleNameAndConfigDefinedInService => { - return { - moduleConfig, - moduleName, - }; - }, - ), - { - moduleConfig: facadeModule, - moduleName: FACADE_MODULE_NAME, - }, - ]; -}; diff --git a/packages/cli/package/src/lib/buildModules.ts b/packages/cli/package/src/lib/buildModules.ts deleted file mode 100644 index 45566519c..000000000 --- a/packages/cli/package/src/lib/buildModules.ts +++ /dev/null @@ -1,299 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import { access, readFile, writeFile } from "node:fs/promises"; -import { relative } from "node:path"; - -import { color } from "@oclif/color"; -import type { JSONSchemaType } from "ajv"; - -import { type ModuleConfigReadonly } from "../lib/configs/project/module.js"; -import { MODULE_TYPE_RUST, DEFAULT_MARINE_BUILD_ARGS } from "../lib/const.js"; -import type { MarineCLI } from "../lib/marineCli.js"; - -import { ajv, validationErrorToString } from "./ajvInstance.js"; -import { commandObj } from "./commandObj.js"; -import { initFluenceConfig } from "./configs/project/fluence.js"; -import { FS_OPTIONS } from "./const.js"; -import { numToStr } from "./helpers/typesafeStringify.js"; -import { splitErrorsAndResults } from "./helpers/utils.js"; -import { projectRootDir, getCargoTomlPath } from "./paths.js"; - -type CargoWorkspaceToml = { - workspace?: { - members?: string[]; - dependencies?: Record; - }; -}; - -type Dependency = - | string - | { - version: string; - package?: string; - }; - -const dependencySchema = { - type: ["string", "object"], - oneOf: [ - { - type: "string", - }, - { - type: "object", - properties: { - version: { - type: "string", - }, - package: { - type: "string", - nullable: true, - }, - }, - required: [], - }, - ], -} as const satisfies JSONSchemaType; - -const cargoWorkspaceTomlSchema: JSONSchemaType = { - type: "object", - properties: { - workspace: { - type: "object", - properties: { - members: { - type: "array", - items: { - type: "string", - }, - nullable: true, - }, - dependencies: { - type: "object", - additionalProperties: dependencySchema, - properties: { - dependencyName: dependencySchema, - }, - required: [], - nullable: true, - }, - }, - required: [], - nullable: true, - }, - }, - required: [], -}; - -const validateCargoWorkspaceToml = ajv.compile(cargoWorkspaceTomlSchema); - -async function updateWorkspaceCargoToml( - moduleAbsolutePaths: string[], - moduleConfigs: ModuleConfigReadonly[], -): Promise { - const cargoTomlPath = getCargoTomlPath(); - let cargoTomlFileContent: string; - - try { - cargoTomlFileContent = await readFile(cargoTomlPath, FS_OPTIONS); - } catch { - cargoTomlFileContent = `[workspace] -members = [] -`; - } - - const { parse } = await import("@iarna/toml"); - const parsedConfig: unknown = parse(cargoTomlFileContent); - - if (!validateCargoWorkspaceToml(parsedConfig)) { - return commandObj.error( - `Cargo.toml at ${cargoTomlPath} is not valid. Please fix it manually. ${await validationErrorToString( - validateCargoWorkspaceToml.errors, - )}`, - ); - } - - const oldCargoWorkspaceMembers = parsedConfig.workspace?.members ?? []; - - const cargoWorkspaceMembersExistance = await Promise.allSettled( - oldCargoWorkspaceMembers.map((member) => { - return access(member); - }), - ); - - const existingCargoWorkspaceMembers = oldCargoWorkspaceMembers.filter( - (_, i) => { - return cargoWorkspaceMembersExistance[i]?.status === "fulfilled"; - }, - ); - - const packageVersionsMap = moduleConfigs.reduce>( - (acc, { rustBindingCrate }) => { - if (rustBindingCrate === undefined) { - return acc; - } - - acc[rustBindingCrate.name] = [ - ...(acc[rustBindingCrate.name] ?? []), - rustBindingCrate.version, - ]; - - return acc; - }, - {}, - ); - - const prevPackageVersionsMap = Object.entries( - parsedConfig.workspace?.dependencies ?? {}, - ).reduce>((acc, [name, strOrObj]) => { - const packageName = - typeof strOrObj === "string" ? name : (strOrObj.package ?? name); - - const version = typeof strOrObj === "string" ? strOrObj : strOrObj.version; - acc[packageName] = [...(acc[packageName] ?? []), version]; - return acc; - }, {}); - - const [packagesWithDifferentVersions, packagesWithSingleVersion] = - splitErrorsAndResults( - Object.entries(packageVersionsMap) - .map(([name, versions]) => { - return { - name, - versions: [...new Set(versions)].filter((v) => { - const prevPackageVersions = prevPackageVersionsMap[name]; - - return ( - prevPackageVersions === undefined || - !prevPackageVersions.includes(v) - ); - }), - }; - }) - .filter( - ( - packageAndVersions, - ): packageAndVersions is { - name: string; - versions: [string, ...string[]]; - } => { - return packageAndVersions.versions.length >= 1; - }, - ), - ({ name, versions }) => { - const [version, ...restVersions] = versions; - - if (restVersions.length === 0) { - return { result: { name, version } }; - } - - return { error: { name, versions } }; - }, - ); - - if (packagesWithDifferentVersions.length > 0) { - commandObj.logToStderr( - `\n${color.yellow( - "WARNING:", - )} The following modules require different versions of rustBindingCrates. They will not be automatically added to the workspace at ${color.yellow( - cargoTomlPath, - )}. But you can add them manually. For example like this:\n\n${color.yellow( - packagesWithDifferentVersions - .map(({ name, versions }) => { - return versions - .map((version, i) => { - return `[workspace.dependencies.${name}_${numToStr(i)}]\npackage = "${name}"\nversion = "${version}"`; - }) - .join("\n"); - }) - .join("\n\n"), - )}\n`, - ); - } - - const members = [ - ...new Set([ - ...existingCargoWorkspaceMembers, - ...moduleAbsolutePaths.map((moduleAbsolutePath) => { - return relative(projectRootDir, moduleAbsolutePath); - }), - ]), - ]; - - const dependencies = { - ...parsedConfig.workspace?.dependencies, - ...Object.fromEntries( - packagesWithSingleVersion.map(({ name, version }) => { - return [name, { version }] as const; - }), - ), - }; - - const workspace = { - ...(parsedConfig.workspace ?? {}), - ...(members.length > 0 ? { members } : {}), - ...(Object.keys(dependencies).length > 0 ? { dependencies } : {}), - }; - - const newConfig: CargoWorkspaceToml = { - ...parsedConfig, - ...(Object.entries(workspace).length > 0 ? { workspace } : {}), - }; - - if (Object.entries(newConfig).length === 0) { - return; - } - - const stringifyToTOML = (await import("@iarna/toml/stringify.js")).default; - await writeFile(cargoTomlPath, stringifyToTOML(newConfig), FS_OPTIONS); -} - -export async function buildModules( - modulesConfigs: ModuleConfigReadonly[], - marineCli: MarineCLI, - marineBuildArgs: string | undefined, -): Promise { - const rustModuleConfigs = modulesConfigs.filter(({ type }) => { - return type === MODULE_TYPE_RUST; - }); - - await updateWorkspaceCargoToml( - rustModuleConfigs.map((moduleConfig) => { - return moduleConfig.$getDirPath(); - }), - modulesConfigs, - ); - - if (rustModuleConfigs.length === 0) { - return; - } - - const pFlagsForEachModule = rustModuleConfigs.flatMap(({ name }) => { - return ["-p", name]; - }); - - const marineBuildArgsToUse = ( - marineBuildArgs ?? - (await initFluenceConfig())?.marineBuildArgs ?? - DEFAULT_MARINE_BUILD_ARGS - ).split(" "); - - await marineCli({ - args: ["build", ...pFlagsForEachModule, ...marineBuildArgsToUse], - cwd: projectRootDir, - }); -} diff --git a/packages/cli/package/src/lib/chain/chainConfig.ts b/packages/cli/package/src/lib/chain/chainConfig.ts index d38b3d66b..d6341f717 100644 --- a/packages/cli/package/src/lib/chain/chainConfig.ts +++ b/packages/cli/package/src/lib/chain/chainConfig.ts @@ -17,6 +17,7 @@ import capitalize from "lodash-es/capitalize.js"; +import type { ChainENV } from "../../common.js"; import { commandObj } from "../commandObj.js"; import { initEnvConfig } from "../configs/project/env/env.js"; import { dbg } from "../dbg.js"; @@ -75,6 +76,38 @@ export async function getRpcUrl() { return rpcUrlPromise; } +let ipfsGatewayPromise: Promise | undefined; + +const IPFS_GATEWAYS: Record = { + local: "https://ipfs-gateway.fluence.dev", + testnet: "https://ipfs-gateway.fluence.dev", + mainnet: "https://ipfs-gateway.fluence.dev", + stage: "https://ipfs-gateway.fluence.dev", +}; + +export async function getIpfsGateway() { + if (ipfsGatewayPromise === undefined) { + ipfsGatewayPromise = (async () => { + const chainEnv = await ensureChainEnv(); + let ipfsGateway = IPFS_GATEWAYS[chainEnv]; + const envConfig = await initEnvConfig(); + + if (envConfig?.ipfsGateway !== undefined) { + commandObj.logToStderr( + `Using custom IPFS Gateway: ${envConfig.ipfsGateway} from ${envConfig.$getPath()}`, + ); + + ipfsGateway = envConfig.ipfsGateway; + } + + dbg(`ipfsGateway: ${ipfsGateway}`); + return ipfsGateway; + })(); + } + + return ipfsGatewayPromise; +} + let blockScoutUrlPromise: | Promise< | { diff --git a/packages/cli/package/src/lib/chain/chainValidators.ts b/packages/cli/package/src/lib/chain/chainValidators.ts index be6f325a9..e1311366a 100644 --- a/packages/cli/package/src/lib/chain/chainValidators.ts +++ b/packages/cli/package/src/lib/chain/chainValidators.ts @@ -25,7 +25,7 @@ import { stringifyUnknown } from "../helpers/stringifyUnknown.js"; import { bigintToStr, numToStr } from "../helpers/typesafeStringify.js"; import type { ValidationResult } from "../helpers/validations.js"; -export async function getMinCCDuration(): Promise { +async function getMinCCDuration(): Promise { let minDuration: bigint = 0n; if ((await ensureChainEnv()) !== "local") { diff --git a/packages/cli/package/src/lib/chain/commitment.ts b/packages/cli/package/src/lib/chain/commitment.ts index b2fb3908b..c16d8d3ce 100644 --- a/packages/cli/package/src/lib/chain/commitment.ts +++ b/packages/cli/package/src/lib/chain/commitment.ts @@ -26,8 +26,8 @@ import { commandObj } from "../commandObj.js"; import { initProviderConfig } from "../configs/project/provider/provider.js"; import { CLI_NAME, - NOX_NAMES_FLAG_NAME, - OFFER_FLAG_NAME, + PEER_NAMES_FLAG_NAME, + type PeerAndOfferNameFlags, CC_IDS_FLAG_NAME, FINISH_COMMITMENT_FLAG_NAME, } from "../const.js"; @@ -65,10 +65,6 @@ import { fltFormatWithSymbol } from "./currencies.js"; const HUNDRED_PERCENT = 100; -export type ComputePeersWithCC = Awaited< - ReturnType ->; - async function getComputePeersWithCCIds( computePeers: ResolvedComputePeer[], ): Promise<[Required, ...Required[]]> { @@ -141,7 +137,7 @@ async function getComputePeersWithCCIds( commandObj.warn( `Some of the commitments were not found for:\n${computePeersWithoutCC .map(({ name, peerId }) => { - return `Nox: ${name}, PeerId: ${peerId}`; + return `Peer: ${name}, PeerId: ${peerId}`; }) .join( "\n", @@ -219,13 +215,11 @@ async function getCCs( return [firstCCInfo, ...restCCInfos]; } -export type CCFlags = { - [NOX_NAMES_FLAG_NAME]?: string | undefined; - [OFFER_FLAG_NAME]?: string | undefined; +export type CCFlags = PeerAndOfferNameFlags & { [CC_IDS_FLAG_NAME]?: string | undefined; }; -export async function getCommitmentsIds( +async function getCommitmentsIds( flags: CCFlags, ): Promise<[CapacityCommitment, ...CapacityCommitment[]]> { if (flags[CC_IDS_FLAG_NAME] !== undefined) { @@ -233,7 +227,7 @@ export async function getCommitmentsIds( } if ( - flags[NOX_NAMES_FLAG_NAME] === undefined && + flags[PEER_NAMES_FLAG_NAME] === undefined && (await initProviderConfig()) === null ) { return getCCs( @@ -252,10 +246,7 @@ export async function getCommitmentsIds( return getComputePeersWithCCIds(await resolveComputePeersByNames(flags)); } -export async function createCommitments(flags: { - [NOX_NAMES_FLAG_NAME]?: string | undefined; - [OFFER_FLAG_NAME]?: string | undefined; -}) { +export async function createCommitments(flags: PeerAndOfferNameFlags) { const computePeers = await resolveComputePeersByNames(flags); const { contracts } = await getContracts(); const precision = await contracts.diamond.precision(); @@ -346,9 +337,9 @@ export async function createCommitments(flags: { try { createCommitmentsTxReceipts = await signBatch( - `Create commitments for the following noxes:\n\n${computePeers + `Create commitments for the following peers:\n\n${computePeers .map(({ name, peerId }) => { - return `Nox: ${name}\nPeerId: ${peerId}`; + return `Peer: ${name}\nPeerId: ${peerId}`; }) .join("\n\n")}`, [firstCommitmentTx, ...restCommitmentTxs], @@ -404,7 +395,7 @@ export async function createCommitments(flags: { commandObj.logToStderr( stringifyDetailedCommitmentsInfo( await getDetailedCommitmentsInfoGroupedByStatus({ - "nox-names": computePeers + [PEER_NAMES_FLAG_NAME]: computePeers .map(({ name }) => { return name; }) @@ -502,7 +493,7 @@ export async function collateralWithdraw( for (const commitment of commitments.flatMap(({ ccInfos }) => { return ccInfos; })) { - const { ccId, name: noxName } = commitment; + const { ccId, name: peerName } = commitment; const [unitIds, isExitedStatuses] = await contracts.diamond.getUnitExitStatuses(ccId); @@ -628,7 +619,7 @@ export async function collateralWithdraw( ); await signBatch( - `${firstNotExitedUnit === undefined ? "F" : "Remove compute units from capacity commitments and f"}inish commitment ${noxName === undefined ? ccId : `for ${noxName} (${ccId})`} ${ccId}`, + `${firstNotExitedUnit === undefined ? "Finish" : "Remove compute units from capacity commitments and finish"} commitment ${peerName === undefined ? ccId : `for ${peerName} (${ccId})`} ${ccId}`, firstNotExitedUnit === undefined ? [populateTx(contracts.diamond.finishCommitment, ccId)] : [ @@ -671,8 +662,8 @@ export function stringifyBasicCommitmentInfo({ peerId, ccId, }: CapacityCommitment) { - const noxName = name === undefined ? "" : `Nox: ${name}\n`; - return `${color.yellow(`${noxName}PeerId: ${peerId}`)}\nCommitmentId: ${ccId}`; + const peerName = name === undefined ? "" : `Peer: ${name}\n`; + return `${color.yellow(`${peerName}PeerId: ${peerId}`)}\nCommitmentId: ${ccId}`; } type CapacityCommitment = { @@ -993,10 +984,10 @@ export function stringifyDetailedCommitmentsInfo( return detailedCommitmentsInfoGroupedByStatus .map(({ statusInfo, CCs }) => { return `${getStatusHeading(statusInfo)}${CCs.map((cc) => { - const noxNameString = - "noxName" in cc ? color.yellow(`Nox: ${cc.noxName}\n`) : ""; + const peerNameString = + "peerName" in cc ? color.yellow(`Peer: ${cc.peerName}\n`) : ""; - return `${noxNameString}${getDetailedCommitmentInfoString(cc)}`; + return `${peerNameString}${getDetailedCommitmentInfoString(cc)}`; }).join("\n\n")}`; }) .join("\n\n"); @@ -1018,7 +1009,7 @@ async function getDetailedCommitmentInfo({ status, peerId, ccId, - name: noxName, + name: peerName, currentEpoch, epochDuration, initTimestamp, @@ -1083,7 +1074,7 @@ async function getDetailedCommitmentInfo({ : undefined; return { - ...(noxName === undefined ? {} : { noxName }), + ...(peerName === undefined ? {} : { peerName }), peerId, commitmentId: ccId, status, diff --git a/packages/cli/package/src/lib/chain/conversions.ts b/packages/cli/package/src/lib/chain/conversions.ts index 0b3b70f0f..126d896e6 100644 --- a/packages/cli/package/src/lib/chain/conversions.ts +++ b/packages/cli/package/src/lib/chain/conversions.ts @@ -17,6 +17,8 @@ import type { CIDV1Struct } from "@fluencelabs/deal-ts-clients/dist/typechain-types/Config.js"; +import { bufferToBase64 } from "../helpers/typesafeStringify.js"; + const PREFIX = new Uint8Array([0, 36, 8, 1, 18, 32]); const BASE_58_PREFIX = "z"; @@ -64,3 +66,11 @@ export async function cidHexStringToBase32(cidHex: string): Promise { const { base32 } = await import("multiformats/bases/base32"); return base32.encode(new Uint8Array(Buffer.from(cidHex, "hex"))); } + +export function hexStringToUTF8ToBase64String(hexString: string): string { + const cleanHexString = hexString.startsWith("0x") + ? hexString.slice(2) + : hexString; + + return bufferToBase64(Buffer.from(cleanHexString, "utf-8")); +} diff --git a/packages/cli/package/src/lib/chain/currencies.ts b/packages/cli/package/src/lib/chain/currencies.ts index e15568a44..cd6ec64ae 100644 --- a/packages/cli/package/src/lib/chain/currencies.ts +++ b/packages/cli/package/src/lib/chain/currencies.ts @@ -25,7 +25,7 @@ export async function fltParse(value: string): Promise { return parseEther(value); } -export async function fltFormat(value: bigint): Promise { +async function fltFormat(value: bigint): Promise { const { formatEther } = await import("ethers"); return formatEther(value); } @@ -50,7 +50,7 @@ export async function ptFormatWithSymbol(value: bigint): Promise { let ptDecimalsPromise: Promise | undefined; -export async function getPtDecimals() { +async function getPtDecimals() { if (ptDecimalsPromise === undefined) { ptDecimalsPromise = (async () => { const { id } = await import("ethers"); diff --git a/packages/cli/package/src/lib/chain/deals.ts b/packages/cli/package/src/lib/chain/deals.ts deleted file mode 100644 index 7d6dadf84..000000000 --- a/packages/cli/package/src/lib/chain/deals.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import { getSignerAddress } from "../dealClient.js"; -import { getDealIdsByProviderId } from "../gql/gql.js"; - -export async function getProviderDeals() { - return (await getDealIdsByProviderId(await getSignerAddress())).map( - ({ id }) => { - return id; - }, - ); -} diff --git a/packages/cli/package/src/lib/chain/depositToDeal.ts b/packages/cli/package/src/lib/chain/depositToDeal.ts deleted file mode 100644 index 3912b9c81..000000000 --- a/packages/cli/package/src/lib/chain/depositToDeal.ts +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import { color } from "@oclif/color"; - -import { commandObj } from "../commandObj.js"; -import { getDeals } from "../deal.js"; -import { getContracts, sign } from "../dealClient.js"; - -import { ptFormatWithSymbol, ptParse } from "./currencies.js"; - -export async function depositToDeal( - flagsAndArgs: { - args: { - "DEPLOYMENT-NAMES": string | undefined; - }; - flags: { - "deal-ids": string | undefined; - }; - }, - amount: string, -) { - const parsedAmount = await ptParse(amount); - const deals = await getDeals(flagsAndArgs); - const { contracts } = await getContracts(); - - for (const { dealName, dealId } of deals) { - const deal = contracts.getDeal(dealId); - const tokensString = await ptFormatWithSymbol(parsedAmount); - - await sign({ - title: `Approve ${tokensString} tokens to be deposited to the deal ${dealName}`, - method: contracts.usdc.approve, - args: [await deal.getAddress(), parsedAmount], - }); - - await sign({ - title: `Deposit ${tokensString} to the deal ${dealName} (${dealId})`, - method: deal.deposit, - args: [parsedAmount], - }); - - commandObj.log( - `${color.yellow( - tokensString, - )} tokens were deposited to the deal ${color.yellow(dealName)}`, - ); - } -} diff --git a/packages/cli/package/src/lib/chain/distributeToNox.ts b/packages/cli/package/src/lib/chain/distributeToNox.ts index 4b353577e..63494fc9c 100644 --- a/packages/cli/package/src/lib/chain/distributeToNox.ts +++ b/packages/cli/package/src/lib/chain/distributeToNox.ts @@ -21,10 +21,9 @@ import { color } from "@oclif/color"; import { commandObj } from "../commandObj.js"; import { - NOX_NAMES_FLAG_NAME, FLT_SYMBOL, - OFFER_FLAG_NAME, MAX_TOKEN_AMOUNT_KEYWORD, + type PeerAndOfferNameFlags, } from "../const.js"; import { getWallet, @@ -36,17 +35,15 @@ import { resolveComputePeersByNames } from "../resolveComputePeersByNames.js"; import { fltFormatWithSymbol, fltParse } from "./currencies.js"; -export async function distributeToNox(flags: { - amount?: string | undefined; - [NOX_NAMES_FLAG_NAME]?: string | undefined; - [OFFER_FLAG_NAME]?: string | undefined; -}) { +export async function distributeToPeer( + flags: { amount?: string | undefined } & PeerAndOfferNameFlags, +) { const computePeers = await resolveComputePeersByNames(flags); const amount = flags.amount ?? (await input({ - message: `Enter the amount of ${FLT_SYMBOL} tokens to distribute to noxes`, + message: `Enter the amount of ${FLT_SYMBOL} tokens to distribute to peers`, })); const parsedAmount = await fltParse(amount); @@ -69,16 +66,17 @@ export async function distributeToNox(flags: { } } -export async function withdrawFromNox(flags: { - amount?: string | undefined; - [NOX_NAMES_FLAG_NAME]?: string | undefined; -}) { +export async function withdrawFromPeer( + flags: { + amount?: string | undefined; + } & PeerAndOfferNameFlags, +) { const computePeers = await resolveComputePeersByNames(flags); const amount = flags.amount ?? (await input({ - message: `Enter the amount of ${FLT_SYMBOL} tokens to distribute to noxes. Use ${color.yellow( + message: `Enter the amount of ${FLT_SYMBOL} tokens to distribute to peers. Use ${color.yellow( MAX_TOKEN_AMOUNT_KEYWORD, )} to withdraw maximum possible amount`, async validate(val: string) { @@ -102,13 +100,16 @@ export async function withdrawFromNox(flags: { }, })); - for (const { walletKey: noxWalletKey, name: noxName } of computePeers) { - const { amountBigInt, txReceipt, noxAddress } = + for (const { walletKey: peerWalletKey, name: peerName } of computePeers) { + const { amountBigInt, txReceipt, peerAddress } = amount === MAX_TOKEN_AMOUNT_KEYWORD - ? await withdrawMaxAmount({ noxWalletKey, noxName }) + ? await withdrawMaxAmount({ + peerWalletKey, + peerName, + }) : await withdrawSpecificAmount({ - noxWalletKey, - noxName, + peerWalletKey, + peerName, amountBigInt: await fltParse(amount), }); @@ -119,33 +120,33 @@ export async function withdrawFromNox(flags: { `Successfully withdrawn ${color.yellow( await fltFormatWithSymbol(amountBigInt), )} from ${color.yellow( - noxName, - )} (${noxAddress}) to ${color.yellow(providerAddress)} with tx hash: ${color.yellow(txReceipt.hash)}\n`, + peerName, + )} (${peerAddress}) to ${color.yellow(providerAddress)} with tx hash: ${color.yellow(txReceipt.hash)}\n`, ); } } } type WithdrawMaxAmountArgs = { - noxWalletKey: string; - noxName: string; + peerWalletKey: string; + peerName: string; }; async function withdrawMaxAmount({ - noxWalletKey, - noxName, + peerWalletKey, + peerName, }: WithdrawMaxAmountArgs) { - const noxWallet = await getWallet(noxWalletKey); - const noxAddress = await getSignerAddress(); + const peerWallet = await getWallet(peerWalletKey); + const peerAddress = await getSignerAddress(); - const gasLimit = await noxWallet.estimateGas({ - to: noxAddress, + const gasLimit = await peerWallet.estimateGas({ + to: peerAddress, value: 0n, }); - const noxProvider = noxWallet.provider; - assert(noxProvider !== null, "Unreachable. We ensure provider is not null"); - const gasPrice = await noxProvider.getFeeData(); + const peerProvider = peerWallet.provider; + assert(peerProvider !== null, "Unreachable. We ensure provider is not null"); + const gasPrice = await peerProvider.getFeeData(); if ( gasPrice.maxFeePerGas === null || @@ -159,18 +160,18 @@ async function withdrawMaxAmount({ const feeAmount = (gasPrice.maxFeePerGas + gasPrice.maxPriorityFeePerGas) * gasLimit; - const totalBalance = await noxProvider.getBalance(noxAddress); + const totalBalance = await peerProvider.getBalance(peerAddress); const amountBigInt = totalBalance - feeAmount; if (amountBigInt <= 0n) { commandObj.logToStderr( - `No ${FLT_SYMBOL} tokens to withdraw from ${noxName} (${noxAddress})`, + `No ${FLT_SYMBOL} tokens to withdraw from ${peerName} (${peerAddress})`, ); return { txReceipt: undefined, amountBigInt: undefined, - noxAddress: undefined, + peerAddress: undefined, }; } @@ -178,7 +179,7 @@ async function withdrawMaxAmount({ const result = { txReceipt: await sendRawTransaction( - `Withdraw max amount of ${await fltFormatWithSymbol(amountBigInt)} from ${noxName} (${noxAddress}) to ${providerAddress}`, + `Withdraw max amount of ${await fltFormatWithSymbol(amountBigInt)} from ${peerName} (${peerAddress}) to ${providerAddress}`, { to: providerAddress, value: amountBigInt, @@ -186,29 +187,29 @@ async function withdrawMaxAmount({ maxFeePerGas: gasPrice.maxFeePerGas, maxPriorityFeePerGas: gasPrice.maxPriorityFeePerGas, }, - noxWallet, + peerWallet, ), amountBigInt, - noxAddress, + peerAddress, }; return result; } async function withdrawSpecificAmount({ - noxWalletKey, - noxName, + peerWalletKey, + peerName, amountBigInt, }: WithdrawMaxAmountArgs & { amountBigInt: bigint }) { const providerAddress = await getSignerAddress(); - const { address: noxAddress } = await getWallet(noxWalletKey); + const { address: peerAddress } = await getWallet(peerWalletKey); return { txReceipt: await sendRawTransaction( - `Withdraw ${await fltFormatWithSymbol(amountBigInt)} from ${noxName} (${noxAddress}) to ${providerAddress}`, + `Withdraw ${await fltFormatWithSymbol(amountBigInt)} from ${peerName} (${peerAddress}) to ${providerAddress}`, { to: providerAddress, value: amountBigInt }, ), amountBigInt, - noxAddress, + peerAddress, }; } diff --git a/packages/cli/package/src/lib/chain/printDealInfo.ts b/packages/cli/package/src/lib/chain/printDealInfo.ts deleted file mode 100644 index 01d2bf176..000000000 --- a/packages/cli/package/src/lib/chain/printDealInfo.ts +++ /dev/null @@ -1,84 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import { color } from "@oclif/color"; - -import { commandObj } from "../commandObj.js"; -import { type DealNameAndId } from "../deal.js"; -import { getReadonlyContracts } from "../dealClient.js"; -import { bigintToStr } from "../helpers/typesafeStringify.js"; - -import { getBlockScoutUrl } from "./chainConfig.js"; -import { peerIdHexStringToBase58String } from "./conversions.js"; -import { ptFormatWithSymbol } from "./currencies.js"; - -export async function printDealInfo({ dealId, dealName }: DealNameAndId) { - const { readonlyContracts } = await getReadonlyContracts(); - const deal = readonlyContracts.getDeal(dealId); - commandObj.log(`\n${color.yellow(dealName)} info:`); - const status = await deal.getStatus(); - const { DealStatus } = await import("@fluencelabs/deal-ts-clients"); - const blockScoutUrl = await getBlockScoutUrl(); - - if ("blockExplorers" in blockScoutUrl) { - commandObj.log( - `Deal: ${blockScoutUrl.blockExplorers.default.url}address/${dealId}`, - ); - } - - commandObj.log(`DealID: "${dealId}"`); - commandObj.log(`Status: ${DealStatus[Number(status)] ?? "Unknown"}`); - - commandObj.log( - `Balance: ${await ptFormatWithSymbol(await deal.getFreeBalance())}`, - ); - - commandObj.log( - `Price per compute unit per epoch: ${await ptFormatWithSymbol( - await deal.pricePerCuPerEpoch(), - )}`, - ); - - commandObj.log(`Payment token: ${await deal.paymentToken()}`); - commandObj.log(`Min workers: ${bigintToStr(await deal.minWorkers())}`); - commandObj.log(`Target workers: ${bigintToStr(await deal.targetWorkers())}`); - - const cuCountPerWorker = await deal.cuCountPerWorker(); - - commandObj.log( - `Compute unit count per worker: ${bigintToStr(cuCountPerWorker)}`, - ); - - const workers = await deal.getWorkers(); - - if (workers.length === 0) { - commandObj.log(`No workers`); - } else { - for (const worker of workers) { - commandObj.log(`\nOff-chain id: ${worker.offchainId}`); - commandObj.log(`On-chain id: ${worker.onchainId}`); - - commandObj.log( - `Peer Id base58: ${await peerIdHexStringToBase58String(worker.peerId)}`, - ); - - commandObj.log(`Joined epoch: ${bigintToStr(worker.joinedEpoch)}`); - commandObj.log(`Compute units: ${worker.computeUnitIds.join("\n")}`); - commandObj.log(`Provider: ${worker.provider}`); - } - } -} diff --git a/packages/cli/package/src/lib/chain/providerInfo.ts b/packages/cli/package/src/lib/chain/providerInfo.ts index b5bd4c0fc..554f7b424 100644 --- a/packages/cli/package/src/lib/chain/providerInfo.ts +++ b/packages/cli/package/src/lib/chain/providerInfo.ts @@ -107,7 +107,7 @@ Provider address: ${signerAddress} `); } -export async function getProviderInfoByAddress(address: string) { +async function getProviderInfoByAddress(address: string) { const { contracts } = await getContracts(); const { name } = await contracts.diamond.getProviderInfo(address); return { name: name === "" ? null : name, address }; diff --git a/packages/cli/package/src/lib/chainFlags.ts b/packages/cli/package/src/lib/chainFlags.ts index 7475c0f7f..90e306be3 100644 --- a/packages/cli/package/src/lib/chainFlags.ts +++ b/packages/cli/package/src/lib/chainFlags.ts @@ -18,7 +18,7 @@ import { commandObj } from "./commandObj.js"; import { ENV_FLAG_NAME, PRIV_KEY_FLAG_NAME } from "./const.js"; -export type ChainFlags = { +type ChainFlags = { [ENV_FLAG_NAME]?: string | undefined; [PRIV_KEY_FLAG_NAME]?: string | undefined; }; diff --git a/packages/cli/package/src/lib/compileAquaAndWatch.ts b/packages/cli/package/src/lib/compileAquaAndWatch.ts deleted file mode 100644 index ae15c69d4..000000000 --- a/packages/cli/package/src/lib/compileAquaAndWatch.ts +++ /dev/null @@ -1,206 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import { resolve } from "node:path"; - -import type { GatherImportsResult } from "@fluencelabs/npm-aqua-compiler"; -import { color } from "@oclif/color"; - -import { - resolveAquaConfig, - compileToFiles, - type CompileToFilesArgs, -} from "./aqua.js"; -import { commandObj } from "./commandObj.js"; -import { - type FluenceConfigReadonly, - type CompileAqua, - initFluenceConfig, -} from "./configs/project/fluence.js"; -import { COMPILE_AQUA_PROPERTY_NAME } from "./const.js"; -import { getAquaImports } from "./helpers/aquaImports.js"; -import { stringifyUnknown } from "./helpers/stringifyUnknown.js"; -import { commaSepStrToArr, splitErrorsAndResults } from "./helpers/utils.js"; -import { projectRootDir } from "./paths.js"; -import type { Required } from "./typeHelpers.js"; - -type CompileAquaFromFluenceConfigArgs = { - imports: GatherImportsResult; - names?: string | undefined; - dry?: boolean | undefined; - watch?: boolean | undefined; -}; - -export async function compileAquaFromFluenceConfig({ - names: namesFlag, - imports, - watch = false, - dry = false, -}: CompileAquaFromFluenceConfigArgs) { - const fluenceConfig = await initFluenceConfig(); - - if (!hasAquaToCompile(fluenceConfig)) { - return; - } - - const names = - namesFlag === undefined - ? Object.keys(fluenceConfig[COMPILE_AQUA_PROPERTY_NAME]) - : commaSepStrToArr(namesFlag); - - const [invalidNames, validNames] = splitErrorsAndResults(names, (name) => { - if (name in fluenceConfig[COMPILE_AQUA_PROPERTY_NAME]) { - return { - result: name, - }; - } - - return { - error: name, - }; - }); - - if (invalidNames.length > 0) { - commandObj.error( - `${color.yellow( - invalidNames - .map((name) => { - return color.yellow(name); - }) - .join(", "), - )} are missing from '${COMPILE_AQUA_PROPERTY_NAME}' property of ${fluenceConfig.$getPath()}`, - ); - } - - const results = await Promise.allSettled( - Object.entries(fluenceConfig[COMPILE_AQUA_PROPERTY_NAME]) - .filter(([name]) => { - return validNames.includes(name); - }) - .map(async ([name, aquaConfig]) => { - return compileAquaAndWatch( - { - ...resolveAquaConfig(aquaConfig, imports), - outputPathAbsolute: resolve(projectRootDir, aquaConfig.output), - watch, - dry, - }, - name, - ); - }), - ); - - if (watch) { - return; - } - - const compilationErrors = results.filter( - (res): res is { status: "rejected"; reason: unknown } => { - return res.status === "rejected"; - }, - ); - - if (compilationErrors.length > 0) { - commandObj.error( - compilationErrors - .map((error) => { - return stringifyUnknown(error.reason); - }) - .join("\n"), - ); - } -} - -export async function compileAquaFromFluenceConfigWithDefaults( - aquaImportsFromFlags?: string[], -) { - await compileAquaFromFluenceConfig({ - imports: await getAquaImports(aquaImportsFromFlags), - }); -} - -export function hasAquaToCompile( - maybeFluenceConfig: FluenceConfigReadonly | null, -): maybeFluenceConfig is FluenceConfigReadonly & { - [COMPILE_AQUA_PROPERTY_NAME]: CompileAqua; -} { - return ( - maybeFluenceConfig !== null && - COMPILE_AQUA_PROPERTY_NAME in maybeFluenceConfig && - Object.keys(maybeFluenceConfig[COMPILE_AQUA_PROPERTY_NAME]).length !== 0 - ); -} - -export async function compileAquaAndWatch( - compileArgs: Required & { - watch: boolean; - }, - name?: string, -) { - function compileSuccessLog() { - const aquaConfigName = name === undefined ? "" : ` ${color.green(name)}`; - - const from = ` from ${color.yellow(compileArgs.filePath)}`; - - const to = - compileArgs.outputPathAbsolute === undefined || compileArgs.dry - ? "" - : ` to ${color.yellow(compileArgs.outputPathAbsolute)}`; - - commandObj.logToStderr( - `Successfully compiled${aquaConfigName}${from}${to}. If you don't see files or functions you expect to see, make sure you exported things you require from your aqua files`, - ); - } - - if (!compileArgs.watch) { - await compileToFiles(compileArgs); - compileSuccessLog(); - return; - } - - function watchingNotification() { - commandObj.logToStderr( - `Watching for changes at ${color.yellow(compileArgs.filePath)}...`, - ); - } - - async function compileWithWatchLog() { - try { - await compileToFiles(compileArgs); - compileSuccessLog(); - watchingNotification(); - } catch (error) { - commandObj.logToStderr(stringifyUnknown(error)); - watchingNotification(); - } - } - - await compileWithWatchLog(); - const chokidar = await import("chokidar"); - - chokidar - .watch(compileArgs.filePath, { - followSymlinks: false, - usePolling: false, - interval: 100, - binaryInterval: 300, - ignoreInitial: true, - }) - .on("all", (): void => { - void compileWithWatchLog(); - }); -} diff --git a/packages/cli/package/src/lib/configs/initConfig.ts b/packages/cli/package/src/lib/configs/initConfig.ts deleted file mode 100644 index 2a38a1e62..000000000 --- a/packages/cli/package/src/lib/configs/initConfig.ts +++ /dev/null @@ -1,655 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import assert from "node:assert"; -import { mkdir, readFile, writeFile } from "node:fs/promises"; -import { dirname, join, relative } from "node:path"; - -import { color } from "@oclif/color"; -import type { AnySchema, JSONSchemaType, ValidateFunction } from "ajv"; -import isInteger from "lodash-es/isInteger.js"; - -import { jsonStringify } from "../../common.js"; -import { validationErrorToString, getAjv } from "../ajvInstance.js"; -import { commandObj } from "../commandObj.js"; -import { - FS_OPTIONS, - SCHEMAS_DIR_NAME, - YAML_EXT, - YML_EXT, - DOCKER_COMPOSE_FILE_NAME, - CLI_NAME_FULL, -} from "../const.js"; -import { removeProperties } from "../helpers/utils.js"; -import type { ValidationResult } from "../helpers/validations.js"; -import type { Mutable } from "../typeHelpers.js"; - -type EnsureSchemaArg = { - name: string; - configDirPath: string; - getSchemaDirPath: GetPath | undefined; - schema: AnySchema; -}; - -const ensureSchema = async ({ - name, - configDirPath, - getSchemaDirPath, - schema, -}: EnsureSchemaArg): Promise => { - const schemaDir = join( - getSchemaDirPath === undefined ? configDirPath : await getSchemaDirPath(), - SCHEMAS_DIR_NAME, - ); - - await mkdir(schemaDir, { recursive: true }); - const schemaPath = join(schemaDir, `${name}.json`); - const correctSchemaContent = jsonStringify(schema) + "\n"; - - try { - const schemaContent = await readFile(schemaPath, FS_OPTIONS); - assert(schemaContent === correctSchemaContent); - } catch { - await writeFile(schemaPath, correctSchemaContent, FS_OPTIONS); - } - - return relative(configDirPath, schemaPath); -}; - -type MigrateConfigOptions< - Config extends BaseConfig, - LatestConfig extends BaseConfig, -> = { - configString: string; - migrations: Migrations; - configPath: string; - validateLatestConfig: ValidateFunction; - config: Config; - validate: undefined | ConfigValidateFunction; - latestConfigVersion: string | number; -}; - -const migrateConfig = async < - Config extends BaseConfig, - LatestConfig extends BaseConfig, ->({ - configString, - migrations, - configPath, - validateLatestConfig, - config, - validate, -}: MigrateConfigOptions): Promise<{ - latestConfig: LatestConfig; - configString: string; -}> => { - let migratedConfig = config; - - for (const migration of migrations.slice(Number(config.version))) { - migratedConfig = await migration(migratedConfig); - } - - const [{ parse }, { yamlDiffPatch }] = await Promise.all([ - import("yaml"), - import("yaml-diff-patch"), - ]); - - const migratedConfigString = yamlDiffPatch( - configString, - parse(configString), - migratedConfig, - ); - - const latestConfig: unknown = parse(migratedConfigString); - - if (!validateLatestConfig(latestConfig)) { - return commandObj.error( - `Couldn't migrate config ${color.yellow( - configPath, - )}. ${await validationErrorToString(validateLatestConfig.errors)}`, - ); - } - - const maybeValidationError = - validate !== undefined && (await validate(latestConfig, configPath)); - - if (typeof maybeValidationError === "string") { - return commandObj.error( - `Invalid config ${color.yellow( - configPath, - )} after successful migration. Config after migration looks like this:\n\n${migratedConfigString}\n\nErrors: ${maybeValidationError}`, - ); - } - - if (configString !== migratedConfigString) { - await saveConfig(configPath, migratedConfigString); - } - - return { - latestConfig, - configString: migratedConfigString, - }; -}; - -type EnsureConfigOptions< - Config extends BaseConfig, - LatestConfig extends BaseConfig, -> = { - configPath: string; - validateLatestConfig: ValidateFunction; - config: Config; - validate: undefined | ConfigValidateFunction; - latestConfigVersion: string | number; -}; - -const ensureConfigIsValidLatest = async < - Config extends BaseConfig, - LatestConfig extends BaseConfig, ->({ - configPath, - validateLatestConfig, - config, - validate, -}: EnsureConfigOptions): Promise => { - if (!validateLatestConfig(config)) { - return commandObj.error( - `Invalid config ${color.yellow( - configPath, - )}. ${await validationErrorToString(validateLatestConfig.errors)}`, - ); - } - - const maybeValidationError = - validate !== undefined && (await validate(config, configPath)); - - if (typeof maybeValidationError === "string") { - return commandObj.error( - `Invalid config ${color.yellow( - configPath, - )}. Errors:\n${maybeValidationError}`, - ); - } - - return config; -}; - -export type InitializedReadonlyConfig = Readonly & { - $getPath(): string; - $getDirPath(): string; - $getConfigString(): string; - $validateLatest: ValidateFunction; -}; -export type InitializedConfig = Mutable< - InitializedReadonlyConfig -> & { - $commit(): Promise; -}; -type BaseConfig = { version: number | string } & Record; -export type Migrations = Array< - (config: Config) => Config | Promise ->; -export type GetDefaultConfig = () => string | Promise; -export type GetPath = () => string | Promise; - -export type ConfigValidateFunction = ( - config: LatestConfig, - configPath: string, -) => ValidationResult | Promise; - -export type InitConfigOptions< - Config extends BaseConfig, - LatestConfig extends BaseConfig, -> = { - allSchemas: Array>; - latestSchema: JSONSchemaType; - migrations: Migrations; - name: string; - getConfigOrConfigDirPath: GetPath; - getSchemaDirPath?: GetPath; - validate?: ConfigValidateFunction; -}; - -type InitFunction = - () => Promise | null>; - -type InitFunctionWithDefault = () => Promise< - InitializedConfig ->; - -type InitReadonlyFunction = - () => Promise | null>; - -type InitReadonlyFunctionWithDefault = () => Promise< - InitializedReadonlyConfig ->; - -export const getConfigPath = ( - configOrConfigDirPath: string, - configName: string, -) => { - return configOrConfigDirPath.endsWith(YAML_EXT) || - configOrConfigDirPath.endsWith(YML_EXT) - ? { - configPath: configOrConfigDirPath, - configDirPath: dirname(configOrConfigDirPath), - } - : { - configPath: join(configOrConfigDirPath, configName), - configDirPath: configOrConfigDirPath, - }; -}; - -export function getReadonlyConfigInitFunction< - Config extends BaseConfig, - LatestConfig extends BaseConfig, ->( - options: InitConfigOptions, - getDefaultConfig?: undefined, -): InitReadonlyFunction; -export function getReadonlyConfigInitFunction< - Config extends BaseConfig, - LatestConfig extends BaseConfig, ->( - options: InitConfigOptions, - getDefaultConfig?: GetDefaultConfig, -): InitReadonlyFunctionWithDefault; - -export function getReadonlyConfigInitFunction< - Config extends BaseConfig, - LatestConfig extends BaseConfig, ->( - options: InitConfigOptions, - getDefaultConfig?: GetDefaultConfig, -): InitReadonlyFunction { - return async (): Promise | null> => { - const { - allSchemas, - latestSchema, - migrations, - name, - getConfigOrConfigDirPath, - validate, - getSchemaDirPath, - } = options; - - // every config schema must have a version, because LatestConfig extends BaseConfig - // but ajv doesn't currently produce correct types for this unfortunately - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-unsafe-member-access - const latestConfigVersion = latestSchema.properties.version.const as - | string - | number; - - const configFullName = `${name}.${YAML_EXT}`; - - const getConfigPathResult = getConfigPath( - await getConfigOrConfigDirPath(), - configFullName, - ); - - const { configDirPath } = getConfigPathResult; - let { configPath } = getConfigPathResult; - const validateLatestConfig = getAjv().compile(latestSchema); - - const schemaPathCommentStart = "# yaml-language-server: $schema="; - - async function ensureSchemaAndSchemaPathComment( - schema: AnySchema, - ): Promise { - return `${schemaPathCommentStart}${await ensureSchema({ - name, - configDirPath, - getSchemaDirPath, - schema, - })}`; - } - - /** - * Add schema path comment (if it's missing) or replace it (if it's incorrect) - */ - async function updateSchemaPathInConfigString(schema: AnySchema) { - if (name === DOCKER_COMPOSE_FILE_NAME) { - return; - } - - const schemaPathComment = await ensureSchemaAndSchemaPathComment(schema); - - configString = configString.startsWith(schemaPathCommentStart) - ? `${[schemaPathComment, ...configString.split("\n").slice(1)] - .join("\n") - .trim()}\n` - : `${schemaPathComment}\n${configString.trim()}\n`; - - if (configString !== configString) { - await saveConfig(configPath, configString); - } - } - - const [{ parse }, { yamlDiffPatch }] = await Promise.all([ - import("yaml"), - import("yaml-diff-patch"), - ]); - - let configString: string; - - try { - let fileContent: string; - - // try reading config file - // if it fails, try replacing .yaml with .yml and vice versa and read again - // this way we can support both .yaml and .yml extensions interchangeably - try { - fileContent = await readFile(configPath, FS_OPTIONS); - } catch (e) { - const endsWithYaml = configPath.endsWith(`.${YAML_EXT}`); - const endsWithYml = configPath.endsWith(`.${YML_EXT}`); - - if (!endsWithYaml && !endsWithYml && getDefaultConfig !== undefined) { - throw e; - } - - // try reading again by replacing .yaml with .yml or vice versa - const newConfigPath = `${configPath.slice( - 0, - -(endsWithYaml ? YAML_EXT : YML_EXT).length, - )}${endsWithYaml ? YML_EXT : YAML_EXT}`; - - fileContent = await readFile(newConfigPath, FS_OPTIONS); - configPath = newConfigPath; - } - - configString = fileContent; - await updateSchemaPathInConfigString(validateLatestConfig.schema); - } catch { - if (getDefaultConfig === undefined) { - // If config file doesn't exist and there is no default config, return null - return null; - } - // If config file doesn't exist, create it with default config and schema path comment - - const documentationLinkComment = `# Documentation for CLI v${commandObj.config.version}: https://github.com/fluencelabs/cli/tree/fluence-cli-v${commandObj.config.version}/packages/cli/package/docs/configs/${name.replace( - `.${YAML_EXT}`, - "", - )}.md`; - - const schemaPathComment = await ensureSchemaAndSchemaPathComment( - validateLatestConfig.schema, - ); - - const description = - typeof latestSchema["description"] === "string" - ? `\n\n# ${latestSchema["description"]}` - : ""; - - const defConf = await getDefaultConfig(); - - configString = yamlDiffPatch( - `${schemaPathComment}\n${description}\n\n${documentationLinkComment}\n`, - {}, - parse(defConf), - ); - - await saveConfig(configPath, configString); - } - - const config: unknown = parse(configString); - let latestConfig: LatestConfig; - - if (name === DOCKER_COMPOSE_FILE_NAME) { - // CLI will not validate docker-compose at all to not cause additional problems for the users - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - latestConfig = config as LatestConfig; - } else { - if ( - typeof config !== "object" || - config === null || - !("version" in config) || - (typeof config.version !== "number" && - typeof config.version !== "string") - ) { - return commandObj.error( - `Invalid config at ${color.yellow( - configPath, - )}. Expected to have a ${color.yellow("version")} property of type number`, - ); - } - - const currentConfigVersion = Number(config.version); - - if (!isInteger(currentConfigVersion) || currentConfigVersion < 0) { - return commandObj.error( - `Invalid config at ${color.yellow( - configPath, - )}. ${color.yellow("version")} property should be a positive integer. Got: ${color.yellow( - config.version, - )}`, - ); - } - - const currentSchema = allSchemas[currentConfigVersion]; - - if (currentSchema === undefined) { - return commandObj.error( - `Invalid config at ${color.yellow( - configPath, - )}. ${color.yellow("version")} property should be less than or equal to ${color.yellow( - allSchemas.length - 1, - )}. Got: ${color.yellow(config.version)}. Make sure you are using a correct version of the CLI that supports this config version`, - ); - } - - const validateCurrentConfigVersion = - getAjv().compile(currentSchema); - - if (!validateCurrentConfigVersion(config)) { - await updateSchemaPathInConfigString( - validateCurrentConfigVersion.schema, - ); - - return commandObj.error( - `Invalid config at ${color.yellow( - configPath, - )}. ${await validationErrorToString( - validateCurrentConfigVersion.errors, - )}${ - currentConfigVersion === latestConfigVersion - ? "" - : `\n\nConfig version is ${color.yellow(currentConfigVersion)}, while the latest config version (that ${CLI_NAME_FULL} of version ${commandObj.config.version} is aware of) is ${color.yellow( - latestConfigVersion, - )}. ${CLI_NAME_FULL} will automatically migrate your config to the latest version when you fix validation errors (that are listed above) and run any command that uses this config` - }`, - ); - } - - if (Number(config.version) < migrations.length) { - ({ latestConfig, configString } = await migrateConfig({ - config, - configPath, - configString, - migrations, - validateLatestConfig, - validate, - latestConfigVersion, - })); - } else { - latestConfig = await ensureConfigIsValidLatest({ - config, - configPath, - validateLatestConfig, - validate, - latestConfigVersion, - }); - } - } - - return { - ...latestConfig, - $getPath(): string { - return configPath; - }, - $getDirPath(): string { - return dirname(configPath); - }, - $getConfigString(): string { - return configString; - }, - $validateLatest: validateLatestConfig, - }; - }; -} - -const initializedConfigs = new Map>(); - -export function formatConfig(configString: string) { - const formattedConfig = configString - .trim() - .split("\n") - .flatMap((line, i, ar) => { - // If it's an empty string - it was a newline before split - remove it - if (line.trim() === "") { - return []; - } - - const maybePreviousLine = ar[i - 1]; - const isComment = line.startsWith("#"); - const isPreviousLineComment = maybePreviousLine?.startsWith("#") ?? false; - - const addNewLineBeforeBlockOfComments = - isComment && !isPreviousLineComment; - - if (addNewLineBeforeBlockOfComments) { - return ["", line]; - } - - const isFirstLine = maybePreviousLine === undefined; - const isIndentedCode = line.startsWith(" "); - - const doNotAddNewLine = - isFirstLine || isIndentedCode || isComment || isPreviousLineComment; - - if (doNotAddNewLine) { - return [line]; - } - - // If it's top level property - separate it with a new line ("" -> "\n" when joined) - return ["", line]; - }) - .join("\n"); - - return `${formattedConfig.trim()}\n`; -} - -async function saveConfig( - configPath: string, - migratedConfigString: string, -): Promise { - const configToSave = formatConfig(migratedConfigString); - await writeFile(configPath, configToSave, FS_OPTIONS); - return configToSave; -} - -export function getConfigInitFunction< - Config extends BaseConfig, - LatestConfig extends BaseConfig, ->( - options: InitConfigOptions, - getDefaultConfig?: never, -): InitFunction; -export function getConfigInitFunction< - Config extends BaseConfig, - LatestConfig extends BaseConfig, ->( - options: InitConfigOptions, - getDefaultConfig: GetDefaultConfig, -): InitFunctionWithDefault; - -export function getConfigInitFunction< - Config extends BaseConfig, - LatestConfig extends BaseConfig, ->( - options: InitConfigOptions, - getDefaultConfig?: GetDefaultConfig, -): InitFunction { - return async (): Promise | null> => { - const configFullName = `${options.name}.${YAML_EXT}`; - - let { configPath } = getConfigPath( - await options.getConfigOrConfigDirPath(), - configFullName, - ); - - const previouslyInitializedConfig = initializedConfigs.get(configPath); - - if (previouslyInitializedConfig !== undefined) { - // It's safe to assert here because we know that previouslyInitializedConfig has the same type - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - return previouslyInitializedConfig as InitializedConfig; - } - - const initializedReadonlyConfig = - getDefaultConfig === undefined - ? await getReadonlyConfigInitFunction(options)() - : await getReadonlyConfigInitFunction(options, getDefaultConfig)(); - - if (initializedReadonlyConfig === null) { - return null; - } - - configPath = initializedReadonlyConfig.$getPath(); - let configString = initializedReadonlyConfig.$getConfigString(); - - const config = { - ...initializedReadonlyConfig, - // have to type-cast `this` because TypeScript incorrectly thinks `this` can be a PromiseLike - async $commit(this: InitializedConfig): Promise { - const config = removeProperties(this, ([, v]) => { - return typeof v === "function"; - }); - - const [{ parse }, { yamlDiffPatch }] = await Promise.all([ - import("yaml"), - import("yaml-diff-patch"), - ]); - - const newConfigString = `${yamlDiffPatch( - configString, - parse(configString), - config, - ).trim()}\n`; - - if (!initializedReadonlyConfig.$validateLatest(config)) { - throw new Error( - `Couldn't save config ${color.yellow( - configPath, - )}.\n\n${newConfigString}\n\n${await validationErrorToString( - initializedReadonlyConfig.$validateLatest.errors, - )}`, - ); - } - - if (configString !== newConfigString) { - configString = await saveConfig(configPath, newConfigString); - } - }, - $getConfigString(): string { - return configString; - }, - }; - - initializedConfigs.set(configPath, config); - return config; - }; -} diff --git a/packages/cli/package/src/lib/configs/initConfigNew.ts b/packages/cli/package/src/lib/configs/initConfigNew.ts index 491f2dc73..0e2d341f5 100644 --- a/packages/cli/package/src/lib/configs/initConfigNew.ts +++ b/packages/cli/package/src/lib/configs/initConfigNew.ts @@ -26,7 +26,6 @@ import { jsonStringify } from "../../common.js"; import { validationErrorToString, getAjv } from "../ajvInstance.js"; import { commandObj } from "../commandObj.js"; import { - FS_OPTIONS, YAML_EXT, YML_EXT, CLI_NAME_FULL, @@ -36,7 +35,6 @@ import { dbg } from "../dbg.js"; import { numToStr } from "../helpers/typesafeStringify.js"; import { removeProperties } from "../helpers/utils.js"; -import { formatConfig, type GetPath } from "./initConfig.js"; import type { InitializedConfig, InitConfigOptions, @@ -45,6 +43,7 @@ import type { OptionsTuple, ConfigOptionsWithoutMigrate, ConfigOptions, + GetPath, } from "./initConfigNewTypes.js"; const initializedConfigs = new Map< @@ -322,7 +321,7 @@ async function getLatestConfig({ // if it fails, try replacing .yaml with .yml or vice versa and read again // this way we can support both .yaml and .yml extensions interchangeably try { - configString = await readFile(actualConfigPath, FS_OPTIONS); + configString = await readFile(actualConfigPath, "utf8"); } catch { const endsWithYaml = actualConfigPath.endsWith(YAML_EXT); @@ -332,7 +331,7 @@ async function getLatestConfig({ -(endsWithYaml ? YAML_EXT : YML_EXT).length, )}${endsWithYaml ? YML_EXT : YAML_EXT}`; - configString = await readFile(newConfigPath, FS_OPTIONS); + configString = await readFile(newConfigPath, "utf8"); actualConfigPath = newConfigPath; } } catch { @@ -466,10 +465,10 @@ async function updateSchema( const schemaString = `${jsonStringify(configOptions.schema)}\n`; try { - const actualSchemaString = await readFile(schemaPath, FS_OPTIONS); + const actualSchemaString = await readFile(schemaPath, "utf8"); assert(actualSchemaString === schemaString); } catch { - await writeFile(schemaPath, schemaString, FS_OPTIONS); + await writeFile(schemaPath, schemaString, "utf8"); } } @@ -483,10 +482,49 @@ async function saveConfig( } const newConfigString = formatConfig(configString); - await writeFile(configPath, newConfigString, FS_OPTIONS); + await writeFile(configPath, newConfigString, "utf8"); return newConfigString; } function getConfigName(configPath: string) { return basename(configPath).replace(/\.(yml|yaml)$/, ""); } + +function formatConfig(configString: string) { + const formattedConfig = configString + .trim() + .split("\n") + .flatMap((line, i, ar) => { + // If it's an empty string - it was a newline before split - remove it + if (line.trim() === "") { + return []; + } + + const maybePreviousLine = ar[i - 1]; + const isComment = line.startsWith("#"); + const isPreviousLineComment = maybePreviousLine?.startsWith("#") ?? false; + + const addNewLineBeforeBlockOfComments = + isComment && !isPreviousLineComment; + + if (addNewLineBeforeBlockOfComments) { + return ["", line]; + } + + const isFirstLine = maybePreviousLine === undefined; + const isIndentedCode = line.startsWith(" "); + + const doNotAddNewLine = + isFirstLine || isIndentedCode || isComment || isPreviousLineComment; + + if (doNotAddNewLine) { + return [line]; + } + + // If it's top level property - separate it with a new line ("" -> "\n" when joined) + return ["", line]; + }) + .join("\n"); + + return `${formattedConfig.trim()}\n`; +} diff --git a/packages/cli/package/src/lib/configs/initConfigNewTypes.ts b/packages/cli/package/src/lib/configs/initConfigNewTypes.ts index 5ee39c773..229d76ff2 100644 --- a/packages/cli/package/src/lib/configs/initConfigNewTypes.ts +++ b/packages/cli/package/src/lib/configs/initConfigNewTypes.ts @@ -19,8 +19,6 @@ import type { JSONSchemaType } from "ajv"; import type { ValidationResult } from "../helpers/validations.js"; -import type { GetPath } from "./initConfig.js"; - export type InitializedConfig = LatestConfig & { $getPath(): string; $commit(): Promise; @@ -83,6 +81,8 @@ export type OptionsTuple = [ ...(C9 extends undefined ? [] : ConfigOptions[]), ]; +export type GetPath = () => string | Promise; + export type InitConfigOptions< C0, C1 = undefined, diff --git a/packages/cli/package/src/lib/configs/keyPair.ts b/packages/cli/package/src/lib/configs/keyPair.ts deleted file mode 100644 index cc5750e8b..000000000 --- a/packages/cli/package/src/lib/configs/keyPair.ts +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import type { JSONSchemaType } from "ajv"; - -export type ConfigKeyPair = { - secretKey: string; - name: string; -}; - -export const configKeyPairSchema: JSONSchemaType = { - title: "Key Pair", - type: "object", - properties: { - secretKey: { type: "string" }, - name: { type: "string" }, - }, - required: ["secretKey", "name"], -}; diff --git a/packages/cli/package/src/lib/configs/project/chainContainers.ts b/packages/cli/package/src/lib/configs/project/chainContainers.ts index c0d6a4384..7129b24cd 100644 --- a/packages/cli/package/src/lib/configs/project/chainContainers.ts +++ b/packages/cli/package/src/lib/configs/project/chainContainers.ts @@ -17,7 +17,7 @@ import { versions } from "../../../versions.js"; -export type Service = { +type Service = { image?: string; ports?: string[]; pull_policy?: string; @@ -29,24 +29,24 @@ export type Service = { healthcheck?: Record; }; -export type Config = { +type Config = { services: Record; volumes?: Record; include?: string[]; secrets?: Record; }; -export const CHAIN_DEPLOY_SCRIPT_NAME = "chain-deploy-script"; +const CHAIN_DEPLOY_SCRIPT_NAME = "chain-deploy-script"; export const CHAIN_RPC_CONTAINER_NAME = "chain-rpc"; export const CHAIN_RPC_PORT = "8545"; -export const GRAPH_NODE_CONTAINER_NAME = "graph-node"; -export const GRAPH_NODE_PORT = "8020"; -export const IPFS_CONTAINER_NAME = "ipfs"; -export const IPFS_PORT = "5001"; -export const POSTGRES_CONTAINER_NAME = "postgres"; -export const SUBGRAPH_DEPLOY_SCRIPT_NAME = "subgraph-deploy-script"; +const GRAPH_NODE_CONTAINER_NAME = "graph-node"; +const GRAPH_NODE_PORT = "8020"; +const IPFS_CONTAINER_NAME = "ipfs"; +const IPFS_PORT = "5001"; +const POSTGRES_CONTAINER_NAME = "postgres"; +const SUBGRAPH_DEPLOY_SCRIPT_NAME = "subgraph-deploy-script"; -export const chainContainers: Config = { +export const chainContainers = { volumes: { "chain-rpc": null, [IPFS_CONTAINER_NAME]: null, @@ -150,4 +150,4 @@ export const chainContainers: Config = { depends_on: [GRAPH_NODE_CONTAINER_NAME], }, }, -}; +} as const satisfies Config; diff --git a/packages/cli/package/src/lib/configs/project/dockerCompose.ts b/packages/cli/package/src/lib/configs/project/dockerCompose.ts index 8ca368bba..c8f95e83f 100644 --- a/packages/cli/package/src/lib/configs/project/dockerCompose.ts +++ b/packages/cli/package/src/lib/configs/project/dockerCompose.ts @@ -15,192 +15,20 @@ * along with this program. If not, see . */ -import assert from "assert"; import { writeFile } from "fs/promises"; -import { join, relative } from "path"; import { yamlDiffPatch } from "yaml-diff-patch"; -import { versions } from "../../../versions.js"; -import { - CONFIGS_DIR_NAME, - HTTP_PORT_START, - PROVIDER_CONFIG_FULL_FILE_NAME, - TCP_PORT_START, - WEB_SOCKET_PORT_START, -} from "../../const.js"; -import { numToStr } from "../../helpers/typesafeStringify.js"; import { pathExists } from "../../helpers/utils.js"; -import { genSecretKeyOrReturnExisting } from "../../keyPairs.js"; -import { ensureFluenceConfigsDir, getFluenceDir } from "../../paths.js"; import { ensureDockerComposeConfigPath } from "../../paths.js"; -import { CHAIN_RPC_CONTAINER_NAME } from "./chainContainers.js"; -import { IPFS_CONTAINER_NAME } from "./chainContainers.js"; -import { CHAIN_DEPLOY_SCRIPT_NAME } from "./chainContainers.js"; -import { - chainContainers, - type Config, - type Service, -} from "./chainContainers.js"; -import { - ensureComputerPeerConfigs, - getConfigTomlName, -} from "./provider/provider.js"; - -type GenNoxImageArgs = { - name: string; - tcpPort: number; - webSocketPort: number; - httpPort: number; - bootstrapName: string; - bootstrapTcpPort?: number; -}; - -function genNox({ - name, - tcpPort, - webSocketPort, - httpPort, - bootstrapName, - bootstrapTcpPort, -}: GenNoxImageArgs): [name: string, service: Service] { - const configTomlName = getConfigTomlName(name); - const configLocation = `/run/${CONFIGS_DIR_NAME}/${configTomlName}`; - const tcpPortString = numToStr(tcpPort); - const websocketPortString = numToStr(webSocketPort); - return [ - name, - { - image: versions.nox, - ports: [ - `${tcpPortString}:${tcpPortString}`, - `${websocketPortString}:${websocketPortString}`, - ], - environment: { - WASM_LOG: "debug", - FLUENCE_MAX_SPELL_PARTICLE_TTL: "30s", - FLUENCE_ROOT_KEY_PAIR__PATH: `/run/secrets/${name}`, - RUST_LOG: - "info,chain_connector=debug,run-console=trace,aquamarine::log=debug,network=trace,worker_inactive=trace,expired=info,spell=debug,ipfs_effector=debug,ipfs_pure=debug,spell_event_bus=trace,system_services=debug,particle_reap=debug,aquamarine::actor=debug,aquamarine::aqua_runtime=off,aquamarine=warn,chain_listener=debug,chain-connector=debug,execution=trace", - }, - command: [ - `--config=${configLocation}`, - "--dev-mode", - "--external-maddrs", - `/dns4/${name}/tcp/${tcpPortString}`, - `/dns4/${name}/tcp/${websocketPortString}/ws`, - "--allow-private-ips", - bootstrapTcpPort === undefined - ? "--local" - : `--bootstraps=/dns/${bootstrapName}/tcp/${numToStr(bootstrapTcpPort)}`, - "--print-config", - ], - depends_on: { - [IPFS_CONTAINER_NAME]: { condition: "service_healthy" }, - [CHAIN_RPC_CONTAINER_NAME]: { condition: "service_healthy" }, - [CHAIN_DEPLOY_SCRIPT_NAME]: { - condition: "service_completed_successfully", - }, - }, - volumes: [ - `./${CONFIGS_DIR_NAME}/${configTomlName}:${configLocation}`, - `${name}:/.fluence`, - ], - secrets: [name], - healthcheck: { - test: `curl -f http://localhost:${numToStr(httpPort)}/health`, - interval: "5s", - timeout: "2s", - retries: 10, - }, - }, - ]; -} - -async function genDefaultDockerCompose(): Promise { - const configsDir = await ensureFluenceConfigsDir(); - const fluenceDir = getFluenceDir(); - const computePeers = await ensureComputerPeerConfigs(); - - const peers = await Promise.all( - computePeers.map(async ({ name, overriddenNoxConfig }) => { - return { - ...(await genSecretKeyOrReturnExisting(name)), - webSocketPort: overriddenNoxConfig.websocketPort, - tcpPort: overriddenNoxConfig.tcpPort, - httpPort: overriddenNoxConfig.httpPort, - relativeConfigFilePath: relative( - fluenceDir, - join(configsDir, getConfigTomlName(name)), - ), - }; - }), - ); - - const [bootstrap, ...restNoxes] = peers; - - assert( - bootstrap !== undefined, - `Unreachable. 'computePeers' non-emptiness is checked during ${PROVIDER_CONFIG_FULL_FILE_NAME} validation`, - ); - - const { - name: bootstrapName, - webSocketPort: bootstrapWebSocketPort = WEB_SOCKET_PORT_START, - tcpPort: bootstrapTcpPort = TCP_PORT_START, - httpPort: bootstrapHttpPort = HTTP_PORT_START, - } = bootstrap; - - return { - volumes: { - ...chainContainers.volumes, - ...Object.fromEntries( - peers.map(({ name }) => { - return [name, null] as const; - }), - ), - }, - secrets: Object.fromEntries( - peers.map(({ name, relativeSecretFilePath: file }) => { - return [name, { file }] as const; - }), - ), - services: { - ...chainContainers.services, - ...Object.fromEntries([ - genNox({ - name: bootstrapName, - tcpPort: bootstrapTcpPort, - webSocketPort: bootstrapWebSocketPort, - httpPort: bootstrapHttpPort, - bootstrapName: bootstrapName, - }), - ]), - ...Object.fromEntries( - restNoxes.map(({ name, tcpPort, webSocketPort, httpPort }) => { - return genNox({ - name, - tcpPort, - webSocketPort, - httpPort, - bootstrapName, - bootstrapTcpPort, - }); - }), - ), - }, - }; -} +import { chainContainers } from "./chainContainers.js"; export async function ensureDockerComposeConfig() { const configPath = await ensureDockerComposeConfigPath(); if (!(await pathExists(configPath))) { - await writeFile( - configPath, - yamlDiffPatch("", {}, await genDefaultDockerCompose()), - ); + await writeFile(configPath, yamlDiffPatch("", {}, chainContainers)); } return configPath; diff --git a/packages/cli/package/src/lib/configs/project/env/env1.ts b/packages/cli/package/src/lib/configs/project/env/env1.ts index 4157901f3..a88ac361e 100644 --- a/packages/cli/package/src/lib/configs/project/env/env1.ts +++ b/packages/cli/package/src/lib/configs/project/env/env1.ts @@ -31,6 +31,7 @@ export type Config = { blockScoutUrl?: string; chainId?: number; deployment?: Partial; + ipfsGateway?: string; }; export default { @@ -86,6 +87,12 @@ export default { diamond: { type: "string", nullable: true }, }, }, + ipfsGateway: { + type: "string", + description: `IPFS gateway URL to use`, + format: "uri", + nullable: true, + }, }, additionalProperties: false, }, diff --git a/packages/cli/package/src/lib/configs/project/fluence.ts b/packages/cli/package/src/lib/configs/project/fluence.ts deleted file mode 100644 index 2a5e4eabc..000000000 --- a/packages/cli/package/src/lib/configs/project/fluence.ts +++ /dev/null @@ -1,1835 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import assert from "node:assert"; -import { rm } from "node:fs/promises"; -import { isAbsolute, join, relative } from "node:path"; - -import type { CompileFromPathArgs } from "@fluencelabs/aqua-api"; -import { color } from "@oclif/color"; -import type { JSONSchemaType } from "ajv"; -import { yamlDiffPatch } from "yaml-diff-patch"; - -import { - CHAIN_ENV, - CHAIN_ENV_OLD, - type ChainENV, - type ChainENVOld, -} from "../../../common.js"; -import CLIPackageJSON from "../../../versions/cli.package.json" with { type: "json" }; -import { versions } from "../../../versions.js"; -import { ajv, validationErrorToString } from "../../ajvInstance.js"; -import { validateProtocolVersion } from "../../chain/chainValidators.js"; -import { commandObj } from "../../commandObj.js"; -import { - COMPUTE_UNIT_MEMORY_STR, - MAX_HEAP_SIZE_DESCRIPTION, - AQUA_DIR_NAME, - AQUA_LIB_NPM_DEPENDENCY, - AUTO_GENERATED, - CLI_NAME, - CLI_NAME_FULL, - DEFAULT_DEPLOYMENT_NAME, - DEFAULT_IPFS_ADDRESS, - DEFAULT_MARINE_BUILD_ARGS, - DOT_FLUENCE_DIR_NAME, - FLUENCE_CONFIG_FILE_NAME, - FLUENCE_CONFIG_FULL_FILE_NAME, - type FluenceEnv, - USER_CONFIG_FULL_FILE_NAME, - IPFS_ADDR_PROPERTY, - LOCAL_IPFS_ADDRESS, - MARINE_BUILD_ARGS_FLAG_NAME, - MARINE_BUILD_ARGS_PROPERTY, - DEFAULT_PRICE_PER_CU_PER_EPOCH_DEVELOPER, - TOP_LEVEL_SCHEMA_ID, - aquaLogLevelsString, - AQUA_LOG_LEVELS, - type AquaLogLevel, - PT_SYMBOL, - COMPILE_AQUA_PROPERTY_NAME, - ENV_CONFIG_FULL_FILE_NAME, -} from "../../const.js"; -import { numToStr } from "../../helpers/typesafeStringify.js"; -import { splitErrorsAndResults } from "../../helpers/utils.js"; -import { - validateCIDs, - validateVersionsIsExact, - validateBatchAsync, -} from "../../helpers/validations.js"; -import { writeSecretKey } from "../../keyPairs.js"; -import { resolveDefaultRelays } from "../../multiaddres.js"; -import { getFluenceDir, projectRootDir } from "../../paths.js"; -import type { Mutable } from "../../typeHelpers.js"; -import { - type ConfigValidateFunction, - getConfigInitFunction, - getReadonlyConfigInitFunction, - type InitConfigOptions, - type InitializedConfig, - type InitializedReadonlyConfig, - type Migrations, -} from "../initConfig.js"; - -import { initNewEnvConfig } from "./env/env.js"; -import { - type OverridableModuleProperties, - overridableModuleProperties, -} from "./module.js"; -import { initReadonlyProjectSecretsConfig } from "./projectSecrets.js"; -import { - overridableServiceProperties, - type OverridableServiceProperties, -} from "./service.js"; -import { - type OverridableSpellProperties, - overridableSpellProperties, -} from "./spell.js"; - -export const TARGET_WORKERS_DEFAULT = 3; - -type ServiceV0 = { name: string; count?: number }; - -type ConfigV0 = { - version: 0; - services: Array; -}; - -const configSchemaV0Obj = { - type: "object", - properties: { - version: { type: "integer", const: 0 }, - services: { - title: "Services", - type: "array", - items: { - type: "object", - properties: { - name: { type: "string" }, - count: { type: "integer", nullable: true, minimum: 1 }, - }, - required: ["name"], - }, - }, - }, - required: ["version", "services"], -} as const; - -const configSchemaV0: JSONSchemaType = configSchemaV0Obj; - -export type OverrideModules = Record; - -type ServiceV1 = { - get: string; - overrideModules?: OverrideModules; -} & OverridableServiceProperties; - -type ConfigV1 = { - version: 1; - services?: Record; - relays?: FluenceEnv | Array; -}; - -const overrideModulesSchema: JSONSchemaType = { - type: "object", - title: "Module overrides", - description: "Overrides for the module config", - properties: { - ...overridableModuleProperties.properties, - }, - required: [], - nullable: true, - additionalProperties: false, -} as const; - -const serviceSchema: JSONSchemaType = { - title: "Service config", - description: - "Service config. Defines where the service is and how to deploy it", - type: "object", - properties: { - get: { - type: "string", - description: `Path to service directory or URL to the tar.gz archive with the service`, - }, - ...overridableServiceProperties.properties, - overrideModules: { - type: "object", - title: "Overrides", - description: "A map of modules to override", - additionalProperties: overrideModulesSchema, - properties: { - Module_name: overrideModulesSchema, - }, - nullable: true, - required: [], - }, - }, - required: ["get", ...overridableServiceProperties.required], - additionalProperties: false, -} as const; - -const configSchemaV1Obj = { - type: "object", - properties: { - services: { - title: "Services", - description: `A map with service names as keys and Service configs as values. You can have any number of services listed here as long as service name keys start with a lowercase letter and contain only letters numbers and underscores. You can use \`${CLI_NAME} service add\` command to add a service to this config`, - type: "object", - additionalProperties: serviceSchema, - properties: { - Service_name: serviceSchema, - }, - required: [], - nullable: true, - }, - relays: { - title: "Relays", - description: `List of Fluence Peer multi addresses or a name of the network. This multi addresses are used for connecting to the Fluence network when deploying. Peer ids from these addresses are also used for deploying in case if you don't specify "peerId" or "peerIds" property in the deployment config. Default: ${CHAIN_ENV[0]}`, - type: ["string", "array"], - oneOf: [ - { type: "string", title: "Network name", enum: CHAIN_ENV }, - { - type: "array", - title: "Multi addresses", - items: { type: "string" }, - minItems: 1, - }, - ], - nullable: true, - }, - version: { type: "integer", const: 1 }, - }, - required: ["version"], -} as const; - -const configSchemaV1: JSONSchemaType = configSchemaV1Obj; - -const AQUA_INPUT_PATH_PROPERTY = "aquaInputPath"; - -type FluenceConfigSpell = { - get: string; -} & OverridableSpellProperties; - -type Deal = { - computeUnits?: 1; - minWorkers?: number; - targetWorkers?: number; - maxWorkersPerProvider?: number; - pricePerWorkerEpoch?: string; - initialBalance?: string; - effectors?: string[]; - whitelist?: string[]; - blacklist?: string[]; - protocolVersion?: number; -}; - -type Worker = { - services?: Array; - spells?: Array; -}; - -type Host = { peerIds: Array }; - -type ConfigV2 = Omit & { - version: 2; - dependencies?: { - npm?: Record; - cargo?: Record; - }; - [AQUA_INPUT_PATH_PROPERTY]?: string; - aquaOutputTSPath?: string; - aquaOutputJSPath?: string; - hosts?: Record; - workers?: Record; - deals?: Record; - chainNetwork?: ChainENVOld; - spells?: Record; - aquaImports?: Array; - cliVersion?: string; - [MARINE_BUILD_ARGS_PROPERTY]?: string; - [IPFS_ADDR_PROPERTY]?: string; -}; - -const spellSchema: JSONSchemaType = { - type: "object", - description: "Spell config", - properties: { - get: { - type: "string", - description: "Path to spell", - }, - ...overridableSpellProperties, - }, - required: ["get"], - additionalProperties: false, -} as const; - -const dealSchemaObj = { - type: "object", - properties: { - computeUnits: { - type: "integer", - minimum: 1, - maximum: 1, - default: 1, - description: `DEPRECATED. USE cuCountPerWorker INSTEAD. Number of compute units you require. 1 compute unit = ${COMPUTE_UNIT_MEMORY_STR}. Currently the only allowed value is 1. This will change in the future. Default: 1`, - nullable: true, - }, - targetWorkers: { - type: "integer", - description: "Max workers in the deal", - default: TARGET_WORKERS_DEFAULT, - nullable: true, - minimum: 1, - }, - minWorkers: { - type: "integer", - description: - "Required workers to activate the deal. Matches target workers by default", - nullable: true, - minimum: 1, - }, - maxWorkersPerProvider: { - type: "integer", - description: - "Max workers per provider. Matches target workers by default", - nullable: true, - minimum: 1, - }, - pricePerWorkerEpoch: { - type: "string", - description: `Price per worker epoch in ${PT_SYMBOL}`, - default: DEFAULT_PRICE_PER_CU_PER_EPOCH_DEVELOPER, - nullable: true, - }, - initialBalance: { - type: "string", - description: `Initial balance after deploy in ${PT_SYMBOL}. Default: targetWorkers * pricePerCuPerEpoch * minDealDepositedEpochs. For local environment: enough for deal to be active for 1 day`, - nullable: true, - }, - effectors: { - type: "array", - description: "Effector CIDs to be used in the deal. Must be a valid CID", - items: { type: "string" }, - nullable: true, - }, - whitelist: { - type: "array", - description: - "Whitelist of providers to deploy to. Can't be used together with blacklist", - items: { type: "string" }, - nullable: true, - }, - blacklist: { - type: "array", - description: - "Blacklist of providers to deploy to. Can't be used together with whitelist", - items: { type: "string" }, - nullable: true, - }, - protocolVersion: { - type: "integer", - description: `Protocol version. Default: ${numToStr(versions.protocolVersion)}`, - nullable: true, - default: versions.protocolVersion, - minimum: 1, - }, - }, - required: [], -} as const satisfies JSONSchemaType; - -const dealSchema: JSONSchemaType = dealSchemaObj; - -const workerConfigSchemaObj = { - type: "object", - description: "Deployment config", - properties: { - services: { - description: `An array of service names to include in this worker. Service names must be listed in ${FLUENCE_CONFIG_FULL_FILE_NAME}`, - type: "array", - items: { type: "string" }, - nullable: true, - }, - spells: { - description: `An array of spell names to include in this worker. Spell names must be listed in ${FLUENCE_CONFIG_FULL_FILE_NAME}`, - type: "array", - items: { type: "string" }, - nullable: true, - }, - }, - required: [], -} as const; - -const workerConfigSchema: JSONSchemaType = workerConfigSchemaObj; - -const hostConfigSchemaObj = { - type: "object", - properties: { - peerIds: { - type: "array", - description: "An array of peer IDs to deploy on", - items: { type: "string" }, - minItems: 1, - }, - }, - required: ["peerIds"], -} as const; - -const hostConfigSchema: JSONSchemaType = hostConfigSchemaObj; - -const validateHostsSchema = ajv.compile(hostConfigSchema); - -export function assertIsArrayWithHostsOrDeals( - unknownArr: [string, unknown][], -): asserts unknownArr is [string, Host | Deal][] { - unknownArr.forEach(([, unknown]) => { - assert(validateHostsSchema(unknown) || validateDeploymentSchema(unknown)); - }); -} - -const configSchemaV2Obj = { - ...configSchemaV1Obj, - properties: { - ...configSchemaV1Obj.properties, - version: { type: "integer", const: 2 }, - dependencies: { - type: "object", - title: "Dependencies", - nullable: true, - description: - "(For advanced users) Overrides for the project dependencies", - properties: { - npm: { - type: "object", - title: "npm dependencies", - nullable: true, - description: `A map of npm aqua dependency versions. ${CLI_NAME_FULL} ensures dependencies are installed each time you run aqua`, - additionalProperties: { type: "string" }, - properties: { - npm_dependency_name: { - type: "string", - description: "npm dependency version", - }, - }, - required: [], - }, - cargo: { - type: "object", - title: "Cargo dependencies", - nullable: true, - description: `A map of cargo dependency versions. ${CLI_NAME_FULL} ensures dependencies are installed each time you run commands that depend on Marine or Marine REPL`, - required: [], - additionalProperties: { type: "string" }, - properties: { - Cargo_dependency_name: { - type: "string", - description: "cargo dependency version", - }, - }, - }, - }, - required: [], - additionalProperties: false, - }, - [AQUA_INPUT_PATH_PROPERTY]: { - type: "string", - nullable: true, - description: - "Path to the aqua file or directory with aqua files that you want to compile by default. Must be relative to the project root dir", - }, - aquaOutputTSPath: { - type: "string", - nullable: true, - description: - "Path to the default compilation target dir from aqua to ts. Must be relative to the project root dir", - }, - aquaOutputJSPath: { - type: "string", - nullable: true, - description: - "Path to the default compilation target dir from aqua to js. Must be relative to the project root dir. Overrides 'aquaOutputTSPath' property", - }, - hosts: { - description: - "A map of objects with worker names as keys, each object defines a list of peer IDs to host the worker on", - type: "object", - nullable: true, - additionalProperties: hostConfigSchema, - properties: { - Worker_to_host: hostConfigSchema, - }, - required: [], - }, - workers: { - nullable: true, - description: - "A Map with worker names as keys and worker configs as values", - type: "object", - additionalProperties: workerConfigSchema, - properties: { - Worker: workerConfigSchema, - }, - required: [], - }, - deals: { - description: - "A map of objects with worker names as keys, each object defines a deal", - type: "object", - nullable: true, - additionalProperties: dealSchema, - properties: { - Worker_to_create_deal_for: dealSchema, - }, - required: [], - }, - chainNetwork: { - type: "string", - description: "The network in which the transactions will be carried out", - enum: CHAIN_ENV_OLD, - default: "dar", - nullable: true, - }, - spells: { - type: "object", - nullable: true, - description: "A map with spell names as keys and spell configs as values", - additionalProperties: spellSchema, - properties: { - Spell_name: spellSchema, - }, - required: [], - }, - aquaImports: { - type: "array", - description: `A list of path to be considered by aqua compiler to be used as imports. First dependency in the list has the highest priority. Priority of imports is considered in the following order: imports from --import flags, imports from aquaImports property in ${FLUENCE_CONFIG_FULL_FILE_NAME}, project's ${join( - DOT_FLUENCE_DIR_NAME, - AQUA_DIR_NAME, - )} dir, npm dependencies from ${FLUENCE_CONFIG_FULL_FILE_NAME}, npm dependencies from user's ${join( - DOT_FLUENCE_DIR_NAME, - USER_CONFIG_FULL_FILE_NAME, - )}, npm dependencies recommended by fluence`, - items: { type: "string" }, - nullable: true, - }, - [MARINE_BUILD_ARGS_PROPERTY]: { - type: "string", - description: `Space separated \`cargo build\` flags and args to pass to marine build. Can be overridden using --${MARINE_BUILD_ARGS_FLAG_NAME} flag Default: ${DEFAULT_MARINE_BUILD_ARGS}`, - nullable: true, - }, - cliVersion: { - type: "string", - description: `The version of the ${CLI_NAME_FULL} that is compatible with this project. Set this to enforce a particular set of versions of all fluence components`, - nullable: true, - }, - [IPFS_ADDR_PROPERTY]: { - type: "string", - description: `IPFS multiaddress to use when uploading workers with '${CLI_NAME} deploy'. Default: ${DEFAULT_IPFS_ADDRESS} or ${LOCAL_IPFS_ADDRESS} if using local local env (for 'workers deploy' IPFS address provided by relay that you are connected to is used)`, - nullable: true, - default: DEFAULT_IPFS_ADDRESS, - }, - }, -} as const satisfies JSONSchemaType; - -const configSchemaV2: JSONSchemaType = configSchemaV2Obj; - -type ConfigV3 = Omit & { - version: 3; - customFluenceEnv?: { - contractsEnv: ChainENVOld; - relays: Array; - }; -}; - -const configSchemaV3Obj = { - ...configSchemaV2Obj, - properties: { - ...(() => { - const properties = { - ...configSchemaV2Obj.properties, - }; - - // @ts-expect-error v3 deprecated relays property - delete properties.relays; - // @ts-expect-error v3 deprecated chainNetwork property - delete properties.chainNetwork; - return properties; - })(), - version: { type: "integer", const: 3 }, - customFluenceEnv: { - type: "object", - description: `Custom Fluence environment to use when connecting to Fluence network`, - nullable: true, - properties: { - contractsEnv: { - type: "string", - description: `Contracts environment to use for this fluence network to sign contracts on the blockchain`, - enum: CHAIN_ENV_OLD, - }, - relays: { - type: "array", - description: `List of custom relay multiaddresses to use when connecting to Fluence network`, - items: { type: "string" }, - minItems: 1, - }, - }, - required: ["contractsEnv", "relays"], - additionalProperties: false, - }, - }, -} as const; - -const configSchemaV3: JSONSchemaType = configSchemaV3Obj; - -type RelayPath = string; - -type ConfigV4 = Omit & { - version: 4; - defaultSecretKeyName?: string; - relaysPath?: RelayPath | Array; -}; - -const configSchemaV4Obj = { - ...configSchemaV3Obj, - properties: { - ...configSchemaV3Obj.properties, - version: { type: "integer", const: 4 }, - defaultSecretKeyName: { - description: - "Secret key with this name will be used by default by js-client inside CLI to run Aqua code", - type: "string", - nullable: true, - }, - relaysPath: { - type: ["string", "array"], - description: - "Single or multiple paths to the directories where you want relays.json file to be generated. Must be relative to the project root dir. This file contains a list of relays to use when connecting to Fluence network and depends on the default environment that you use in your project", - oneOf: [ - { type: "string" }, - { - type: "array", - items: { type: "string" }, - minItems: 1, - }, - ], - nullable: true, - }, - }, -} as const; - -const configSchemaV4: JSONSchemaType = configSchemaV4Obj; - -type ConfigV5 = Omit & { - version: 5; - deals?: Record; - hosts?: Record; -}; - -const configSchemaV4ObjPropertiesWithoutWorkers: Omit< - typeof configSchemaV4Obj.properties, - "workers" -> & - Mutable>> = { - ...configSchemaV4Obj.properties, -}; - -delete configSchemaV4ObjPropertiesWithoutWorkers.workers; - -const configSchemaV5Obj = { - ...configSchemaV4Obj, - properties: { - ...configSchemaV4ObjPropertiesWithoutWorkers, - version: { type: "integer", const: 5 }, - deals: { - description: - "A map of objects with worker names as keys, each object defines a deal", - type: "object", - nullable: true, - additionalProperties: { - ...workerConfigSchemaObj, - additionalProperties: false, - properties: { - ...workerConfigSchemaObj.properties, - ...dealSchemaObj.properties, - }, - }, - properties: { - dealName: { - ...workerConfigSchemaObj, - additionalProperties: false, - properties: { - ...workerConfigSchemaObj.properties, - ...dealSchemaObj.properties, - }, - }, - }, - required: [], - }, - hosts: { - description: - "A map of objects with worker names as keys, each object defines a list of peer IDs to host the worker on. Intended to be used by providers to deploy directly without using the blockchain", - type: "object", - nullable: true, - additionalProperties: { - ...workerConfigSchemaObj, - properties: { - ...workerConfigSchemaObj.properties, - ...hostConfigSchemaObj.properties, - }, - additionalProperties: false, - }, - properties: { - workerName: { - ...workerConfigSchemaObj, - properties: { - ...workerConfigSchemaObj.properties, - ...hostConfigSchemaObj.properties, - }, - additionalProperties: false, - }, - }, - required: [], - }, - }, - additionalProperties: false, -} as const; - -const configSchemaV5: JSONSchemaType = configSchemaV5Obj; - -type ConfigV6 = Omit & { - version: 6; - aquaDependencies: Record; - marineVersion?: string; - mreplVersion?: string; -}; - -const configSchemaV5ObjPropertiesWithoutDependencies: Omit< - typeof configSchemaV5Obj.properties, - "dependencies" -> & - Mutable>> = - { - ...configSchemaV5Obj.properties, - }; - -delete configSchemaV5ObjPropertiesWithoutDependencies.dependencies; - -const configSchemaV6Obj = { - ...configSchemaV5Obj, - properties: { - ...configSchemaV5ObjPropertiesWithoutDependencies, - version: { type: "integer", const: 6 }, - aquaDependencies: { - description: "A map of npm aqua dependency versions", - type: "object", - additionalProperties: { type: "string" }, - properties: { - "npm-aqua-dependency-name": { - type: "string", - description: - "Valid npm dependency version specification (check out https://docs.npmjs.com/cli/v10/configuring-npm/package-json#dependencies)", - }, - }, - required: [], - }, - marineVersion: { - type: "string", - description: "Marine version", - nullable: true, - }, - mreplVersion: { - type: "string", - description: "Mrepl version", - nullable: true, - }, - }, - additionalProperties: false, - required: [...configSchemaV5Obj.required, "aquaDependencies"], -} as const satisfies JSONSchemaType; - -const configSchemaV6: JSONSchemaType = configSchemaV6Obj; - -export type Constant = string | number | boolean; -export type Constants = Record; - -const constantSchema = { - type: ["string", "number", "boolean"], -} as const satisfies JSONSchemaType; - -export type CompileAquaConfig = Omit< - CompileFromPathArgs, - "imports" | "targetType" | "filePath" | "constants" | "logLevel" -> & { - input: string; - output: string; - target: "ts" | "js" | "air"; - logLevel?: AquaLogLevel; - constants?: Record; -}; - -export type CompileAqua = Record; - -type ConfigV7 = Omit< - ConfigV6, - "version" | "aquaInputPath" | "aquaOutputTSPath" | "aquaOutputJSPath" -> & { - version: 7; - [COMPILE_AQUA_PROPERTY_NAME]?: CompileAqua; -}; - -const configSchemaV6ObjPropertiesWithoutAqua: Omit< - typeof configSchemaV6Obj.properties, - "aquaInputPath" | "aquaOutputTSPath" | "aquaOutputJSPath" -> & - Mutable< - Partial< - Pick< - typeof configSchemaV6Obj.properties, - "aquaInputPath" | "aquaOutputTSPath" | "aquaOutputJSPath" - > - > - > = { - ...configSchemaV6Obj.properties, -}; - -delete configSchemaV6ObjPropertiesWithoutAqua.aquaOutputJSPath; -delete configSchemaV6ObjPropertiesWithoutAqua.aquaOutputTSPath; -delete configSchemaV6ObjPropertiesWithoutAqua.aquaInputPath; - -const compileAquaConfigSchema = { - type: "object", - properties: { - input: { - type: "string", - description: - "Relative path to the aqua file or directory with aqua files", - }, - output: { - type: "string", - description: "Relative path to the output directory", - }, - target: { - type: "string", - description: "Compilation target", - enum: ["ts", "js", "air"], - }, - constants: { - type: "object", - description: - "A list of constants to pass to the compiler. Constant name must be uppercase", - additionalProperties: constantSchema, - properties: { - SOME_CONSTANT: constantSchema, - }, - nullable: true, - required: [], - }, - logLevel: { - type: "string", - description: `Log level for the compiler. Default: info`, - nullable: true, - enum: AQUA_LOG_LEVELS, - }, - noRelay: { - type: "boolean", - description: - "Do not generate a pass through the relay node. Default: false", - nullable: true, - default: false, - }, - noXor: { - type: "boolean", - description: - "Do not generate a wrapper that catches and displays errors. Default: false", - nullable: true, - default: false, - }, - tracing: { - type: "boolean", - description: `Compile aqua in tracing mode (for debugging purposes). Default: false`, - nullable: true, - default: false, - }, - noEmptyResponse: { - type: "boolean", - description: - "Do not generate response call if there are no returned values. Default: false", - nullable: true, - default: false, - }, - }, - additionalProperties: false, - required: ["input", "output", "target"], -} as const satisfies JSONSchemaType; - -const configSchemaV7Obj = { - ...configSchemaV6Obj, - properties: { - ...configSchemaV6ObjPropertiesWithoutAqua, - version: { type: "integer", const: 7 }, - [COMPILE_AQUA_PROPERTY_NAME]: { - type: "object", - description: "A map of aqua files to compile", - additionalProperties: compileAquaConfigSchema, - properties: { - "aqua-config-name": compileAquaConfigSchema, - }, - required: [], - nullable: true, - }, - }, -} as const satisfies JSONSchemaType; - -const configSchemaV7: JSONSchemaType = configSchemaV7Obj; - -const configSchemaV7ObjPropertiesWithoutDeals: Omit< - typeof configSchemaV7Obj.properties, - "deals" -> & - Mutable>> = { - ...configSchemaV7Obj.properties, -}; - -delete configSchemaV7ObjPropertiesWithoutDeals.deals; - -type ConfigV8 = Omit & { - version: 8; - deployments?: ConfigV7["deals"]; - rustToolchain?: string; -}; - -const configSchemaV8Obj = { - ...configSchemaV7Obj, - properties: { - ...configSchemaV7ObjPropertiesWithoutDeals, - version: { type: "integer", const: 8 }, - deployments: { - ...configSchemaV7Obj.properties.deals, - description: - "A map with deployment names as keys and deployments as values", - properties: { - deploymentName: configSchemaV7Obj.properties.deals.properties.dealName, - }, - }, - rustToolchain: { - type: "string", - description: `Rust toolchain to use for building the project. By default ${versions["rust-toolchain"]} is used`, - nullable: true, - }, - }, -} as const satisfies JSONSchemaType; - -const configSchemaV8: JSONSchemaType = configSchemaV8Obj; - -type Deployment = Omit & { - pricePerCuPerEpoch?: string; - cuCountPerWorker?: number; -}; - -type ConfigV9 = Omit & { - version: 9; - deployments?: Record; -}; - -const dealObjWithoutPricePerWorkerEpoch: Omit< - typeof configSchemaV8Obj.properties.deployments.properties.deploymentName.properties, - "pricePerWorkerEpoch" -> & - Mutable< - Partial< - Pick< - typeof configSchemaV8Obj.properties.deployments.properties.deploymentName.properties, - "pricePerWorkerEpoch" - > - > - > = { - ...configSchemaV8Obj.properties.deployments.properties.deploymentName - .properties, -}; - -delete dealObjWithoutPricePerWorkerEpoch.pricePerWorkerEpoch; - -const deploymentSchemaObj = { - description: "Deployment config", - ...dealSchemaObj, - properties: { - ...dealObjWithoutPricePerWorkerEpoch, - pricePerCuPerEpoch: { - type: "string", - description: `Price per compute unit per epoch in ${PT_SYMBOL}`, - nullable: true, - }, - cuCountPerWorker: { - type: "integer", - description: `Number of compute units per worker. Default: 1`, - nullable: true, - default: 1, - }, - }, -} as const satisfies JSONSchemaType; - -const deploymentSchema: JSONSchemaType = deploymentSchemaObj; - -const validateDeploymentSchema = ajv.compile(deploymentSchema); - -const configSchemaV9Obj = { - ...configSchemaV8Obj, - properties: { - ...configSchemaV8Obj.properties, - version: { type: "integer", const: 9 }, - deployments: { - ...configSchemaV8Obj.properties.deployments, - additionalProperties: deploymentSchema, - properties: { - deploymentName: deploymentSchema, - }, - }, - }, -} as const satisfies JSONSchemaType; - -const configSchemaV9: JSONSchemaType = configSchemaV9Obj; - -type ConfigV10 = Omit & { - version: 10; - customFluenceEnv?: Omit< - NonNullable, - "contractsEnv" - > & { - contractsEnv: ChainENV; - }; -}; - -const configSchemaV10Obj = { - ...configSchemaV9Obj, - properties: { - ...configSchemaV9Obj.properties, - version: { type: "integer", const: 10 }, - customFluenceEnv: { - ...configSchemaV9Obj.properties.customFluenceEnv, - properties: { - ...configSchemaV9Obj.properties.customFluenceEnv.properties, - contractsEnv: { - type: "string", - description: `Contracts environment to use for this fluence network to sign contracts on the blockchain`, - enum: [...CHAIN_ENV], - }, - }, - }, - }, -} as const satisfies JSONSchemaType; - -const latestSchemaObj = configSchemaV10Obj; - -const latestSchema: JSONSchemaType = { - $id: `${TOP_LEVEL_SCHEMA_ID}/${FLUENCE_CONFIG_FULL_FILE_NAME}`, - title: FLUENCE_CONFIG_FULL_FILE_NAME, - description: `Defines Fluence Project, most importantly - what exactly you want to deploy and how. You can use \`${CLI_NAME} init\` command to generate a template for new Fluence project`, - ...latestSchemaObj, -}; - -const getConfigOrConfigDirPath = () => { - return projectRootDir; -}; - -function getDefault(): string { - return `# Defines Fluence Project -# Most importantly - what exactly you want to deploy and how -# You can use \`fluence init\` command to generate a template for new Fluence project - -# config version -version: ${numToStr(latestSchemaObj.properties.version.const)} - -# A map of deployment names as keys and deployments as values -deployments: - ${DEFAULT_DEPLOYMENT_NAME}: - targetWorkers: ${numToStr(TARGET_WORKERS_DEFAULT)} # max amount of workers in the deal - pricePerCuPerEpoch: "${DEFAULT_PRICE_PER_CU_PER_EPOCH_DEVELOPER}" # price per compute unit per epoch in ${PT_SYMBOL} - cuCountPerWorker: 1 # number of compute units per worker. Default: 1 - services: [] # list of service names to be deployed to this worker - spells: [] # list of spell names to be deployed to this worker -# # initialBalance: "1.98" - -${yamlDiffPatch( - "", - {}, - { - aquaDependencies: versions.npm, - }, -)} - -# ${COMPILE_AQUA_PROPERTY_NAME}: -# # aqua config name -# aquaConfigName: -# # relative path to the aqua file or directory with aqua files -# input: src/aqua -# # relative path to the output directory -# output: src/aqua -# # compilation target -# target: ts -# # a list of constants to pass to the compiler. Constant name must be uppercase -# constants: -# SOME_CONSTANT: 1 -# # log level for the compiler. Default: info. Must be one of: ${aquaLogLevelsString} -# logLevel: info -# # do not generate a pass through the relay node. Default: false -# noRelay: false -# # do not generate a wrapper that catches and displays errors. Default: false -# noXor: false -# # compile aqua in tracing mode (for debugging purposes). Default: false -# tracing: false -# # do not generate response call if there are no returned values. Default: false -# noEmptyResponse: false - -# # A list of custom relay multiaddresses to use when connecting to Fluence network -# # A map with service names as keys and service configs as values. -# # Service names must start with a lowercase letter and contain only letters numbers and underscores. -# # You can use \`fluence service new\` or \`fluence service add\` command to add a service -# services: -# # service name -# myService: -# # Path to service directory, service config or URL to the tar.gz archive that contains the service -# get: "src/services/myService" -# # A map of modules that you want to override for this service -# overrideModules: -# # module name -# moduleName: -# # environment variables accessible by a particular module -# # with standard Rust env API like this: std::env::var(IPFS_ADDR_ENV_NAME) -# # Module environment variables could be examined with repl -# envs: -# ENV_VARIABLE: "env variable string value" -# -# # Set true to allow module to use the Marine SDK logger -# loggerEnabled: true -# -# # manages the logging targets, described in detail: https://fluence.dev/docs/marine-book/marine-rust-sdk/developing/logging#using-target-map -# loggingMask: 1 -# -# # ${MAX_HEAP_SIZE_DESCRIPTION} -# maxHeapSize: 1KiB -# -# # A map of binary executable files that module is allowed to call -# mountedBinaries: -# curl: "/usr/bin/curl" -# -# # A map of accessible files and their aliases. -# # Aliases should be used in Marine module development because it's hard to know the full path to a file -# volumes: -# alias: "some/alias/path" -# -# -# # A map with spell names as keys and spell configs as values -# spells: -# # spell name -# mySpell: -# # Path to spell config or directory with spell config -# get: "src/spells/mySpell" -# -# # overrides for the spell: -# -# # Path to Aqua file which contains an Aqua function that you want to use as a spell -# aquaFilePath: "src/spells/mySpell/spell.aqua" -# # Name of the Aqua function that you want to use as a spell -# function: main -# # A map of Aqua function arguments names as keys and arguments values as values. -# # These arguments will be passed to the spell function and will be stored in the key-value storage for this particular spell. -# initArgs: -# someArg: someArgStringValue -# # Trigger the spell execution periodically -# # If you want to disable this property by overriding it -# # pass an empty config for it like this: \`clock: {}\` -# clock: -# # How often the spell will be executed. -# # If set to 0, the spell will be executed only once. -# # If this value not provided at all - the spell will never be executed -# periodSec: 3 -# # How long to wait before the first execution in seconds. -# # If this property or \`startTimestamp\` not specified, periodic execution will start immediately. -# # WARNING! Currently your computer's clock is used to determine a final timestamp that is sent to the server. -# # If it is set to 0 - the spell will never be executed -# # This property conflicts with \`startTimestamp\`. You can specify only one of them -# startDelaySec: 1 -# # An ISO timestamp when the periodic execution should start. -# # If this property or \`startDelaySec\` not specified, periodic execution will start immediately. -# startTimestamp: '2023-07-06T23:59:59Z' -# # How long to wait before the last execution in seconds. -# # If this property or \`endTimestamp\` not specified, periodic execution will never end. -# # WARNING! Currently your computer's clock is used to determine a final timestamp that is sent to the server. -# # If it is in the past at the moment of spell creation - the spell will never be executed. -# # This property conflicts with \`endTimestamp\`. You can specify only one of them -# endDelaySec: 0 -# # An ISO timestamp when the periodic execution should end. -# # If this property or \`endDelaySec\` not specified, periodic execution will never end. -# # If it is in the past at the moment of spell creation on Rust peer - the spell will never be executed -# endTimestamp: '2023-07-06T23:59:59Z' -# -# -# # A list of paths to be considered by aqua compiler to be used as imports. -# # First dependency in the list has the highest priority -# # -# # Priority of imports is considered in the following order: -# # 1. imports from --import flags, -# # 2. imports from aquaImports property in ${FLUENCE_CONFIG_FULL_FILE_NAME} -# # 3. project's ${join(DOT_FLUENCE_DIR_NAME, AQUA_DIR_NAME)} dir -# # 4. aqua dependencies from ${FLUENCE_CONFIG_FULL_FILE_NAME} -# aquaImports: -# - "./node_modules" -# -# # The version of the CLI that is compatible with this project. -# # You can set this to enforce a particular set of versions of all fluence components -# cliVersion: ${CLIPackageJSON.version} -# -# # A map of npm aqua dependency versions -# # CLI ensures dependencies are installed each time you run aqua -# # There are also some dependencies that are installed by default (e.g. ${AQUA_LIB_NPM_DEPENDENCY}) -# # You can check default dependencies using \`fluence dep v --default\` -# # use \`fluence dep i\` to install project aqua dependencies -# aquaDependencies: -# "${AQUA_LIB_NPM_DEPENDENCY}": ${versions.npm[AQUA_LIB_NPM_DEPENDENCY]} -# -# # CLI ensures dependencies are installed each time you run commands that depend on Marine or Marine REPL -# # use \`fluence dep i\` to install marine and mrepl -# marineVersion: ${versions.cargo.marine} -# mreplVersion: ${versions.cargo.mrepl} -# -# # If you want to deploy your services to specific peerIds. Intended to be used by providers to deploy directly without using the blockchain -# hosts: -# # worker name -# ${DEFAULT_DEPLOYMENT_NAME}: -# peerIds: [] -# services: [] # list of service names to be deployed to this worker -# spells: [] # list of spell names to be deployed to this worker -# # Space separated \`cargo build\` flags and args to pass to marine build. Default: ${DEFAULT_MARINE_BUILD_ARGS} -# ${MARINE_BUILD_ARGS_PROPERTY}: '${DEFAULT_MARINE_BUILD_ARGS}' -# # IPFS multiaddress to use when uploading workers with '${CLI_NAME} deploy'. Default: ${DEFAULT_IPFS_ADDRESS} or ${LOCAL_IPFS_ADDRESS} if using local local env (for 'workers deploy' IPFS address provided by relay that you are connected to is used) -# ${IPFS_ADDR_PROPERTY}: '${DEFAULT_IPFS_ADDRESS}' -# # Secret key with this name will be used by default by js-client inside CLI to run Aqua code -# defaultSecretKeyName: ${AUTO_GENERATED} -# # Path to the directory where you want relays.json file to be generated. Must be relative to the project root dir. This file contains a list of relays to use when connecting to Fluence network and depends on the default environment that you use in your project -# relaysPath: relative/path -`; -} - -const validateConfigSchemaV0 = ajv.compile(configSchemaV0); -const validateConfigSchemaV1 = ajv.compile(configSchemaV1); -const validateConfigSchemaV2 = ajv.compile(configSchemaV2); -const validateConfigSchemaV3 = ajv.compile(configSchemaV3); -const validateConfigSchemaV4 = ajv.compile(configSchemaV4); -const validateConfigSchemaV5 = ajv.compile(configSchemaV5); -const validateConfigSchemaV6 = ajv.compile(configSchemaV6); -const validateConfigSchemaV7 = ajv.compile(configSchemaV7); -const validateConfigSchemaV8 = ajv.compile(configSchemaV8); -const validateConfigSchemaV9 = ajv.compile(configSchemaV9); - -const migrations: Migrations = [ - async (config: Config): Promise => { - if (!validateConfigSchemaV0(config)) { - throw new Error( - `Migration error. Errors: ${await validationErrorToString( - validateConfigSchemaV0.errors, - )}`, - ); - } - - const services = config.services.reduce>( - (acc, { name }): Record => { - return { - ...acc, - [name]: { - get: relative( - projectRootDir, - join(projectRootDir, "artifacts", name), - ), - }, - }; - }, - {}, - ); - - return { - version: 1, - services, - }; - }, - async (config: Config): Promise => { - if (!validateConfigSchemaV1(config)) { - throw new Error( - `Migration error. Errors: ${await validationErrorToString( - validateConfigSchemaV1.errors, - )}`, - ); - } - - return { - ...config, - version: 2, - }; - }, - async (config: Config): Promise => { - if (!validateConfigSchemaV2(config)) { - throw new Error( - `Migration error. Errors: ${await validationErrorToString( - validateConfigSchemaV2.errors, - )}`, - ); - } - - const { relays, chainNetwork, ...restConfig } = config; - - let customFluenceEnv: ConfigV3["customFluenceEnv"] | undefined; - - // if some kind of custom network was previously set - migrate it to the new format - if (Array.isArray(relays) || chainNetwork !== undefined) { - customFluenceEnv = { - contractsEnv: chainNetwork ?? "dar", - relays: - relays === undefined || typeof relays === "string" - ? await resolveDefaultRelays() - : relays, - }; - } - - return { - ...restConfig, - ...(customFluenceEnv === undefined ? {} : customFluenceEnv), - version: 3, - }; - }, - async (config: Config): Promise => { - if (!validateConfigSchemaV3(config)) { - throw new Error( - `Migration error. Errors: ${await validationErrorToString( - validateConfigSchemaV3.errors, - )}`, - ); - } - - const projectSecretsConfig = await initReadonlyProjectSecretsConfig(); - - await Promise.all( - (projectSecretsConfig?.keyPairs ?? []).map(({ name, secretKey }) => { - return writeSecretKey({ - name, - secretKey, - isUser: false, - }); - }), - ); - - if (projectSecretsConfig !== null) { - await rm(projectSecretsConfig.$getPath()); - } - - return { - ...config, - version: 4, - ...(projectSecretsConfig?.defaultKeyPairName === undefined - ? {} - : { defaultSecretKeyName: projectSecretsConfig.defaultKeyPairName }), - }; - }, - async (config: Config): Promise => { - if (!validateConfigSchemaV4(config)) { - throw new Error( - `Migration error. Errors: ${await validationErrorToString( - validateConfigSchemaV4.errors, - )}`, - ); - } - - const { workers, ...restConfig } = config; - - const res: ConfigV5 = { - ...restConfig, - version: 5, - }; - - if (res.hosts !== undefined) { - res.hosts = Object.fromEntries( - Object.entries(res.hosts).map(([k, v]) => { - const worker = workers?.[k] ?? {}; - return [k, { ...v, ...worker }] as const; - }), - ); - } - - if (res.deals !== undefined) { - res.deals = Object.fromEntries( - Object.entries(res.deals).map(([k, v]) => { - const worker = workers?.[k] ?? {}; - return [k, { ...v, ...worker }] as const; - }), - ); - } - - return res; - }, - async (config: Config): Promise => { - if (!validateConfigSchemaV5(config)) { - throw new Error( - `Migration error. Errors: ${await validationErrorToString( - validateConfigSchemaV5.errors, - )}`, - ); - } - - const { dependencies, ...restConfig } = config; - const marine = dependencies?.cargo?.["marine"]; - const mrepl = dependencies?.cargo?.["mrepl"]; - - const res: ConfigV6 = { - ...restConfig, - version: 6, - aquaDependencies: dependencies?.npm ?? versions.npm, - ...(marine === undefined ? {} : { marineVersion: marine }), - ...(mrepl === undefined ? {} : { mreplVersion: mrepl }), - }; - - return res; - }, - async (config: Config): Promise => { - if (!validateConfigSchemaV6(config)) { - throw new Error( - `Migration error. Errors: ${await validationErrorToString( - validateConfigSchemaV6.errors, - )}`, - ); - } - - const { aquaInputPath, aquaOutputTSPath, aquaOutputJSPath, ...restConfig } = - config; - - const res: ConfigV7 = { - ...restConfig, - version: 7, - }; - - if (aquaInputPath !== undefined) { - res.compileAqua = { - default: { - input: aquaInputPath, - output: - aquaOutputJSPath ?? - aquaOutputTSPath ?? - relative(projectRootDir, join("src", "compiled-aqua")), - target: aquaOutputJSPath === undefined ? "ts" : "js", - }, - }; - } - - return res; - }, - async (config: Config): Promise => { - if (!validateConfigSchemaV7(config)) { - throw new Error( - `Migration error. Errors: ${await validationErrorToString( - validateConfigSchemaV7.errors, - )}`, - ); - } - - return { - ...config, - version: 8, - deployments: config.deals, - }; - }, - async (config: Config): Promise => { - if (!validateConfigSchemaV8(config)) { - throw new Error( - `Migration error. Errors: ${await validationErrorToString( - validateConfigSchemaV8.errors, - )}`, - ); - } - - const { deployments, ...restConfig } = config; - - return { - ...restConfig, - version: 9, - ...(deployments === undefined - ? {} - : { - deployments: Object.fromEntries( - Object.entries(deployments).map( - ([k, { pricePerWorkerEpoch, ...v }]) => { - return [ - k, - { - ...v, - ...(pricePerWorkerEpoch === undefined - ? {} - : { pricePerCuPerEpoch: pricePerWorkerEpoch }), - }, - ] as const; - }, - ), - ), - }), - }; - }, - async (config: Config): Promise => { - if (!validateConfigSchemaV9(config)) { - throw new Error( - `Migration error. Errors: ${await validationErrorToString( - validateConfigSchemaV9.errors, - )}`, - ); - } - - const { customFluenceEnv, ...rest } = config; - - if (customFluenceEnv !== undefined) { - const envConfig = await initNewEnvConfig(); - envConfig.relays = customFluenceEnv.relays; - - envConfig.fluenceEnv = - customFluenceEnv.contractsEnv === "kras" - ? "mainnet" - : customFluenceEnv.contractsEnv === "dar" - ? "testnet" - : customFluenceEnv.contractsEnv; - - commandObj.warn( - `Custom fluence env migrated from ${FLUENCE_CONFIG_FULL_FILE_NAME} to ${DOT_FLUENCE_DIR_NAME}/${ENV_CONFIG_FULL_FILE_NAME}`, - ); - } - - return { ...rest, version: 10 }; - }, -]; - -type Config = - | ConfigV0 - | ConfigV1 - | ConfigV2 - | ConfigV3 - | ConfigV4 - | ConfigV5 - | ConfigV6 - | ConfigV7 - | ConfigV8 - | ConfigV9 - | ConfigV10; -type LatestConfig = ConfigV10; -export type FluenceConfig = InitializedConfig; -export type FluenceConfigReadonly = InitializedReadonlyConfig; -export type FluenceConfigWithServices = FluenceConfig & { - services: NonNullable; -}; - -export function isFluenceConfigWithServices( - config: FluenceConfig, -): config is FluenceConfigWithServices { - return "services" in config; -} - -const checkDuplicatesAndPresence = ( - fluenceConfig: Pick< - FluenceConfig, - "spells" | "services" | "hosts" | "deployments" - >, - servicesOrSpells: "services" | "spells", -) => { - const servicesOrSpellsSet = new Set( - Object.keys(fluenceConfig[servicesOrSpells] ?? {}).flatMap( - (serviceOrSpellName) => { - return serviceOrSpellName; - }, - ), - ); - - const hostsValidity = Object.entries(fluenceConfig.hosts ?? {}).reduce< - string | true - >((acc, [workerName, workerConfig]) => { - return checkDuplicatesAndPresenceImplementation({ - workerConfig, - servicesOrSpells, - servicesOrSpellsSet, - acc, - workerName, - }); - }, true); - - if (typeof hostsValidity === "string") { - return hostsValidity; - } - - return Object.entries(fluenceConfig.deployments ?? {}).reduce( - (acc, [workerName, workerConfig]) => { - return checkDuplicatesAndPresenceImplementation({ - workerConfig, - servicesOrSpells, - servicesOrSpellsSet, - acc, - workerName, - }); - }, - true, - ); -}; - -type CheckDuplicatesAndPresenceImplementationArgs = { - workerConfig: Deal & Worker; - servicesOrSpells: "services" | "spells"; - servicesOrSpellsSet: Set; - acc: string | boolean; - workerName: string; -}; - -function checkDuplicatesAndPresenceImplementation({ - workerConfig, - servicesOrSpells, - servicesOrSpellsSet, - acc, - workerName, -}: CheckDuplicatesAndPresenceImplementationArgs) { - const workerServicesOrSpells = workerConfig[servicesOrSpells] ?? []; - const workerServicesOrSpellsSet = new Set(workerServicesOrSpells); - - const notListedInFluenceYAML = workerServicesOrSpells.filter( - (serviceName) => { - return !servicesOrSpellsSet.has(serviceName); - }, - ); - - const maybePreviousError = typeof acc === "string" ? acc : null; - - const maybeNotListedError = - notListedInFluenceYAML.length !== 0 - ? `Worker ${color.yellow( - workerName, - )} has ${servicesOrSpells} that are not listed in ${color.yellow( - servicesOrSpells, - )} property in ${FLUENCE_CONFIG_FULL_FILE_NAME}: ${color.yellow( - [...new Set(notListedInFluenceYAML)].join(", "), - )}` - : null; - - const maybeHasDuplicatesError = - workerServicesOrSpellsSet.size !== workerServicesOrSpells.length - ? `Worker ${color.yellow( - workerName, - )} has duplicated ${servicesOrSpells} in ${FLUENCE_CONFIG_FULL_FILE_NAME}: ${color.yellow( - workerServicesOrSpells.filter((serviceName, index) => { - return workerServicesOrSpells.indexOf(serviceName) !== index; - }), - )}` - : null; - - const errors = [ - maybePreviousError, - maybeNotListedError, - maybeHasDuplicatesError, - ].filter((error): error is string => { - return error !== null; - }); - - return errors.length === 0 ? true : errors.join("\n"); -} - -function validateCompileAquaPathsAreRelative(config: LatestConfig) { - const compileAqua = config[COMPILE_AQUA_PROPERTY_NAME]; - - if (compileAqua === undefined) { - return true; - } - - const [absolutePathErrors] = splitErrorsAndResults( - Object.entries(compileAqua), - ([name, { input, output }]) => { - if (isAbsolute(input)) { - return { - error: `'${COMPILE_AQUA_PROPERTY_NAME}.${name}.input' must be a relative path, got ${input}`, - }; - } - - if (isAbsolute(output)) { - return { - error: `'${COMPILE_AQUA_PROPERTY_NAME}.${name}.output' must be a relative path, got ${output}`, - }; - } - - return { result: { input, output, name } }; - }, - ); - - return absolutePathErrors.length === 0 ? true : absolutePathErrors.join("\n"); -} - -function validateNotBothBlacklistAndWhitelist( - config: LatestConfig, -): string | true { - const errors = Object.entries(config.deployments ?? {}) - .map(([deploymentName, { blacklist, whitelist }]) => { - if (blacklist !== undefined && whitelist !== undefined) { - return `Both ${color.yellow("blacklist")} and ${color.yellow( - "whitelist", - )} are set for deployment ${color.yellow( - deploymentName, - )}. Only one of them should be set`; - } - - return true; - }) - .filter((error): error is string => { - return error !== true; - }); - - if (errors.length !== 0) { - return errors.join("\n"); - } - - return true; -} - -async function validateProtocolVersions(config: LatestConfig) { - const errors = ( - await Promise.all( - Object.entries(config.deployments ?? {}) - .map(([deployment, { protocolVersion }]) => { - return { - deployment, - protocolVersion, - }; - }) - .filter((v): v is { deployment: string; protocolVersion: number } => { - return v.protocolVersion !== undefined; - }) - .map(async ({ deployment, protocolVersion }) => { - return { - validity: await validateProtocolVersion(protocolVersion), - deployment, - }; - }), - ) - ).filter((v): v is { validity: string; deployment: string } => { - return v.validity !== true; - }); - - if (errors.length !== 0) { - return errors - .map(({ deployment, validity }) => { - return `Deployment ${color.yellow( - deployment, - )} has invalid protocol version: ${color.yellow(validity)}`; - }) - .join("\n"); - } - - return true; -} - -function warnComputeUnitsIsDeprecated(config: LatestConfig): true { - const deploymentsWithWarning = Object.entries( - config.deployments ?? {}, - ).filter(([, deployment]) => { - return "computeUnits" in deployment; - }); - - if (deploymentsWithWarning.length > 0) { - commandObj.warn( - `'computeUnits' property is deprecated. Use 'cuCountPerWorker' instead. Deployment(s): ${deploymentsWithWarning - .map(([deploymentName]) => { - return deploymentName; - }) - .join(", ")}`, - ); - } - - return true; -} - -const validate: ConfigValidateFunction = async (config) => { - return validateBatchAsync( - validateNotBothBlacklistAndWhitelist(config), - validateCIDs( - Object.entries(config.deployments ?? {}).flatMap( - ([name, { effectors }]) => { - return (effectors ?? []).map((cid) => { - return { - cid, - location: `${FLUENCE_CONFIG_FULL_FILE_NAME} > deployments > ${name} > effectors > ${cid}`, - }; - }); - }, - ), - ), - validateCompileAquaPathsAreRelative(config), - checkDuplicatesAndPresence(config, "services"), - checkDuplicatesAndPresence(config, "spells"), - validateVersionsIsExact("marineVersion", config.marineVersion), - validateVersionsIsExact("mreplVersion", config.mreplVersion), - validateProtocolVersions(config), - warnComputeUnitsIsDeprecated(config), - ); -}; - -const initConfigOptions: InitConfigOptions = { - allSchemas: [ - configSchemaV0, - configSchemaV1, - configSchemaV2, - configSchemaV3, - configSchemaV4, - configSchemaV5, - configSchemaV6, - configSchemaV7, - configSchemaV8, - configSchemaV9, - latestSchema, - ], - latestSchema, - migrations, - name: FLUENCE_CONFIG_FILE_NAME, - getConfigOrConfigDirPath, - getSchemaDirPath: getFluenceDir, - validate, -}; - -export const initFluenceConfigWithPath = async ( - path: string, -): Promise | null> => { - return getConfigInitFunction({ - ...initConfigOptions, - getConfigOrConfigDirPath: () => { - return path; - }, - })(); -}; - -export const initReadonlyFluenceConfigWithPath = async ( - path: string, -): Promise | null> => { - return getReadonlyConfigInitFunction({ - ...initConfigOptions, - getConfigOrConfigDirPath: () => { - return path; - }, - })(); -}; - -export const initNewFluenceConfig = getConfigInitFunction( - initConfigOptions, - getDefault, -); -export const initFluenceConfig = getConfigInitFunction(initConfigOptions); -export const initReadonlyFluenceConfig = - getReadonlyConfigInitFunction(initConfigOptions); - -export const fluenceSchema: JSONSchemaType = latestSchema; diff --git a/packages/cli/package/src/lib/configs/project/module.ts b/packages/cli/package/src/lib/configs/project/module.ts deleted file mode 100644 index d1efe2f00..000000000 --- a/packages/cli/package/src/lib/configs/project/module.ts +++ /dev/null @@ -1,393 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import type { JSONSchemaType } from "ajv"; - -import { ajv, validationErrorToString } from "../../ajvInstance.js"; -import { BYTES_PATTERN, MAX_HEAP_SIZE_DESCRIPTION } from "../../const.js"; -import { - type ModuleType, - MODULE_CONFIG_FULL_FILE_NAME, - MODULE_TYPES, - MODULE_TYPE_COMPILED, - MODULE_TYPE_RUST, - TOP_LEVEL_SCHEMA_ID, - MODULE_CONFIG_FILE_NAME, - CLI_NAME, -} from "../../const.js"; -import { ensureModuleAbsolutePath } from "../../helpers/downloadFile.js"; -import { getFluenceDir } from "../../paths.js"; -import { - getReadonlyConfigInitFunction, - type InitConfigOptions, - type InitializedConfig, - type InitializedReadonlyConfig, - type Migrations, - type GetDefaultConfig, - getConfigInitFunction, -} from "../initConfig.js"; - -type OverridableModulePropertiesV0 = { - maxHeapSize?: string; - loggerEnabled?: boolean; - loggingMask?: number; - volumes?: Record; - envs?: Record; - mountedBinaries?: Record; -}; - -export type ConfigV0 = { - version: 0; - name: string; - type?: ModuleType; - cid?: string; - rustBindingCrate?: string; -} & OverridableModulePropertiesV0; - -const overridableModulePropertiesV0 = { - maxHeapSize: { - type: "string", - nullable: true, - pattern: BYTES_PATTERN, - description: MAX_HEAP_SIZE_DESCRIPTION, - }, - loggerEnabled: { - type: "boolean", - nullable: true, - description: "Set true to allow module to use the Marine SDK logger", - }, - loggingMask: { - type: "integer", - nullable: true, - description: - "manages the logging targets, described in detail: https://fluence.dev/docs/marine-book/marine-rust-sdk/developing/logging#using-target-map", - }, - volumes: { - type: "object", - nullable: true, - required: [], - title: "Volumes", - additionalProperties: { type: "string" }, - properties: { - Alias: { type: "string", description: "path" }, - }, - description: - "A map of accessible files and their aliases. Aliases should be used in Marine module development because it's hard to know the full path to a file", - }, - envs: { - type: "object", - title: "Environment variables", - nullable: true, - required: [], - additionalProperties: { - type: "string", - }, - properties: { - Environment_variable_name: { - type: "string", - description: "Environment variable value", - }, - }, - description: - "environment variables accessible by a particular module with standard Rust env API like this: std::env::var(IPFS_ADDR_ENV_NAME). Please note that Marine adds three additional environment variables. Module environment variables could be examined with repl", - }, - mountedBinaries: { - title: "Mounted binaries", - type: "object", - additionalProperties: { - type: "string", - }, - properties: { - Mounted_binary_name: { - type: "string", - description: "Path to a mounted binary", - }, - }, - nullable: true, - required: [], - description: - "A map of binary executable files that module is allowed to call. Example: curl: /usr/bin/curl", - }, - cid: { - description: "CID of the module when it was packed", - type: "string", - nullable: true, - }, - rustBindingCrate: { - description: - "Name of the interface crate that should be used with this module", - type: "string", - nullable: true, - }, -} as const; - -const configSchemaV0: JSONSchemaType = { - type: "object", - description: `!IMPORTANT: All the properties in this config (except for "name") are relevant only for providers who provide effector modules. If you are not a provider - properties in this config will be ignored when you deploy your code. But they will still have effect when running using 'fluence service repl' command. This config defines [Marine Module](https://fluence.dev/docs/build/concepts/#modules). You can use \`${CLI_NAME} module new\` command to generate a template for new module`, - properties: { - name: { - type: "string", - description: `"name" property from the Cargo.toml (for module type "${MODULE_TYPE_RUST}") or name of the precompiled .wasm file (for module type "${MODULE_TYPE_COMPILED}")`, - }, - type: { - type: "string", - enum: MODULE_TYPES, - nullable: true, - default: MODULE_TYPE_COMPILED, - description: `Module type "${MODULE_TYPE_COMPILED}" is for the precompiled modules. Module type "${MODULE_TYPE_RUST}" is for the source code written in rust which can be compiled into a Marine module`, - }, - ...overridableModulePropertiesV0, - version: { type: "integer", const: 0 }, - }, - additionalProperties: false, - required: ["version", "name"], -}; - -type Effects = { - binaries?: Record; -}; - -const effectsSchema = { - type: "object", - nullable: true, - description: - "Effects configuration. Only providers can allow and control effector modules by changing the nox configuration. Properties in this config are ignored when you deploy your code", - properties: { - binaries: { - type: "object", - additionalProperties: { - type: "string", - }, - properties: { - "binary-name": { - type: "string", - description: "Path to a binary", - }, - }, - required: [], - nullable: true, - description: - "A map of binary executable files that module is allowed to call. Example: curl: /usr/bin/curl", - }, - }, - additionalProperties: false, - required: [], -} as const satisfies JSONSchemaType; - -type Repl = { - loggerEnabled?: boolean; - loggingMask?: number; -}; - -const replSchema = { - type: "object", - nullable: true, - description: - "REPL configuration. Properties in this config are ignored when you deploy your code", - properties: { - loggerEnabled: { - type: "boolean", - nullable: true, - description: "Set true to allow module to use the Marine SDK logger", - }, - loggingMask: { - type: "number", - nullable: true, - description: - "manages the logging targets, that are described in detail here: https://fluence.dev/docs/marine-book/marine-rust-sdk/developing/logging#using-target-map", - }, - }, - additionalProperties: false, - required: [], -} as const satisfies JSONSchemaType; - -type OverridableModulePropertiesV1 = { - effects?: Effects; - repl?: Repl; -}; - -const overridableModulePropertiesV1 = { - type: "object", - properties: { - effects: effectsSchema, - repl: replSchema, - }, - additionalProperties: false, - required: [], -} as const satisfies JSONSchemaType; - -type ConfigV1 = { - version: 1; - name: string; - type?: ModuleType; - cid?: string; - rustBindingCrate?: { - name: string; - version: string; - }; - effects?: Effects; - repl?: Repl; -}; - -const configSchemaV1: JSONSchemaType = { - $id: `${TOP_LEVEL_SCHEMA_ID}/${MODULE_CONFIG_FULL_FILE_NAME}`, - title: MODULE_CONFIG_FULL_FILE_NAME, - type: "object", - description: `Defines Marine Module. You can use \`${CLI_NAME} module new\` command to generate a template for new module`, - properties: { - name: { - type: "string", - description: `"name" property from the Cargo.toml (for module type "${MODULE_TYPE_RUST}") or name of the precompiled .wasm file (for module type "${MODULE_TYPE_COMPILED}")`, - }, - type: { - type: "string", - enum: MODULE_TYPES, - nullable: true, - default: MODULE_TYPE_COMPILED, - description: `Default: ${MODULE_TYPE_COMPILED}. Module type "${MODULE_TYPE_RUST}" is for the source code written in rust which can be compiled into a Marine module. Module type "${MODULE_TYPE_COMPILED}" is for the precompiled modules.`, - }, - version: { type: "integer", const: 1 }, - cid: { - description: "CID of the module when it was packed", - type: "string", - nullable: true, - }, - rustBindingCrate: { - type: "object", - properties: { - name: { type: "string" }, - version: { type: "string" }, - }, - nullable: true, - required: ["name", "version"], - description: "Interface crate that can be used with this module", - }, - ...overridableModulePropertiesV1.properties, - }, - additionalProperties: false, - required: ["version", "name"], -}; - -const validateConfigSchemaV0 = ajv.compile(configSchemaV0); - -const migrations: Migrations = [ - async (config: Config): Promise => { - if (!validateConfigSchemaV0(config)) { - throw new Error( - `Migration error. Errors: ${await validationErrorToString( - validateConfigSchemaV0.errors, - )}`, - ); - } - - return { - version: 1, - name: config.name, - ...(config.type === undefined ? {} : { type: config.type }), - ...(config.mountedBinaries === undefined - ? {} - : { effects: { binaries: config.mountedBinaries } }), - ...(config.loggerEnabled === undefined && config.loggingMask === undefined - ? {} - : { - repl: { - ...(config.loggerEnabled === undefined - ? {} - : { loggerEnabled: config.loggerEnabled }), - ...(config.loggingMask === undefined - ? {} - : { loggingMask: config.loggingMask }), - }, - }), - }; - }, -]; - -export type OverridableModuleProperties = OverridableModulePropertiesV1; - -type Config = ConfigV0 | ConfigV1; -type LatestConfig = ConfigV1; -export type ModuleConfig = InitializedConfig; -export type ModuleConfigReadonly = InitializedReadonlyConfig; - -const getInitConfigOptions = ( - configPath: string, -): InitConfigOptions => { - return { - allSchemas: [configSchemaV0, configSchemaV1], - latestSchema: configSchemaV1, - migrations, - name: MODULE_CONFIG_FILE_NAME, - getSchemaDirPath: getFluenceDir, - getConfigOrConfigDirPath: (): string => { - return configPath; - }, - }; -}; - -export const initReadonlyModuleConfig = async ( - configOrConfigDirPathOrUrl: string, - absolutePath?: string, -): Promise | null> => { - return getReadonlyConfigInitFunction( - getInitConfigOptions( - await ensureModuleAbsolutePath(configOrConfigDirPathOrUrl, absolutePath), - ), - )(); -}; - -const getDefault: (name: string) => GetDefaultConfig = ( - name: string, -): GetDefaultConfig => { - return () => { - return `# Module type "rust" is for the source code written in rust which can be compiled into a Marine module - -# config versions -version: 0 - -# Module type "compiled" is for the precompiled modules. -type: ${MODULE_TYPE_RUST} # default: "compiled" - -# "name" property from the Cargo.toml (for module type "rust") -# or name of the precompiled .wasm file (for module type "compiled") -name: ${name} -`; - }; -}; - -export const initNewReadonlyModuleConfig = ( - configPath: string, - name: string, -): Promise | null> => { - return getReadonlyConfigInitFunction( - getInitConfigOptions(configPath), - getDefault(name), - )(); -}; - -export const initNewModuleConfig = ( - configPath: string, - name: string, -): Promise> => { - return getConfigInitFunction( - getInitConfigOptions(configPath), - getDefault(name), - )(); -}; - -export const moduleSchema: JSONSchemaType = configSchemaV1; -export const overridableModuleProperties = overridableModulePropertiesV1; diff --git a/packages/cli/package/src/lib/configs/project/projectSecrets.ts b/packages/cli/package/src/lib/configs/project/projectSecrets.ts deleted file mode 100644 index fda43b1ad..000000000 --- a/packages/cli/package/src/lib/configs/project/projectSecrets.ts +++ /dev/null @@ -1,118 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import { color } from "@oclif/color"; -import type { JSONSchemaType } from "ajv"; - -import { - CLI_NAME, - FLUENCE_CONFIG_FULL_FILE_NAME, - PROJECT_SECRETS_CONFIG_FILE_NAME, - PROJECT_SECRETS_FULL_CONFIG_FILE_NAME, - TOP_LEVEL_SCHEMA_ID, -} from "../../const.js"; -import { - validateHasDefault, - validateBatch, - validateUnique, - type ValidationResult, -} from "../../helpers/validations.js"; -import { getFluenceDir } from "../../paths.js"; -import { - getReadonlyConfigInitFunction, - type InitConfigOptions, - type InitializedConfig, - type InitializedReadonlyConfig, - type Migrations, -} from "../initConfig.js"; -import { type ConfigKeyPair, configKeyPairSchema } from "../keyPair.js"; - -type ConfigV0 = { - version: 0; - keyPairs: Array; - defaultKeyPairName?: string; -}; - -const configSchemaV0: JSONSchemaType = { - $id: `${TOP_LEVEL_SCHEMA_ID}/${PROJECT_SECRETS_FULL_CONFIG_FILE_NAME}`, - title: PROJECT_SECRETS_FULL_CONFIG_FILE_NAME, - type: "object", - description: `Defines project's secret keys that are used only in the scope of this particular Fluence project. You can manage project's keys using commands from \`${CLI_NAME} key\` group of commands`, - properties: { - keyPairs: { - title: "Key Pairs", - description: "Key Pairs available for the particular project", - type: "array", - items: configKeyPairSchema, - }, - defaultKeyPairName: { - type: "string", - nullable: true, - description: `Key pair with this name will be used for the deployment by default. You can override it with flags or by using keyPair properties in ${FLUENCE_CONFIG_FULL_FILE_NAME}`, - }, - version: { type: "integer", const: 0 }, - }, - required: ["version", "keyPairs"], -}; - -const migrations: Migrations = []; - -const validate = (config: LatestConfig): ValidationResult => { - return validateBatch( - validateUnique( - config.keyPairs, - ({ name }): string => { - return name; - }, - (name): string => { - return `There are multiple key-pairs with the same name ${color.yellow( - name, - )}`; - }, - ), - typeof config.defaultKeyPairName === "string" - ? validateHasDefault( - config.keyPairs, - config.defaultKeyPairName, - ({ name }): string => { - return name; - }, - `Default key-pair ${color.yellow( - config.defaultKeyPairName, - )} not found`, - ) - : true, - ); -}; - -type Config = ConfigV0; -type LatestConfig = ConfigV0; -export type ProjectSecretsConfig = InitializedConfig; -export type ProjectSecretsConfigReadonly = - InitializedReadonlyConfig; - -const initConfigOptions: InitConfigOptions = { - allSchemas: [configSchemaV0], - latestSchema: configSchemaV0, - migrations, - name: PROJECT_SECRETS_CONFIG_FILE_NAME, - getConfigOrConfigDirPath: getFluenceDir, - validate, -}; - -export const initReadonlyProjectSecretsConfig = - getReadonlyConfigInitFunction(initConfigOptions); diff --git a/packages/cli/package/src/lib/configs/project/provider/provider.ts b/packages/cli/package/src/lib/configs/project/provider/provider.ts index 7e575b991..4ae86faeb 100644 --- a/packages/cli/package/src/lib/configs/project/provider/provider.ts +++ b/packages/cli/package/src/lib/configs/project/provider/provider.ts @@ -18,43 +18,42 @@ import { writeFile } from "fs/promises"; import { join } from "path"; -import { type JsonMap } from "@iarna/toml"; -import kebabCase from "lodash-es/kebabCase.js"; -import mapKeys from "lodash-es/mapKeys.js"; -import snakeCase from "lodash-es/snakeCase.js"; import times from "lodash-es/times.js"; -import { type ChainENV } from "../../../../common.js"; -import { ajv } from "../../../ajvInstance.js"; +import { + getChainId, + getIpfsGateway, + getRpcUrl, +} from "../../../chain/chainConfig.js"; +import { hexStringToUTF8ToBase64String } from "../../../chain/conversions.js"; +import { peerIdBase58ToHexString } from "../../../chain/conversions.js"; import { commandObj, isInteractive } from "../../../commandObj.js"; import { DEFAULT_OFFER_NAME, PROVIDER_CONFIG_FULL_FILE_NAME, - FS_OPTIONS, TCP_PORT_START, WEB_SOCKET_PORT_START, - TOML_EXT, defaultNumberProperties, DEFAULT_CC_DURATION, DEFAULT_CC_STAKER_REWARD, - DEFAULT_NUMBER_OF_COMPUTE_UNITS_ON_NOX, - DEFAULT_CURL_EFFECTOR_CID, + DEFAULT_NUMBER_OF_COMPUTE_UNITS_ON_PEER, CLI_NAME, - DEFAULT_NUMBER_OF_LOCAL_NET_NOXES, - DEFAULT_VM_EFFECTOR_CID, + DEFAULT_NUMBER_OF_LOCAL_NET_PEERS, + WS_CHAIN_URLS, } from "../../../const.js"; +import { resolveDeployment } from "../../../dealClient.js"; import { ensureChainEnv } from "../../../ensureChainNetwork.js"; import { type ProviderConfigArgs } from "../../../generateUserProviderConfig.js"; +import { genManifest } from "../../../genManifest.js"; import { getPeerIdFromSecretKey } from "../../../helpers/getPeerIdFromSecretKey.js"; import { numToStr } from "../../../helpers/typesafeStringify.js"; import { splitErrorsAndResults } from "../../../helpers/utils.js"; import { genSecretKeyOrReturnExisting } from "../../../keyPairs.js"; import { - ensureFluenceConfigsDir, getProviderConfigPath, getFluenceDir, ensureFluenceSecretsFilePath, - ensureFluenceCCPConfigsDir, + ensureK8sManifestsDir, } from "../../../paths.js"; import { input } from "../../../prompt.js"; import { getConfigInitFunction } from "../../initConfigNew.js"; @@ -64,23 +63,11 @@ import { initNewProviderSecretsConfig } from "../providerSecrets/providerSecrets import configOptions0, { type Config as Config0 } from "./provider0.js"; import configOptions1, { - DEFAULT_LOG_LEVEL, - DEFAULT_PROMETHEUS_ENDPOINT_HOST, - DEFAULT_PROMETHEUS_ENDPOINT_PORT, - DEFAULT_REPORT_HASHRATE, - DEFAULT_RPC_ENDPOINT_HOST, - DEFAULT_RPC_ENDPOINT_PORT, - type CCPConfigYAML, type ComputePeer, type Config as Config1, - type NoxConfigYAML, } from "./provider1.js"; import configOptions2, { type Config as Config2 } from "./provider2.js"; -import configOptions3, { - mergeConfigYAMLWithRawConfig, - resolveNoxConfigYAML, - type Config as Config3, -} from "./provider3.js"; +import configOptions3, { type Config as Config3 } from "./provider3.js"; export const options: InitConfigOptions = { description: "Defines config used for provider set up", @@ -91,24 +78,14 @@ export const options: InitConfigOptions = { export type ProviderConfig = Awaited>; -const ipValidator = ajv.compile({ - type: "string", - format: "ipv4", -}); - -function validateIp(value: string) { - return ipValidator(value) ? true : "Must be a valid IPv4 address"; -} - function getDefault(args: ProviderConfigArgs) { return async () => { const chainEnv = await ensureChainEnv(); await initNewEnvConfig(chainEnv); const isLocal = chainEnv === "local"; - const hasVM = !isLocal && args["no-vm"] !== true; - const numberOfNoxes = - args.noxes ?? + const numberOfPeers = + args.peers ?? (isInteractive && !isLocal ? Number( await input({ @@ -120,33 +97,15 @@ function getDefault(args: ProviderConfigArgs) { }, }), ) - : DEFAULT_NUMBER_OF_LOCAL_NET_NOXES); + : DEFAULT_NUMBER_OF_LOCAL_NET_PEERS); const computePeerEntries: [string, ComputePeer][] = []; - for (const i of times(numberOfNoxes)) { - const peerConfig = hasVM - ? { - nox: { - vm: { - network: { - publicIp: isInteractive - ? await input({ - message: `Enter public IP address for nox-${numToStr(i)}`, - validate: validateIp, - }) - : "", - }, - }, - }, - } - : {}; - + for (const i of times(numberOfPeers)) { computePeerEntries.push([ - `nox-${numToStr(i)}`, + `peer-${numToStr(i)}`, { - computeUnits: DEFAULT_NUMBER_OF_COMPUTE_UNITS_ON_NOX, - ...peerConfig, + computeUnits: DEFAULT_NUMBER_OF_COMPUTE_UNITS_ON_PEER, }, ] as const); } @@ -155,30 +114,17 @@ function getDefault(args: ProviderConfigArgs) { return { providerName: "defaultProvider", - nox: { - effectors: { - curl: { - wasmCID: DEFAULT_CURL_EFFECTOR_CID, - allowedBinaries: { curl: "/usr/bin/curl" }, - }, - ...(hasVM ? { vm: { wasmCID: DEFAULT_VM_EFFECTOR_CID } } : {}), - }, - }, computePeers, offers: { [DEFAULT_OFFER_NAME]: { ...defaultNumberProperties, computePeers: Object.keys(computePeers), - effectors: [ - DEFAULT_CURL_EFFECTOR_CID, - ...(hasVM ? [DEFAULT_VM_EFFECTOR_CID] : []), - ], }, }, capacityCommitments: Object.fromEntries( - Object.keys(computePeers).map((noxName) => { + Object.keys(computePeers).map((peerName) => { return [ - noxName, + peerName, { duration: DEFAULT_CC_DURATION, stakerReward: DEFAULT_CC_STAKER_REWARD, @@ -208,163 +154,6 @@ export async function ensureReadonlyProviderConfig() { return providerConfig; } -function resolveCCPConfigYAML( - globalCCPConfig: CCPConfigYAML | undefined = {}, - computePeerCCPConfig: CCPConfigYAML | undefined = {}, -) { - const config = mergeConfigYAMLWithRawConfig( - getDefaultCCPConfigYAML(), - globalCCPConfig, - ); - - return mergeConfigYAMLWithRawConfig(config, computePeerCCPConfig); -} - -function getObjByKey(obj: Record, key: string): object { - if (!(key in obj)) { - return {}; - } - - const value = obj[key]; - return typeof value === "object" && value !== null ? value : {}; -} - -function noxConfigYAMLToConfigToml( - { - chain: { diamondContract, walletPrivateKey, ...chain } = {}, - ccp, - listenIp, - metrics, - effectors, - ...config - }: NoxConfigYAML, - ccpConfig: CCPConfigYAML, - env: ChainENV, -) { - const chainConfig = { - httpEndpoint: chain.httpEndpoint, - diamondContractAddress: diamondContract, - networkId: chain.networkId, - walletKey: walletPrivateKey, - defaultBaseFee: chain.defaultBaseFee, - defaultPriorityFee: chain.defaultPriorityFee, - ...getObjByKey(config, "chain_config"), - }; - - // Would be too hard to properly type this - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - return camelCaseKeysToSnakeCase({ - ...config, - ...(listenIp === undefined - ? {} - : { - listenConfig: { - listenIp, - ...getObjByKey(config, "listen_config"), - }, - }), - chainConfig, - ...(env === "local" - ? {} - : { - chainListenerConfig: { - wsEndpoint: chain.wsEndpoint, - ccpEndpoint: - ccp?.ccpEndpoint ?? - `http://${ - ccpConfig.rpcEndpoint?.host ?? DEFAULT_RPC_ENDPOINT_HOST - }:${numToStr( - ccpConfig.rpcEndpoint?.port ?? DEFAULT_RPC_ENDPOINT_PORT, - )}`, - proofPollPeriod: ccp?.proofPollPeriod, - ...getObjByKey(config, "chain_listener_config"), - }, - }), - tokioMetricsEnabled: metrics?.tokioMetricsEnabled, - tokioDetailedMetricsEnabled: metrics?.tokioDetailedMetricsEnabled, - metricsEnabled: metrics?.enabled, - metricsTimerResolution: metrics?.timerResolution, - ...(effectors === undefined - ? {} - : { - effectors: Object.fromEntries( - Object.entries(effectors).map( - ([name, { wasmCID, allowedBinaries }]) => { - return [ - name, - { wasmCID, allowedBinaries: allowedBinaries ?? {} }, - ] as const; - }, - ), - ), - }), - }) as JsonMap; -} - -function ccpConfigYAMLToConfigToml(config: CCPConfigYAML) { - // Would be too hard to properly type this - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - return camelCaseKeysToKebabCase(config) as JsonMap; -} - -function camelCaseToDifferentCase(caseFn: (str: string) => string) { - const camelCaseToDifferentCaseImpl = (val: unknown): unknown => { - if (typeof val === "object" && val !== null) { - if (Array.isArray(val)) { - return val.map(camelCaseToDifferentCaseImpl); - } - - const objWithSnakeCaseKeys = mapKeys(val, (_, key) => { - return caseFn(key); - }); - - return Object.fromEntries( - Object.entries(objWithSnakeCaseKeys).map(([key, value]) => { - return [key, camelCaseToDifferentCaseImpl(value)]; - }), - ); - } - - return val; - }; - - return camelCaseToDifferentCaseImpl; -} - -function camelCaseKeysToSnakeCase(val: unknown): unknown { - return camelCaseToDifferentCase(snakeCase)(val); -} - -function camelCaseKeysToKebabCase(val: unknown): unknown { - return camelCaseToDifferentCase(kebabCase)(val); -} - -function getDefaultCCPConfigYAML(): CCPConfigYAML { - return { - rpcEndpoint: { - host: DEFAULT_RPC_ENDPOINT_HOST, - port: DEFAULT_RPC_ENDPOINT_PORT, - utilityThreadIds: [1], - }, - prometheusEndpoint: { - host: DEFAULT_PROMETHEUS_ENDPOINT_HOST, - port: DEFAULT_PROMETHEUS_ENDPOINT_PORT, - }, - logs: { - reportHashrate: DEFAULT_REPORT_HASHRATE, - logLevel: DEFAULT_LOG_LEVEL, - }, - }; -} - -export function getConfigTomlName(noxName: string) { - return `${noxName}_Config.${TOML_EXT}`; -} - -function getCCPConfigTomlName(noxName: string) { - return `${noxName}_Config.${TOML_EXT}`; -} - export type EnsureComputerPeerConfig = Awaited< ReturnType >[number]; @@ -466,9 +255,6 @@ export async function ensureComputerPeerConfigs(computePeerNames?: string[]) { ); } - const { stringify } = await import("@iarna/toml"); - const configsDir = await ensureFluenceConfigsDir(); - const ccpConfigsDir = await ensureFluenceCCPConfigsDir(); const env = await ensureChainEnv(); if (env === "local") { @@ -496,59 +282,54 @@ export async function ensureComputerPeerConfigs(computePeerNames?: string[]) { } } + const k8sManifestsDir = await ensureK8sManifestsDir(); + const { diamond: diamondContract } = await resolveDeployment(); + const networkId = numToStr(await getChainId()); + const ipfsGatewayEndpoint = await getIpfsGateway(); + const wsEndpoint = WS_CHAIN_URLS[env]; + const httpEndpoint = await getRpcUrl(); + return Promise.all( computePeersWithCC.map( - async ( - { - computePeerName, - computePeer, - secretKey, - signingWallet, - capacityCommitment, - }, - i, - ) => { + async ({ + computePeerName, + computePeer, + secretKey, + signingWallet, + capacityCommitment, + }) => { await writeFile( await ensureFluenceSecretsFilePath(computePeerName), secretKey, - FS_OPTIONS, + "utf8", ); - const overridenCCPConfig = resolveCCPConfigYAML( - providerConfig.ccp, - computePeer.ccp, - ); + const peerId = await getPeerIdFromSecretKey(secretKey); - await writeFile( - join(ccpConfigsDir, getCCPConfigTomlName(computePeerName)), - stringify(ccpConfigYAMLToConfigToml(overridenCCPConfig)), - FS_OPTIONS, - ); + const ipSupplies = computePeer.resources?.ip.supply ?? []; - const overriddenNoxConfig = await resolveNoxConfigYAML( - providerConfig.nox, - computePeer.nox, - { i, signingWallet }, - ); + const manifest = genManifest({ + chainPrivateKey: hexStringToUTF8ToBase64String(signingWallet), + ipSupplies, + httpEndpoint, + wsEndpoint, + ipfsGatewayEndpoint, + peerIdHex: await peerIdBase58ToHexString(peerId), + networkId, + diamondContract, + }); - await writeFile( - join(configsDir, getConfigTomlName(computePeerName)), - stringify( - noxConfigYAMLToConfigToml( - overriddenNoxConfig, - overridenCCPConfig, - env, - ), - ), - FS_OPTIONS, - ); + const manifestPath = join(k8sManifestsDir, `${computePeerName}.yaml`); + await writeFile(manifestPath, manifest, "utf8"); return { name: computePeerName, - overriddenNoxConfig, secretKey, - peerId: await getPeerIdFromSecretKey(secretKey), + peerId, computeUnits: computePeer.computeUnits, + kubeconfigPath: computePeer.kubeconfigPath, + ipSupplies, + manifestPath, walletKey: signingWallet, walletAddress: await new Wallet(signingWallet).getAddress(), capacityCommitment, diff --git a/packages/cli/package/src/lib/configs/project/provider/provider0.ts b/packages/cli/package/src/lib/configs/project/provider/provider0.ts index 6e814ce9b..2d0474efa 100644 --- a/packages/cli/package/src/lib/configs/project/provider/provider0.ts +++ b/packages/cli/package/src/lib/configs/project/provider/provider0.ts @@ -27,7 +27,7 @@ import { DEFAULT_CC_DURATION, DEFAULT_CC_STAKER_REWARD, DURATION_EXAMPLE, - DEFAULT_NUMBER_OF_COMPUTE_UNITS_ON_NOX, + DEFAULT_NUMBER_OF_COMPUTE_UNITS_ON_PEER, PT_SYMBOL, } from "../../../const.js"; import { numToStr } from "../../../helpers/typesafeStringify.js"; @@ -394,7 +394,7 @@ const computePeerSchema = { computeUnits: { type: "integer", description: `How many compute units should nox have. Default: ${numToStr( - DEFAULT_NUMBER_OF_COMPUTE_UNITS_ON_NOX, + DEFAULT_NUMBER_OF_COMPUTE_UNITS_ON_PEER, )} (each compute unit requires ${COMPUTE_UNIT_MEMORY_STR} of RAM)`, }, nox: noxConfigYAMLSchema, diff --git a/packages/cli/package/src/lib/configs/project/provider/provider1.ts b/packages/cli/package/src/lib/configs/project/provider/provider1.ts index ab9086e65..fe605aa73 100644 --- a/packages/cli/package/src/lib/configs/project/provider/provider1.ts +++ b/packages/cli/package/src/lib/configs/project/provider/provider1.ts @@ -26,7 +26,7 @@ import { HTTP_PORT_START, TCP_PORT_START, WEB_SOCKET_PORT_START, - DEFAULT_NUMBER_OF_COMPUTE_UNITS_ON_NOX, + DEFAULT_NUMBER_OF_COMPUTE_UNITS_ON_PEER, } from "../../../const.js"; import { boolToStr, numToStr } from "../../../helpers/typesafeStringify.js"; import type { ConfigOptions } from "../../initConfigNewTypes.js"; @@ -120,9 +120,9 @@ export type NoxConfigYAML = { }; }; -export const DEFAULT_TIMER_RESOLUTION = "1 minute"; -export const DEFAULT_PROOF_POLL_PERIOD = "60 seconds"; -export const DEFAULT_IPFS_BINARY_PATH = "/usr/bin/ipfs"; +const DEFAULT_TIMER_RESOLUTION = "1 minute"; +const DEFAULT_PROOF_POLL_PERIOD = "60 seconds"; +const DEFAULT_IPFS_BINARY_PATH = "/usr/bin/ipfs"; export const noxConfigYAMLSchema = { type: "object", @@ -530,12 +530,12 @@ export type CCPConfigYAML = { rawConfig?: string; }; -export const DEFAULT_RPC_ENDPOINT_HOST = "0.0.0.0"; -export const DEFAULT_RPC_ENDPOINT_PORT = 9389; -export const DEFAULT_PROMETHEUS_ENDPOINT_HOST = "0.0.0.0"; -export const DEFAULT_PROMETHEUS_ENDPOINT_PORT = 9384; -export const DEFAULT_REPORT_HASHRATE = false; -export const DEFAULT_LOG_LEVEL = "debug"; +const DEFAULT_RPC_ENDPOINT_HOST = "0.0.0.0"; +const DEFAULT_RPC_ENDPOINT_PORT = 9389; +const DEFAULT_PROMETHEUS_ENDPOINT_HOST = "0.0.0.0"; +const DEFAULT_PROMETHEUS_ENDPOINT_PORT = 9384; +const DEFAULT_REPORT_HASHRATE = false; +const DEFAULT_LOG_LEVEL = "debug"; const DEFAULT_STATE_PATH = "./state"; export const ccpConfigYAMLSchema = { @@ -642,8 +642,92 @@ export const ccpConfigYAMLSchema = { additionalProperties: false, } as const satisfies JSONSchemaType; +type IPSupply = + | { + start: string; + end?: string; + } + | { + cidr: string; + }; + +const supplySchema = { + type: "object", + description: + "Either specify only a `start` property (if you want a single IP) or `start` and `end` properties (if you want a range) or `cidr` property (if you want a CIDR notation)", + oneOf: [ + { + additionalProperties: false, + properties: { + start: { + type: "string", + format: "ipv4", + description: "Start of the IP range or individual IP", + }, + end: { + nullable: true, + type: "string", + format: "ipv4", + description: "End of the IP range", + }, + }, + required: ["start"], + }, + { + additionalProperties: false, + properties: { + cidr: { + type: "string", + description: "CIDR notation of the IP range", + }, + }, + required: ["cidr"], + }, + ], + required: [], + nullable: true, +} as const satisfies JSONSchemaType; + +export type IPSupplies = Array; + +type IP = { + supply: IPSupplies; +}; + +const ipSchema = { + type: "object", + description: "IP configuration", + additionalProperties: false, + properties: { + supply: { + type: "array", + items: supplySchema, + description: "IP supply", + minItems: 1, + }, + }, + required: ["supply"], +} as const satisfies JSONSchemaType; + +type Resources = { + ip: IP; +}; + +const resourcesSchema = { + type: "object", + description: "Resources configuration", + additionalProperties: false, + properties: { + ip: ipSchema, + }, + required: ["ip"], + nullable: true, +} as const satisfies JSONSchemaType; + export type ComputePeer = { computeUnits: number; + resources?: Resources; + kubeconfigPath?: string; nox?: NoxConfigYAML; ccp?: CCPConfigYAML; }; @@ -656,9 +740,15 @@ const computePeerSchema = { computeUnits: { type: "integer", description: `How many compute units should nox have. Default: ${numToStr( - DEFAULT_NUMBER_OF_COMPUTE_UNITS_ON_NOX, + DEFAULT_NUMBER_OF_COMPUTE_UNITS_ON_PEER, )} (each compute unit requires ${COMPUTE_UNIT_MEMORY_STR} of RAM)`, }, + resources: resourcesSchema, + kubeconfigPath: { + nullable: true, + type: "string", + description: `Path to the kubeconfig file`, + }, nox: noxConfigYAMLSchema, ccp: ccpConfigYAMLSchema, }, diff --git a/packages/cli/package/src/lib/configs/project/provider/provider2.ts b/packages/cli/package/src/lib/configs/project/provider/provider2.ts index 75c25db1d..3e2e1388f 100644 --- a/packages/cli/package/src/lib/configs/project/provider/provider2.ts +++ b/packages/cli/package/src/lib/configs/project/provider/provider2.ts @@ -38,7 +38,7 @@ import { type Config as PrevConfig, } from "./provider1.js"; -export type Offer = { +type Offer = { minPricePerCuPerEpoch: string; computePeers: Array; effectors?: Array; diff --git a/packages/cli/package/src/lib/configs/project/provider/provider3.ts b/packages/cli/package/src/lib/configs/project/provider/provider3.ts index aefcd02cd..723ff5907 100644 --- a/packages/cli/package/src/lib/configs/project/provider/provider3.ts +++ b/packages/cli/package/src/lib/configs/project/provider/provider3.ts @@ -15,17 +15,12 @@ * along with this program. If not, see . */ -import { parse } from "@iarna/toml"; import { color } from "@oclif/color"; import type { JSONSchemaType } from "ajv"; -import cloneDeep from "lodash-es/cloneDeep.js"; import isEmpty from "lodash-es/isEmpty.js"; import mapValues from "lodash-es/mapValues.js"; -import mergeWith from "lodash-es/mergeWith.js"; -import { jsonStringify, type ChainENV } from "../../../../common.js"; import { versions } from "../../../../versions.js"; -import { getChainId } from "../../../chain/chainConfig.js"; import { ccDurationValidator, validateAddress, @@ -33,38 +28,21 @@ import { } from "../../../chain/chainValidators.js"; import { PROVIDER_CONFIG_FULL_FILE_NAME, - DEFAULT_AQUAVM_POOL_SIZE, - HTTP_PORT_START, - TCP_PORT_START, - WEB_SOCKET_PORT_START, - LOCAL_IPFS_ADDRESS, DEFAULT_CC_DURATION, DEFAULT_CC_STAKER_REWARD, DURATION_EXAMPLE, - WS_CHAIN_URLS, - DEFAULT_VM_EFFECTOR_CID, } from "../../../const.js"; -import { resolveDeployment } from "../../../dealClient.js"; -import { ensureChainEnv } from "../../../ensureChainNetwork.js"; import { type ValidationResult, validateCIDs, } from "../../../helpers/validations.js"; import { validateBatchAsync } from "../../../helpers/validations.js"; -import { resolveRelaysWithoutLocal } from "../../../multiaddresWithoutLocal.js"; import type { ConfigOptions } from "../../initConfigNewTypes.js"; -import { CHAIN_RPC_CONTAINER_NAME } from "../chainContainers.js"; -import { IPFS_CONTAINER_NAME } from "../chainContainers.js"; -import { CHAIN_RPC_PORT, IPFS_PORT } from "../chainContainers.js"; -import { initEnvConfig } from "../env/env.js"; import { providerNameSchema } from "./provider0.js"; import { ccpConfigYAMLSchema, computePeersSchema, - DEFAULT_IPFS_BINARY_PATH, - DEFAULT_PROOF_POLL_PERIOD, - DEFAULT_TIMER_RESOLUTION, noxConfigYAMLSchema, type CCPConfigYAML, type ComputePeers, @@ -191,7 +169,6 @@ export default { }, ), ), - validateEffectors(config), validateCC(config), validateMissingComputePeers(config), validateNoDuplicateNoxNamesInOffers(config), @@ -255,82 +232,6 @@ async function validateProtocolVersions(providerConfig: Config) { return true; } -export async function validateEffectors( - providerConfig: Config, -): Promise { - const errors = ( - await Promise.all( - Object.entries(providerConfig.offers).flatMap( - ([offerName, { effectors = [], computePeers: computePeerNames }]) => { - const offerEffectorsString = jsonStringify([...effectors].sort()); - - return computePeerNames.map(async (computePeerName) => { - const computePeer = providerConfig.computePeers[computePeerName]; - - if (computePeer === undefined) { - return true; - } - - const noxConfig = await resolveNoxConfigYAML( - providerConfig.nox, - computePeer.nox, - ); - - const computePeerEffectors = [ - ...Object.values(noxConfig.effectors ?? {}).map(({ wasmCID }) => { - return wasmCID; - }), - ].sort(); - - const hasDefaultVmEffector = computePeerEffectors.includes( - DEFAULT_VM_EFFECTOR_CID, - ); - - if ( - noxConfig.vm?.network.publicIp !== undefined && - !hasDefaultVmEffector - ) { - return `Compute peer ${color.yellow( - computePeerName, - )} has a defined publicIp property:\n\nvm:\n network:\n publicIp: ${noxConfig.vm.network.publicIp}\n\nso it is expected to also have a vm effector:\n\neffectors:\n vm:\n wasmCID: ${DEFAULT_VM_EFFECTOR_CID}`; - } - - if ( - noxConfig.vm?.network.publicIp === undefined && - hasDefaultVmEffector - ) { - return `Compute peer ${color.yellow( - computePeerName, - )} has a vm effector:\n\neffectors:\n vm:\n wasmCID: ${DEFAULT_VM_EFFECTOR_CID}\n\nso it is expected to also have a defined publicIp property:\n\nvm:\n network:\n publicIp: `; - } - - const computePeerEffectorsString = - jsonStringify(computePeerEffectors); - - if (computePeerEffectorsString !== offerEffectorsString) { - return `Offer ${color.yellow( - offerName, - )} contains computePeer ${color.yellow( - computePeerName, - )}, that has effectors ${color.yellow( - computePeerEffectorsString, - )} which doesn't match effectors that are specified in the offer ${color.yellow( - offerEffectorsString, - )}`; - } - - return true; - }); - }, - ), - ) - ).filter((result): result is string => { - return typeof result === "string"; - }); - - return errors.length > 0 ? errors.join("\n\n") : true; -} - function validateNoDuplicateNoxNamesInOffers(config: Config): ValidationResult { const noxNamesInOffers: Record = {}; @@ -355,7 +256,7 @@ function validateNoDuplicateNoxNamesInOffers(config: Config): ValidationResult { if (duplicateNoxNames.length > 0) { return duplicateNoxNames .map(([noxName, offerNames]) => { - return `Nox ${color.yellow( + return `Peer ${color.yellow( noxName, )} is present in multiple offers: ${color.yellow( offerNames.join(", "), @@ -444,165 +345,3 @@ function validateMissingComputePeers(config: Config): ValidationResult { return true; } - -function mergeConfigYAML(a: T, b: Record) { - return mergeWith(cloneDeep(a), b, (objValue, srcValue) => { - if (Array.isArray(objValue) && Array.isArray(srcValue)) { - return srcValue; - } - - return undefined; - }); -} - -export function mergeConfigYAMLWithRawConfig< - T extends { rawConfig?: string | undefined } & Record, ->(a: T, b: T) { - const { rawConfig: rawConfigB, ...configB } = b; - let config = mergeConfigYAML(a, configB); - - const parsedRawConfigB = - rawConfigB === undefined ? undefined : parse(rawConfigB); - - if (parsedRawConfigB !== undefined) { - config = mergeConfigYAML(config, parsedRawConfigB); - } - - return config; -} - -export async function resolveNoxConfigYAML( - globalNoxConfig: NoxConfigYAML | undefined = {}, - computePeerNoxConfig: NoxConfigYAML | undefined = {}, - { i = 0, signingWallet = "" }: { i?: number; signingWallet?: string } = {}, -) { - const env = await ensureChainEnv(); - const isLocal = env === "local"; - - let config = mergeConfigYAMLWithRawConfig( - await getDefaultNoxConfigYAML(), - globalNoxConfig, - ); - - config = mergeConfigYAMLWithRawConfig(config, computePeerNoxConfig); - - /* eslint-disable @typescript-eslint/consistent-type-assertions */ - - const tcpPort = - (config["tcp_port"] as number | undefined) ?? - config.tcpPort ?? - (isLocal ? TCP_PORT_START - i : TCP_PORT_START); - - const websocketPort = - (config["websocket_port"] as number | undefined) ?? - config.websocketPort ?? - (isLocal ? WEB_SOCKET_PORT_START - i : WEB_SOCKET_PORT_START); - - const httpPort = - (config["http_port"] as number | undefined) ?? - config.httpPort ?? - (isLocal ? HTTP_PORT_START - i : HTTP_PORT_START); - - const walletPrivateKey = - // @ts-expect-error we allow user to put anything in raw config - (config["chain_config"]?.["wallet_key"] as string | undefined) ?? - config.chain?.walletPrivateKey ?? - signingWallet; - - /* eslint-enable @typescript-eslint/consistent-type-assertions */ - - if (config.chain?.walletPrivateKey === undefined) { - config.chain = { ...config.chain, walletPrivateKey }; - } - - let ipfs: undefined | NoxConfigYAML["ipfs"]; - // eslint-disable-next-line prefer-const - ({ ipfs, ...config } = config); - - config.systemServices = { - ...config.systemServices, - aquaIpfs: { - ...config.systemServices?.aquaIpfs, - externalApiMultiaddr: - config.systemServices?.aquaIpfs?.externalApiMultiaddr ?? - ipfs?.externalApiMultiaddr ?? - EXTERNAL_API_MULTIADDRS[env], - localApiMultiaddr: - config.systemServices?.aquaIpfs?.localApiMultiaddr ?? - ipfs?.localApiMultiaddr ?? - LOCAL_API_MULTIADDRS[env], - ipfsBinaryPath: - config.systemServices?.aquaIpfs?.ipfsBinaryPath ?? - ipfs?.ipfsBinaryPath ?? - DEFAULT_IPFS_BINARY_PATH, - }, - }; - - return { ...config, tcpPort, websocketPort, httpPort }; -} - -const EXTERNAL_API_MULTIADDRS: Record = { - mainnet: "/dns4/ipfs.kras.fluence.dev/tcp/5020", - testnet: "/dns4/ipfs.dar.fluence.dev/tcp/5020", - stage: "/dns4/ipfs.fluence.dev/tcp/5001", - local: LOCAL_IPFS_ADDRESS, -}; - -export const NOX_IPFS_MULTIADDR = `/dns4/${IPFS_CONTAINER_NAME}/tcp/${IPFS_PORT}`; - -const LOCAL_API_MULTIADDRS: Record = { - ...EXTERNAL_API_MULTIADDRS, - local: NOX_IPFS_MULTIADDR, -}; - -export async function getDefaultNoxConfigYAML(): Promise { - const env = await ensureChainEnv(); - const networkId = await getChainId(); - const { RPC_URLS } = await import("@fluencelabs/deal-ts-clients"); - const envConfig = await initEnvConfig(); - - const CHAIN_URLS_FOR_CONTAINERS = { - ...RPC_URLS, - local: `http://${CHAIN_RPC_CONTAINER_NAME}:${CHAIN_RPC_PORT}`, - }; - - return { - aquavmPoolSize: DEFAULT_AQUAVM_POOL_SIZE, - ipfs: { - externalApiMultiaddr: EXTERNAL_API_MULTIADDRS[env], - localApiMultiaddr: LOCAL_API_MULTIADDRS[env], - ipfsBinaryPath: DEFAULT_IPFS_BINARY_PATH, - }, - systemServices: { - enable: ["aqua-ipfs", "decider"], - decider: { - deciderPeriodSec: 30, - workerIpfsMultiaddr: - env === "local" - ? NOX_IPFS_MULTIADDR - : "/dns4/ipfs.fluence.dev/tcp/5001", - }, - }, - chain: { - httpEndpoint: - envConfig?.rpcUrl === undefined - ? CHAIN_URLS_FOR_CONTAINERS[env] - : envConfig.rpcUrl, - wsEndpoint: WS_CHAIN_URLS[env], - diamondContract: (await resolveDeployment()).diamond, - networkId, - defaultPriorityFee: 0, - }, - ccp: { - proofPollPeriod: DEFAULT_PROOF_POLL_PERIOD, - }, - ...(env === "local" - ? {} - : { bootstrapNodes: await resolveRelaysWithoutLocal(env) }), - metrics: { - enabled: true, - timerResolution: DEFAULT_TIMER_RESOLUTION, - tokioMetricsEnabled: true, - }, - }; -} diff --git a/packages/cli/package/src/lib/configs/project/service.ts b/packages/cli/package/src/lib/configs/project/service.ts deleted file mode 100644 index b38015689..000000000 --- a/packages/cli/package/src/lib/configs/project/service.ts +++ /dev/null @@ -1,271 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import { cwd } from "node:process"; - -import { color } from "@oclif/color"; -import type { JSONSchemaType } from "ajv"; - -import { commandObj } from "../../commandObj.js"; -import { - MIN_MEMORY_PER_MODULE_STR, - BYTES_PATTERN, - CLI_NAME, - FLUENCE_CONFIG_FULL_FILE_NAME, - SERVICE_CONFIG_FILE_NAME, - SERVICE_CONFIG_FULL_FILE_NAME, - TOP_LEVEL_SCHEMA_ID, - COMPUTE_UNIT_MEMORY_STR, - BYTES_FORMAT, -} from "../../const.js"; -import { - ensureServiceAbsolutePath, - validateAquaName, -} from "../../helpers/downloadFile.js"; -import { getFluenceDir, projectRootDir } from "../../paths.js"; -import { - getConfigInitFunction, - getReadonlyConfigInitFunction, - type InitConfigOptions, - type InitializedConfig, - type InitializedReadonlyConfig, - type Migrations, - type GetDefaultConfig, - type ConfigValidateFunction, -} from "../initConfig.js"; - -import { initFluenceConfig } from "./fluence.js"; -import { - type OverridableModuleProperties, - overridableModuleProperties, -} from "./module.js"; - -export type ServiceModuleV0 = { - get: string; -} & OverridableModuleProperties; - -const moduleSchemaForService: JSONSchemaType = { - type: "object", - title: "Module", - properties: { - get: { - type: "string", - description: - "Either path to the module directory or URL to the tar.gz archive which contains the content of the module directory", - }, - ...overridableModuleProperties.properties, - }, - required: ["get"], - additionalProperties: false, -}; - -export const FACADE_MODULE_NAME = "facade"; - -type Modules = { [FACADE_MODULE_NAME]: ServiceModuleV0 } & Record< - string, - ServiceModuleV0 ->; - -type ConfigV0 = { - version: 0; - name: string; - modules: Modules; - totalMemoryLimit?: string; -}; - -export type OverridableServiceProperties = Pick; - -export const overridableServiceProperties = { - type: "object", - properties: { - totalMemoryLimit: { - type: "string", - pattern: BYTES_PATTERN, - nullable: true, - description: `Memory limit for all service modules. If you specify this property please make sure it's at least \`${MIN_MEMORY_PER_MODULE_STR} * numberOfModulesInTheService\`. In repl default is the entire compute unit memory: ${COMPUTE_UNIT_MEMORY_STR}. When deploying service as part of the worker default is: computeUnits * ${COMPUTE_UNIT_MEMORY_STR} / (amount of services in the worker). Format: ${BYTES_FORMAT}`, - }, - }, - required: [], -} as const satisfies JSONSchemaType; - -const configSchemaV0: JSONSchemaType = { - type: "object", - $id: `${TOP_LEVEL_SCHEMA_ID}/${SERVICE_CONFIG_FULL_FILE_NAME}`, - title: SERVICE_CONFIG_FULL_FILE_NAME, - description: `Defines a [Marine service](https://fluence.dev/docs/build/concepts/#services), most importantly the modules that the service consists of. You can use \`${CLI_NAME} service new\` command to generate a template for new service`, - properties: { - name: { - type: "string", - description: `Service name. Currently it is used for the service name only when you add service to ${FLUENCE_CONFIG_FULL_FILE_NAME} using "add" command. But this name can be overridden to any other with the --name flag or manually in ${FLUENCE_CONFIG_FULL_FILE_NAME}`, - }, - modules: { - title: "Modules", - description: `Service must have a facade module. Each module properties can be overridden by the same properties in the service config`, - type: "object", - additionalProperties: moduleSchemaForService, - properties: { - [FACADE_MODULE_NAME]: moduleSchemaForService, - Other_module_name: moduleSchemaForService, - }, - required: [FACADE_MODULE_NAME], - }, - ...overridableServiceProperties.properties, - version: { type: "integer", const: 0 }, - }, - required: [ - "version", - "name", - "modules", - ...overridableServiceProperties.required, - ], - additionalProperties: false, -}; - -export function isValidServiceModules( - arg: Record, -): arg is Modules { - return FACADE_MODULE_NAME in arg; -} - -const migrations: Migrations = []; - -type Config = ConfigV0; -type LatestConfig = ConfigV0; - -export type ServiceConfig = InitializedConfig; -export type ServiceConfigReadonly = InitializedReadonlyConfig; - -const validate: ConfigValidateFunction = ( - config, -): ReturnType> => { - const validity = validateAquaName(config.name); - - if (validity === true) { - return true; - } - - return `Invalid service name: ${validity}`; -}; - -const getInitConfigOptions = ( - configDirPath: string, -): InitConfigOptions => { - return { - allSchemas: [configSchemaV0], - latestSchema: configSchemaV0, - migrations, - name: SERVICE_CONFIG_FILE_NAME, - getSchemaDirPath: getFluenceDir, - getConfigOrConfigDirPath: (): string => { - return configDirPath; - }, - validate, - }; -}; - -export const initServiceConfig = async ( - configOrConfigDirPathOrUrl: string, - absolutePath: string, -): Promise | null> => { - return getConfigInitFunction( - getInitConfigOptions( - await ensureServiceAbsolutePath(configOrConfigDirPathOrUrl, absolutePath), - ), - )(); -}; - -export async function ensureServiceConfig( - nameOrPathOrUrl: string, -): Promise { - const fluenceConfig = await initFluenceConfig(); - - const maybeServicePathFromFluenceConfig = - fluenceConfig?.services?.[nameOrPathOrUrl]?.get; - - const serviceOrServiceDirPathOrUrl = - maybeServicePathFromFluenceConfig ?? nameOrPathOrUrl; - - const serviceConfig = await initServiceConfig( - serviceOrServiceDirPathOrUrl, - maybeServicePathFromFluenceConfig === "string" ? projectRootDir : cwd(), - ); - - if (serviceConfig === null) { - return commandObj.error( - `No service config found at ${color.yellow( - serviceOrServiceDirPathOrUrl, - )}`, - ); - } - - return serviceConfig; -} - -export const initReadonlyServiceConfig = async ( - configOrConfigDirPathOrUrl: string, - absolutePath: string, -): Promise | null> => { - return getReadonlyConfigInitFunction( - getInitConfigOptions( - await ensureServiceAbsolutePath(configOrConfigDirPathOrUrl, absolutePath), - ), - )(); -}; - -const getDefault: ( - relativePathToFacade: string, - name: string, -) => GetDefaultConfig = ( - relativePathToFacade: string, - name: string, -): GetDefaultConfig => { - return () => { - return `# Defines a [Marine service](https://fluence.dev/docs/build/concepts/#services), -# most importantly the modules that the service consists of. -# You can use \`fluence service new\` command to generate a template for new service - -# config version -version: 0 - -# Service name. -# Currently it is used for the service name only when you add service to fluence.yaml using "add" command. -# But this name can be overridden to any other with the --name flag or manually in fluence.yaml -name: ${name} - -# A map of modules that the service consists of. -# Service must have a facade module. Each module properties can be overridden -modules: - facade: - # Either path to the module directory or - # URL to the tar.gz archive which contains the content of the module directory - get: '${relativePathToFacade}' -`; - }; -}; - -export const initNewReadonlyServiceConfig = ( - configPath: string, - relativePathToFacade: string, - name: string, -): Promise> => { - return getReadonlyConfigInitFunction( - getInitConfigOptions(configPath), - getDefault(relativePathToFacade, name), - )(); -}; - -export const serviceSchema: JSONSchemaType = configSchemaV0; diff --git a/packages/cli/package/src/lib/configs/project/spell.ts b/packages/cli/package/src/lib/configs/project/spell.ts deleted file mode 100644 index 2aee80605..000000000 --- a/packages/cli/package/src/lib/configs/project/spell.ts +++ /dev/null @@ -1,352 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import { access } from "fs/promises"; -import { dirname, resolve } from "path"; - -import type { JSONSchemaType } from "ajv"; - -import { - CLI_NAME, - FLUENCE_CONFIG_FULL_FILE_NAME, - SPELL_CONFIG_FILE_NAME, - SPELL_CONFIG_FULL_FILE_NAME, - TOP_LEVEL_SCHEMA_ID, - U32_MAX, -} from "../../const.js"; -import { ensureSpellAbsolutePath } from "../../helpers/downloadFile.js"; -import { numToStr } from "../../helpers/typesafeStringify.js"; -import { validateBatchAsync } from "../../helpers/validations.js"; -import { getFluenceDir } from "../../paths.js"; -import { - getConfigInitFunction, - getReadonlyConfigInitFunction, - type InitConfigOptions, - type InitializedConfig, - type InitializedReadonlyConfig, - type Migrations, - type ConfigValidateFunction, -} from "../initConfig.js"; - -const MAX_PERIOD_YEAR = 100; - -/** Max period is 100 years in secs: 60 sec * 60 min * 24 hours * 365 days * 100 years */ -const MAX_PERIOD_SEC = 60 * 60 * 24 * 365 * MAX_PERIOD_YEAR; - -type SpellProperties = { - aquaFilePath: string; - function: string; - initArgs?: Record; - clock?: { - periodSec?: number; - startTimestamp?: string; - endTimestamp?: string; - startDelaySec?: number; - endDelaySec?: number; - }; -}; - -export type OverridableSpellProperties = Partial; - -type ConfigV0 = { - version: 0; -} & SpellProperties; - -const spellProperties = { - version: { type: "integer", const: 0 }, - aquaFilePath: { - type: "string", - description: - "Path to Aqua file which contains an Aqua function that you want to use as a spell", - }, - function: { - type: "string", - description: "Name of the Aqua function that you want to use as a spell", - }, - initArgs: { - type: "object", - description: - "A map of Aqua function arguments names as keys and arguments values as values. They will be passed to the spell function and will be stored in the key-value storage for this particular spell.", - nullable: true, - }, - clock: { - type: "object", - additionalProperties: false, - nullable: true, - description: `Trigger the spell execution periodically. If you want to disable this property by overriding it in ${FLUENCE_CONFIG_FULL_FILE_NAME} - pass empty config for it like this: \`clock: {}\``, - properties: { - periodSec: { - type: "integer", - description: - "How often the spell will be executed. If set to 0, the spell will be executed only once. If this value not provided at all - the spell will never be executed", - minimum: 0, - maximum: MAX_PERIOD_SEC, - nullable: true, - }, - startTimestamp: { - type: "string", - description: - "An ISO timestamp when the periodic execution should start. If this property or `startDelaySec` not specified, periodic execution will start immediately. If it is set to 0 - the spell will never be executed", - nullable: true, - }, - endTimestamp: { - type: "string", - description: - "An ISO timestamp when the periodic execution should end. If this property or `endDelaySec` not specified, periodic execution will never end. If it is in the past at the moment of spell creation on Rust peer - the spell will never be executed", - nullable: true, - }, - startDelaySec: { - type: "integer", - description: - "How long to wait before the first execution in seconds. If this property or `startTimestamp` not specified, periodic execution will start immediately. WARNING! Currently your computer's clock is used to determine a final timestamp that is sent to the server. This property conflicts with `startTimestamp`. You can specify only one of them", - nullable: true, - minimum: 0, - maximum: U32_MAX, - }, - endDelaySec: { - type: "integer", - description: - "How long to wait before the last execution in seconds. If this property or `endTimestamp` not specified, periodic execution will never end. WARNING! Currently your computer's clock is used to determine a final timestamp that is sent to the server. If it is in the past at the moment of spell creation - the spell will never be executed. This property conflicts with `endTimestamp`. You can specify only one of them", - nullable: true, - minimum: 0, - maximum: U32_MAX, - }, - }, - required: [], - }, -} as const; - -export const overridableSpellProperties = { - ...spellProperties, - aquaFilePath: { - ...spellProperties.aquaFilePath, - nullable: true, - }, - function: { - ...spellProperties.function, - nullable: true, - }, -} as const; - -const configSchemaV0: JSONSchemaType = { - type: "object", - $id: `${TOP_LEVEL_SCHEMA_ID}/${SPELL_CONFIG_FULL_FILE_NAME}`, - title: SPELL_CONFIG_FULL_FILE_NAME, - description: `Defines a spell. You can use \`${CLI_NAME} spell new\` command to generate a template for new spell`, - properties: spellProperties, - required: ["version", "function", "aquaFilePath"], - additionalProperties: false, -}; - -const migrations: Migrations = []; -type Config = ConfigV0; -type LatestConfig = ConfigV0; -export type SpellConfig = InitializedConfig; -export type SpellConfigReadonly = InitializedReadonlyConfig; - -const getDateSec = (date: Date) => { - return Math.round(date.getTime() / 1000); -}; - -export const resolveStartSec = ({ clock }: LatestConfig): number => { - if (clock === undefined) { - return 0; - } - - if (clock.startDelaySec !== undefined) { - return getDateSec(new Date()) + clock.startDelaySec; - } - - return clock.startTimestamp === undefined - ? 1 - : getDateSec(new Date(clock.startTimestamp)); -}; - -export const resolveEndSec = ({ clock }: LatestConfig): number => { - if (clock === undefined) { - return 0; - } - - if (clock.endDelaySec !== undefined) { - return getDateSec(new Date()) + clock.endDelaySec; - } - - return clock.endTimestamp === undefined - ? 0 - : getDateSec(new Date(clock.endTimestamp)); -}; - -const validate: ConfigValidateFunction = async ( - config, - configPath, -) => { - return validateBatchAsync( - config.clock?.startTimestamp !== undefined && - config.clock.startDelaySec !== undefined - ? `You can't specify both 'startTimestamp' and 'startDelaySec' properties` - : true, - - config.clock?.endTimestamp !== undefined && - config.clock.endDelaySec !== undefined - ? `You can't specify both 'endTimestamp' and 'endDelaySec' properties` - : true, - - (() => { - let endSec; - - try { - endSec = resolveEndSec(config); - } catch { - return "Invalid `endTimestamp` value. It must be an ISO timestamp"; - } - - try { - const startSec = resolveStartSec(config); - - if (startSec === 0) { - return true; - } - - if (endSec !== 0 && endSec < startSec) { - return `Start time must be earlier than end time. Got: start time ${numToStr(startSec)}, end time ${numToStr(endSec)}`; - } - - const currentSec = getDateSec(new Date()); - - if (endSec !== 0 && endSec < currentSec) { - return `End time must be later than current time. Got: current time ${numToStr(currentSec)}, end time ${numToStr(endSec)}`; - } - - if ((config.clock?.periodSec ?? 0) > MAX_PERIOD_SEC) { - return `Period must be less than ${numToStr(MAX_PERIOD_YEAR)} years (${numToStr(MAX_PERIOD_SEC)} seconds). Got: ${numToStr( - endSec - startSec, - )} seconds`; - } - - return true; - } catch { - return `Invalid 'startTimestamp' value. It must be an ISO timestamp`; - } - })(), - (async () => { - try { - await access(resolve(dirname(configPath), config.aquaFilePath)); - return true; - } catch { - return `Aqua file '${config.aquaFilePath}' doesn't exist`; - } - })(), - ); -}; - -const getInitConfigOptions = ( - configPath: string, -): InitConfigOptions => { - return { - allSchemas: [configSchemaV0], - latestSchema: configSchemaV0, - migrations, - validate, - name: SPELL_CONFIG_FILE_NAME, - getSchemaDirPath: getFluenceDir, - getConfigOrConfigDirPath: (): string => { - return configPath; - }, - }; -}; - -export const initSpellConfig = async ( - configOrConfigDirPathOrUrl: string, - absolutePath: string, -): Promise | null> => { - return getConfigInitFunction( - getInitConfigOptions( - await ensureSpellAbsolutePath(configOrConfigDirPathOrUrl, absolutePath), - ), - )(); -}; - -export const initReadonlySpellConfig = async ( - configOrConfigDirPathOrUrl: string, - absolutePath: string, -): Promise | null> => { - return getReadonlyConfigInitFunction( - getInitConfigOptions( - await ensureSpellAbsolutePath(configOrConfigDirPathOrUrl, absolutePath), - ), - )(); -}; - -const getDefault = (): string => { - return `# Defines a spell. You can use \`fluence spell new\` command to generate a template for new spell - -# config version -version: 0 - -# Path to Aqua file which contains an Aqua function that you want to use as a spell -aquaFilePath: "./spell.aqua" - -# Name of the Aqua function that you want to use as a spell -function: spell - -# # These arguments will be passed to the spell function and will be stored in the key-value storage for this particular spell. -# initArgs: -# someArg: someArgStringValue - -# Trigger the spell execution periodically -# If you want to disable this property by overriding it -# pass an empty config for it like this: \`clock: {}\` -clock: - # How often the spell will be executed. - # If set to 0, the spell will be executed only once. - # If this value not provided at all - the spell will never be executed - periodSec: 60 - # How long to wait before the last execution in seconds. - # If this property or \`endTimestamp\` not specified, periodic execution will never end. - # WARNING! Currently your computer's clock is used to determine a final timestamp that is sent to the server. - # If it is in the past at the moment of spell creation - the spell will never be executed. - # This property conflicts with \`endTimestamp\`. You can specify only one of them - endDelaySec: 1800 - -# # other 'clock' properties: - -# # How long to wait before the first execution in seconds. -# # If this property or \`startTimestamp\` not specified, periodic execution will start immediately. -# # WARNING! Currently your computer's clock is used to determine a final timestamp that is sent to the server. -# # If it is set to 0 - the spell will never be executed -# # This property conflicts with \`startTimestamp\`. You can specify only one of them -# startDelaySec: 1 -# # An ISO timestamp when the periodic execution should start. -# # If this property or \`startDelaySec\` not specified, periodic execution will start immediately. -# startTimestamp: '2023-07-06T23:59:59Z' -# # An ISO timestamp when the periodic execution should end. -# # If this property or \`endDelaySec\` not specified, periodic execution will never end. -# # If it is in the past at the moment of spell creation on Rust peer - the spell will never be executed -# endTimestamp: '2023-07-06T23:59:59Z' -`; -}; - -export const initNewReadonlySpellConfig = ( - configPath: string, -): Promise | null> => { - return getReadonlyConfigInitFunction( - getInitConfigOptions(configPath), - getDefault, - )(); -}; - -export const spellSchema: JSONSchemaType = configSchemaV0; diff --git a/packages/cli/package/src/lib/configs/project/workers.ts b/packages/cli/package/src/lib/configs/project/workers.ts deleted file mode 100644 index 33dbc0003..000000000 --- a/packages/cli/package/src/lib/configs/project/workers.ts +++ /dev/null @@ -1,555 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import { join } from "path"; - -import type { JSONSchemaType } from "ajv"; -import isEmpty from "lodash-es/isEmpty.js"; - -import { - CHAIN_ENV, - CHAIN_ENV_OLD, - type ChainENVOld, - chainEnvOldToNew, - DEFAULT_PUBLIC_FLUENCE_ENV, - isChainEnvOld, -} from "../../../common.js"; -import { ajv, validationErrorToString } from "../../ajvInstance.js"; -import { - WORKERS_CONFIG_FULL_FILE_NAME, - TOP_LEVEL_SCHEMA_ID, - WORKERS_CONFIG_FILE_NAME, - CLI_NAME, - DEFAULT_DEPLOYMENT_NAME, - DEFAULT_WORKER_NAME, - type FluenceEnvOld, - type FluenceEnv, -} from "../../const.js"; -import { numToStr } from "../../helpers/typesafeStringify.js"; -import { getFluenceDir } from "../../paths.js"; -import { fluenceEnvOldPrompt } from "../../resolveFluenceEnv.js"; -import { - getReadonlyConfigInitFunction, - getConfigInitFunction, - type InitConfigOptions, - type InitializedConfig, - type InitializedReadonlyConfig, - type Migrations, - type GetDefaultConfig, -} from "../initConfig.js"; - -type WorkerInfo = { - timestamp: string; - definition: string; -}; - -const workerInfoSchema = { - type: "object", - properties: { - definition: { - type: "string", - description: - "CID of uploaded to IPFS App Definition, which contains the data about everything that you are trying to deploy, including spells, service and module configs and CIDs for service wasms", - }, - timestamp: { - type: "string", - description: "ISO timestamp of the time when the worker was deployed", - }, - }, - required: ["timestamp", "definition"], - additionalProperties: false, -} as const satisfies JSONSchemaType; - -type DealV1 = WorkerInfo & { - dealId: string; - dealIdOriginal: string; - chainNetworkId: number; - chainNetwork?: ChainENVOld; - matched?: boolean; -}; - -type HostV1 = WorkerInfo & { - relayId: string; - dummyDealId: string; - installation_spells: { - host_id: string; - spell_id: string; - worker_id: string; - }[]; -}; - -type ConfigV0 = { - version: 0; - deals?: Record; - hosts?: Record; -}; - -const hostSchemaV1: JSONSchemaType = { - ...workerInfoSchema, - description: - "Contains data related to your direct deployment. Most importantly, it contains ids in installation_spells property that can be used to resolve workers in aqua", - properties: { - ...workerInfoSchema.properties, - dummyDealId: { - type: "string", - description: - "random string generated by CLI, used in Nox. You can get worker id from it", - }, - installation_spells: { - type: "array", - description: "A list of installation spells", - items: { - type: "object", - properties: { - host_id: { - type: "string", - description: - "Can be used to access worker in aqua: `on s.workerId via s.hostId`", - }, - spell_id: { - type: "string", - description: - "id of the installation spell, can be used to e.g. print spell logs", - }, - worker_id: { - type: "string", - description: - "Can be used to access worker in aqua: `on s.workerId via s.hostId`", - }, - }, - required: ["host_id", "spell_id", "worker_id"], - additionalProperties: false, - }, - }, - relayId: { - type: "string", - description: "relay peer id that was used when deploying", - }, - }, - required: [ - ...workerInfoSchema.required, - "installation_spells", - "relayId", - "dummyDealId", - ], -} as const; - -const dealSchemaV1: JSONSchemaType = { - ...workerInfoSchema, - description: - "Contains data related to your deployment, including, most importantly, deal id, that can be used to resolve workers in aqua", - properties: { - ...workerInfoSchema.properties, - dealId: { - type: "string", - description: - "Lowercased version of dealIdOriginal without 0x prefix. Currently unused. Was previously used to resolve workers in aqua", - }, - dealIdOriginal: { - type: "string", - description: - "Blockchain transaction id that you get when deploy workers. Can be used in aqua to get worker and host ids. Check out example in the aqua generated in the default template", - }, - chainNetwork: { - type: "string", - enum: CHAIN_ENV_OLD, - description: - "DEPRECATED. Blockchain network name that was used when deploying workers", - nullable: true, - }, - chainNetworkId: { - type: "integer", - description: "Blockchain network id that was used when deploying workers", - }, - matched: { - type: "boolean", - description: "Is deal matched", - nullable: true, - }, - }, - required: [ - ...workerInfoSchema.required, - "dealId", - "dealIdOriginal", - "chainNetworkId", - ], -} as const; - -const mapOfDealsSchemaV1 = { - type: "object", - description: "A map of created deals", - additionalProperties: dealSchemaV1, - properties: { - Worker_deployed_using_deals: dealSchemaV1, - }, - required: [], - nullable: true, -} as const satisfies JSONSchemaType>; - -const mapOfHostsSchemaV1 = { - type: "object", - description: "A map of directly deployed workers", - additionalProperties: hostSchemaV1, - properties: { - Worker_deployed_using_direct_hosting: hostSchemaV1, - }, - required: [], - nullable: true, -} as const satisfies JSONSchemaType>; - -const configSchemaV0: JSONSchemaType = { - type: "object", - additionalProperties: false, - properties: { - version: { type: "integer", const: 0 }, - deals: mapOfDealsSchemaV1, - hosts: mapOfHostsSchemaV1, - }, - required: ["version"], -} as const; - -type DealsV1 = Partial>>; -type HostsV1 = Partial>>; - -type ConfigV1 = { - version: 1; - deals?: DealsV1; - hosts?: HostsV1; -}; - -const configSchemaV1: JSONSchemaType = { - type: "object", - additionalProperties: false, - required: ["version"], - properties: { - version: { type: "integer", const: 1, description: "Config version" }, - deals: { - type: "object", - description: - "Info about deals created when deploying workers that is stored by environment that you deployed to", - additionalProperties: false, - nullable: true, - required: [], - properties: { - custom: mapOfDealsSchemaV1, - dar: mapOfDealsSchemaV1, - kras: mapOfDealsSchemaV1, - local: mapOfDealsSchemaV1, - stage: mapOfDealsSchemaV1, - }, - }, - hosts: { - description: - "Info about directly deployed workers that is stored by environment that you deployed to", - type: "object", - additionalProperties: false, - nullable: true, - required: [], - properties: { - custom: mapOfHostsSchemaV1, - dar: mapOfHostsSchemaV1, - kras: mapOfHostsSchemaV1, - local: mapOfHostsSchemaV1, - stage: mapOfHostsSchemaV1, - }, - }, - }, -}; - -type DealV2 = WorkerInfo & { - dealId: string; - dealIdOriginal: string; - chainNetworkId: number; - matched?: boolean; -}; - -type DealsV2 = Partial>>; -type HostsV2 = Partial>>; - -type ConfigV2 = { - version: 2; - deals?: DealsV2; - hosts?: HostsV2; -}; - -const dealSchemaV2 = { - ...workerInfoSchema, - description: - "Contains data related to your deployment, including, most importantly, deal id, that can be used to resolve workers in aqua", - properties: { - ...workerInfoSchema.properties, - dealId: { - type: "string", - description: - "Lowercased version of dealIdOriginal without 0x prefix. Currently unused. Was previously used to resolve workers in aqua", - }, - dealIdOriginal: { - type: "string", - description: - "Blockchain transaction id that you get when deploy workers. Can be used in aqua to get worker and host ids. Check out example in the aqua generated in the default template", - }, - chainNetwork: { - type: "string", - enum: CHAIN_ENV_OLD, - description: - "DEPRECATED. Blockchain network name that was used when deploying workers", - nullable: true, - }, - chainNetworkId: { - type: "integer", - description: "Blockchain network id that was used when deploying workers", - }, - matched: { - type: "boolean", - description: "Is deal matched", - nullable: true, - }, - }, - required: [ - ...workerInfoSchema.required, - "dealId", - "dealIdOriginal", - "chainNetworkId", - ], -} as const satisfies JSONSchemaType; - -const mapOfDealsSchemaV2 = { - type: "object", - description: "A map of created deals", - additionalProperties: dealSchemaV2, - properties: { - Worker_deployed_using_deals: dealSchemaV2, - }, - required: [], - nullable: true, -} as const satisfies JSONSchemaType>; - -const configSchemaV2 = { - type: "object", - additionalProperties: false, - properties: { - version: { type: "integer", const: 2, description: "Config version" }, - deals: { - type: "object", - description: - "Info about deals created when deploying workers that is stored by environment that you deployed to", - additionalProperties: false, - nullable: true, - properties: { - testnet: mapOfDealsSchemaV2, - custom: mapOfDealsSchemaV2, - mainnet: mapOfDealsSchemaV2, - local: mapOfDealsSchemaV2, - stage: mapOfDealsSchemaV2, - }, - }, - hosts: { - description: - "Info about directly deployed workers that is stored by environment that you deployed to", - type: "object", - additionalProperties: false, - nullable: true, - properties: { - testnet: mapOfHostsSchemaV1, - custom: mapOfHostsSchemaV1, - mainnet: mapOfHostsSchemaV1, - local: mapOfHostsSchemaV1, - stage: mapOfHostsSchemaV1, - }, - }, - }, - required: ["version"], -} as const satisfies JSONSchemaType; - -const latestSchemaObj = { - $id: `${TOP_LEVEL_SCHEMA_ID}/${WORKERS_CONFIG_FULL_FILE_NAME}`, - title: WORKERS_CONFIG_FULL_FILE_NAME, - description: `A result of app deployment. This file is created automatically after successful deployment using \`${CLI_NAME} workers deploy\` command`, - ...configSchemaV2, -} as const satisfies JSONSchemaType; - -const latestSchema: JSONSchemaType = latestSchemaObj; - -const validateConfigSchemaV0 = ajv.compile(configSchemaV0); -const validateConfigSchemaV1 = ajv.compile(configSchemaV1); - -export type Deal = DealV2; -export type Host = HostV1; -export type Deals = DealsV2; -export type Hosts = HostsV2; - -const migrations: Migrations = [ - async (config: Config): Promise => { - if (!validateConfigSchemaV0(config)) { - throw new Error( - `Migration error. Errors: ${await validationErrorToString( - validateConfigSchemaV0.errors, - )}`, - ); - } - - const configPath = join(getFluenceDir(), WORKERS_CONFIG_FULL_FILE_NAME); - - const deals: DealsV1 = {}; - - for (const [workerName, deal] of Object.entries(config.deals ?? {})) { - const env = await fluenceEnvOldPrompt( - `Select the environment that you used for deploying worker ${workerName} with dealId: ${deal.dealId} at ${configPath}`, - ); - - let dealsForEnv = deals[env]; - - if (dealsForEnv === undefined) { - dealsForEnv = {}; - deals[deal.chainNetwork ?? "dar"] = dealsForEnv; - } - - dealsForEnv[workerName] = deal; - } - - const hosts: HostsV1 = {}; - - for (const [workerName, host] of Object.entries(config.hosts ?? {})) { - const env = await fluenceEnvOldPrompt( - `Select the environment that you used for deploying worker ${workerName} with dummyDealId: ${host.dummyDealId} at ${configPath}`, - "kras", - ); - - let hostsForEnv = hosts[env]; - - if (hostsForEnv === undefined) { - hostsForEnv = {}; - hosts[env] = hostsForEnv; - } - - hostsForEnv[workerName] = host; - } - - return { - version: 1, - ...(isEmpty(deals) ? {} : { deals }), - ...(isEmpty(hosts) ? {} : { hosts }), - }; - }, - async (config: Config): Promise => { - if (!validateConfigSchemaV1(config)) { - throw new Error( - `Migration error. Errors: ${await validationErrorToString( - validateConfigSchemaV0.errors, - )}`, - ); - } - - const newConfig: ConfigV2 = { version: 2 }; - - for (const [env, configPerEnv] of Object.entries(config.deals ?? {})) { - if (!isChainEnvOld(env)) { - throw new Error( - `Unreachable. Migration error. Unknown env ${env} in ${WORKERS_CONFIG_FULL_FILE_NAME}`, - ); - } - - if (newConfig.deals === undefined) { - newConfig.deals = {}; - } - - newConfig.deals[chainEnvOldToNew(env)] = configPerEnv; - } - - for (const [env, configPerEnv] of Object.entries(config.hosts ?? {})) { - if (!isChainEnvOld(env)) { - throw new Error( - `Unreachable. Migration error. Unknown env ${env} in ${WORKERS_CONFIG_FULL_FILE_NAME}`, - ); - } - - if (newConfig.hosts === undefined) { - newConfig.hosts = {}; - } - - newConfig.hosts[chainEnvOldToNew(env)] = configPerEnv; - } - - return newConfig; - }, -]; - -type Config = ConfigV0 | ConfigV1 | ConfigV2; -type LatestConfig = ConfigV2; -export type WorkersConfig = InitializedConfig; -export type WorkersConfigReadonly = InitializedReadonlyConfig; - -const initConfigOptions: InitConfigOptions = { - allSchemas: [configSchemaV0, configSchemaV1, latestSchema], - latestSchema, - migrations, - name: WORKERS_CONFIG_FILE_NAME, - getConfigOrConfigDirPath: getFluenceDir, -}; - -const getDefault: GetDefaultConfig = () => { - return `# A result of app deployment. -# This file is updated automatically after successful deployment using \`fluence workers deploy\` command - -# config version -version: ${numToStr(latestSchemaObj.properties.version.const)} - -# deals: -# # A map of created deals -# ${DEFAULT_PUBLIC_FLUENCE_ENV}: -# ${DEFAULT_DEPLOYMENT_NAME}: -# # worker CID -# definition: bafkreigvy3k4racm6i6vvavtr5mdkllmfi2lfkmdk72gnzwk7zdnhajw4y -# # ISO timestamp of the time when the worker was deployed -# timestamp: 2023-07-07T11:23:52.353Z -# # deal ID used in aqua to resolve workers -# dealId: 799c4beb18ae084d57a90582c2cb8bb19098139e -# # original deal ID that you get after signing the contract -# dealIdOriginal: "0x799C4BEB18Ae084D57a90582c2Cb8Bb19098139E" -# # network ID that was used when deploying worker -# chainNetworkId: 1313161555 - -# hosts: -# # A map of directly deployed workers -# ${CHAIN_ENV[0]}: -# ${DEFAULT_WORKER_NAME}: -# # worker CID -# definition: bafkreicoctafgctpxf7jk4nynpnma4wdxpcecjtspsjmuidmag6enctnqa -# # worker installation spells -# # host_id and worker_id can be used to access the worker -# installation_spells: -# - host_id: 12D3KooWBM3SdXWqGaawQDGQ6JprtwswEg3FWGvGhmgmMez1vRbR -# spell_id: 9dbe4003-1232-4a20-9d52-5651c5cf4c5c -# worker_id: 12D3KooWLBQAdDFXz9vWnmgs6MyMfo25bhUTUEiLPsG94ppYq35w -# # ISO timestamp of the time when the worker was deployed -# timestamp: 2023-07-07T11:39:57.610Z -# # relay that was used when connecting to the network -# relayId: 12D3KooWPisGn7JhooWhggndz25WM7vQ2JmA121EV8jUDQ5xMovJ -`; -}; - -export const initNewWorkersConfig = getConfigInitFunction( - initConfigOptions, - getDefault, -); - -export const initNewWorkersConfigReadonly = getReadonlyConfigInitFunction( - initConfigOptions, - getDefault, -); - -export const workersSchema: JSONSchemaType = latestSchema; diff --git a/packages/cli/package/src/lib/configs/user/config/config.ts b/packages/cli/package/src/lib/configs/user/config/config.ts index 6da4b30b2..1332c21d4 100644 --- a/packages/cli/package/src/lib/configs/user/config/config.ts +++ b/packages/cli/package/src/lib/configs/user/config/config.ts @@ -19,7 +19,6 @@ import { color } from "@oclif/color"; import { commandObj, isInteractive } from "../../../commandObj.js"; import { CLI_NAME_FULL, AUTO_GENERATED } from "../../../const.js"; -import { createSecretKey, getUserSecretKeys } from "../../../keyPairs.js"; import { getUserConfigPath } from "../../../paths.js"; import { confirm } from "../../../prompt.js"; import { getConfigInitFunction } from "../../initConfigNew.js"; @@ -56,26 +55,5 @@ export async function initNewUserConfig() { return { countlyConsent, defaultSecretKeyName: AUTO_GENERATED }; })(); - const userSecretKey = await getUserSecretKey(AUTO_GENERATED); - - if (userSecretKey === undefined) { - await createSecretKey({ - name: AUTO_GENERATED, - isUser: true, - askToSetKeyAsDefaultInteractively: false, - userOrProjectConfig: userConfig, - }); - } - return userConfig; } - -export type UserConfig = Awaited>; - -export async function getUserSecretKey( - secretKeyName: string | undefined, -): Promise { - return (await getUserSecretKeys())[ - secretKeyName ?? (await initNewUserConfig()).defaultSecretKeyName - ]; -} diff --git a/packages/cli/package/src/lib/configs/user/config/config1.ts b/packages/cli/package/src/lib/configs/user/config/config1.ts index 2ef6029d1..f04b51f1d 100644 --- a/packages/cli/package/src/lib/configs/user/config/config1.ts +++ b/packages/cli/package/src/lib/configs/user/config/config1.ts @@ -15,19 +15,14 @@ * along with this program. If not, see . */ -import { rm } from "fs/promises"; - import { commandObj } from "../../../commandObj.js"; -import { AUTO_GENERATED } from "../../../const.js"; -import { genSecretKeyString, writeSecretKey } from "../../../keyPairs.js"; import type { ConfigOptions } from "../../initConfigNewTypes.js"; -import { initReadonlyUserSecretsConfig } from "../userSecrets.js"; import type { Config as PrevConfig } from "./config0.js"; export type Config = { countlyConsent: boolean; - defaultSecretKeyName: string; + defaultSecretKeyName?: string; }; export default { @@ -42,47 +37,19 @@ export default { defaultSecretKeyName: { type: "string", description: - "Secret key with this name will be used by default by js-client inside CLI to run Aqua code", + "DEPRECATED: Secret key with this name will be used by default by js-client inside CLI to run Aqua code", + nullable: true, }, }, - required: ["countlyConsent", "defaultSecretKeyName"], + required: ["countlyConsent"], }, - async migrate({ lastCheckForUpdates, ...config }) { + migrate({ lastCheckForUpdates, ...config }) { if (lastCheckForUpdates !== undefined) { commandObj.log( `Use of 'lastCheckForUpdates' field is deprecated. It's currently advised to install CLI without using npm`, ); } - const userSecretsConfig = await initReadonlyUserSecretsConfig(); - - await Promise.all( - (userSecretsConfig?.keyPairs ?? []).map(({ name, secretKey }) => { - return writeSecretKey({ - name, - secretKey, - isUser: true, - }); - }), - ); - - if (userSecretsConfig !== null) { - await rm(userSecretsConfig.$getPath()); - } - - const { defaultKeyPairName } = userSecretsConfig ?? {}; - - if (defaultKeyPairName === undefined) { - await writeSecretKey({ - name: AUTO_GENERATED, - secretKey: await genSecretKeyString(), - isUser: true, - }); - } - - return { - ...config, - defaultSecretKeyName: defaultKeyPairName ?? AUTO_GENERATED, - }; + return config; }, } as const satisfies ConfigOptions; diff --git a/packages/cli/package/src/lib/configs/user/userSecrets.ts b/packages/cli/package/src/lib/configs/user/userSecrets.ts deleted file mode 100644 index 6e62d6526..000000000 --- a/packages/cli/package/src/lib/configs/user/userSecrets.ts +++ /dev/null @@ -1,107 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import { color } from "@oclif/color"; -import type { JSONSchemaType } from "ajv"; - -import { - CLI_NAME, - TOP_LEVEL_SCHEMA_ID, - USER_SECRETS_CONFIG_FILE_NAME, - USER_SECRETS_CONFIG_FULL_FILE_NAME, -} from "../../const.js"; -import { - validateHasDefault, - validateBatch, - validateUnique, - type ValidationResult, -} from "../../helpers/validations.js"; -import { ensureUserFluenceDir } from "../../paths.js"; -import { - getReadonlyConfigInitFunction, - type InitConfigOptions, - type InitializedConfig, - type InitializedReadonlyConfig, - type Migrations, -} from "../initConfig.js"; -import { type ConfigKeyPair, configKeyPairSchema } from "../keyPair.js"; - -type ConfigV0 = { - version: 0; - keyPairs: Array; - defaultKeyPairName: string; -}; - -const configSchemaV0: JSONSchemaType = { - type: "object", - $id: `${TOP_LEVEL_SCHEMA_ID}/${USER_SECRETS_CONFIG_FULL_FILE_NAME}`, - title: USER_SECRETS_CONFIG_FULL_FILE_NAME, - description: `Defines user's secret keys that can be used across different Fluence projects. You can manage user's keys using commands from \`${CLI_NAME} key\` group of commands with \`--user\` flag`, - properties: { - defaultKeyPairName: { type: "string" }, - keyPairs: { - title: "Key Pairs", - type: "array", - items: configKeyPairSchema, - }, - version: { type: "integer", const: 0 }, - }, - required: ["version", "keyPairs", "defaultKeyPairName"], -}; - -const migrations: Migrations = []; - -const validate = (config: LatestConfig): ValidationResult => { - return validateBatch( - validateUnique( - config.keyPairs, - ({ name }): string => { - return name; - }, - (name): string => { - return `There are multiple key-pairs with the same name ${color.yellow( - name, - )}`; - }, - ), - validateHasDefault( - config.keyPairs, - config.defaultKeyPairName, - ({ name }): string => { - return name; - }, - `Default key-pair ${color.yellow(config.defaultKeyPairName)} not found`, - ), - ); -}; - -type Config = ConfigV0; -type LatestConfig = ConfigV0; -export type UserSecretsConfig = InitializedConfig; -export type UserSecretsConfigReadonly = InitializedReadonlyConfig; - -const initConfigOptions: InitConfigOptions = { - allSchemas: [configSchemaV0], - latestSchema: configSchemaV0, - migrations, - name: USER_SECRETS_CONFIG_FILE_NAME, - getConfigOrConfigDirPath: ensureUserFluenceDir, - validate, -}; - -export const initReadonlyUserSecretsConfig = - getReadonlyConfigInitFunction(initConfigOptions); diff --git a/packages/cli/package/src/lib/const.ts b/packages/cli/package/src/lib/const.ts index c0a9a8962..04834546f 100644 --- a/packages/cli/package/src/lib/const.ts +++ b/packages/cli/package/src/lib/const.ts @@ -15,14 +15,7 @@ * along with this program. If not, see . */ -import { join } from "node:path"; - -import { color } from "@oclif/color"; import { Args, Flags } from "@oclif/core"; -import type { Flag, OutputFlags, ParserOutput } from "@oclif/core/interfaces"; -import camelCase from "lodash-es/camelCase.js"; -import upperFirst from "lodash-es/upperFirst.js"; -import xbytes from "xbytes"; import { getIsUnion, @@ -34,86 +27,26 @@ import { import { CHAIN_RPC_CONTAINER_NAME, CHAIN_RPC_PORT, - IPFS_PORT, } from "./configs/project/chainContainers.js"; -import { aquaComment } from "./helpers/utils.js"; export const CLI_NAME = "fluence"; export const CLI_NAME_FULL = "Fluence CLI"; -const GITHUB_REPO_NAME = "https://github.com/fluencelabs/cli"; export const NODE_JS_MAJOR_VERSION = 22; -export const DEFAULT_IPFS_ADDRESS = "/dns4/ipfs.fluence.dev/tcp/5001"; export const FLT_SYMBOL = "FLT"; export const PT_SYMBOL = "USDC"; export const MAX_TOKEN_AMOUNT_KEYWORD = "max"; -export const RUST_WASM32_WASI_TARGET = "wasm32-wasi"; - -export const DEFAULT_MARINE_BUILD_ARGS = `--release`; - -export const currencyProperties = ["minPricePerCuPerEpoch"] as const; -export type CurrencyProperty = (typeof currencyProperties)[number]; +const DEFAULT_PRICE_PER_CU_PER_EPOCH_PROVIDER = "0.33"; -export const COLLATERAL_DEFAULT = 1; -export const DEFAULT_PRICE_PER_CU_PER_EPOCH_PROVIDER = "0.33"; -export const DEFAULT_PRICE_PER_CU_PER_EPOCH_DEVELOPER = "0.33"; -export const DEFAULT_DEAL_ACTIVE_DURATION_FOR_LOCAL_ENV = BigInt(60 * 60 * 24); - -export const defaultNumberProperties: Record = { - minPricePerCuPerEpoch: DEFAULT_PRICE_PER_CU_PER_EPOCH_PROVIDER, -}; - -export const MIN_MEMORY_PER_MODULE_STR = "2 MiB"; -export const MIN_MEMORY_PER_MODULE = xbytes.parseSize( - MIN_MEMORY_PER_MODULE_STR, -); +export const defaultNumberProperties: Record<"minPricePerCuPerEpoch", string> = + { + minPricePerCuPerEpoch: DEFAULT_PRICE_PER_CU_PER_EPOCH_PROVIDER, + }; export const COMPUTE_UNIT_MEMORY_STR = "2GB"; -export const COMPUTE_UNIT_MEMORY = xbytes.parseSize(COMPUTE_UNIT_MEMORY_STR); - -export const DEFAULT_CURL_EFFECTOR_CID = - "bafybeicorj3sl6fhqpuubmt5r2n7plkjkmto6jxqckgte2624dj5zvzufm"; - -export const DEFAULT_VM_EFFECTOR_CID = - "bafkreidfw2so3evkkkky7bodnamzg32zweunfkthna362r7e6a63cnqxiy"; - -const byteUnits = [ - "kB", - "KB", - "kiB", - "KiB", - "KIB", - "mB", - "MB", - "miB", - "MiB", - "MIB", - "gB", - "GB", - "giB", - "GiB", - "GIB", -]; - -export const BYTES_PATTERN = `^\\d+(\\.\\d+)?(\\s?)(${byteUnits.join("|")})$`; -export const BYTES_FORMAT = `[number][whitespace?][B] where ? is an optional field and B is one of the following: ${byteUnits.join( - ", ", -)}`; -export const MAX_HEAP_SIZE_DESCRIPTION = `DEPRECATED. Use \`totalMemoryLimit\` service property instead. Max size of the heap that a module can allocate in format: ${BYTES_FORMAT}`; - -export const U32_MAX = 4_294_967_295; - -export const DEFAULT_NUMBER_OF_COMPUTE_UNITS_ON_NOX = 32; - -export const CLI_CONNECTOR_URL = "https://cli-connector.fluence.dev"; -export const WC_PROJECT_ID = "70c1c5ed2a23e7383313de1044ddce7e"; -export const WC_METADATA = { - name: CLI_NAME, - description: `${CLI_NAME_FULL} is designed to be the only tool that you need to manage the life cycle of applications written on Fluence.`, - url: GITHUB_REPO_NAME, - icons: [], -}; + +export const DEFAULT_NUMBER_OF_COMPUTE_UNITS_ON_PEER = 32; export type FluenceEnv = (typeof CHAIN_ENV)[number]; export const isFluenceEnv = getIsUnion(CHAIN_ENV); @@ -132,12 +65,11 @@ export function fluenceOldEnvToNewEnv(env: FluenceEnvOld): FluenceEnv { )[env]; } -export const LOCAL_IPFS_ADDRESS = `/ip4/127.0.0.1/tcp/${IPFS_PORT}`; export const TCP_PORT_START = 977; export const WEB_SOCKET_PORT_START = 999; export const HTTP_PORT_START = 918; export const DEFAULT_AQUAVM_POOL_SIZE = 2; -export const DEFAULT_NUMBER_OF_LOCAL_NET_NOXES = 3; +export const DEFAULT_NUMBER_OF_LOCAL_NET_PEERS = 3; export const WS_CHAIN_URLS: Record = { mainnet: "wss://ws.mainnet.fluence.dev", @@ -146,105 +78,34 @@ export const WS_CHAIN_URLS: Record = { local: `wss://${CHAIN_RPC_CONTAINER_NAME}:${CHAIN_RPC_PORT}`, }; -export const AQUA_EXT = "aqua"; -export const TS_EXT = "ts"; -export const JS_EXT = "js"; export const JSON_EXT = "json"; export const YAML_EXT = "yaml"; export const YML_EXT = "yml"; -export const WASM_EXT = "wasm"; -export const TOML_EXT = "toml"; export const CLI_CONNECTOR_DIR_NAME = "cli-connector"; export const DOT_FLUENCE_DIR_NAME = ".fluence"; -export const AQUA_DEPENDENCIES_DIR_NAME = "aqua-dependencies"; -export const SERVICE_CONFIGS_DIR_NAME = "service-configs"; -export const MODULE_VOLUMES_DIR_NAME = "volumes"; -export const MODULE_VOLUMES_SERVICES_DIR_NAME = "services"; export const SCHEMAS_DIR_NAME = "schemas"; -export const SRC_DIR_NAME = "src"; -export const FRONTEND_DIR_NAME = "frontend"; -export const GATEWAY_DIR_NAME = "gateway"; -export const TMP_DIR_NAME = "tmp"; -export const VSCODE_DIR_NAME = ".vscode"; -export const NODE_MODULES_DIR_NAME = "node_modules"; -export const AQUA_DIR_NAME = "aqua"; -export const COMPILED_AQUA_DIR_NAME = "compiled-aqua"; -export const MODULES_DIR_NAME = "modules"; -export const SERVICES_DIR_NAME = "services"; -export const SPELLS_DIR_NAME = "spells"; -export const NPM_DIR_NAME = "npm"; -export const CARGO_DIR_NAME = "cargo"; -export const BIN_DIR_NAME = "bin"; export const COUNTLY_DIR_NAME = "countly"; export const SECRETS_DIR_NAME = "secrets"; export const BACKUPS_DIR_NAME = "backups"; -export const CONFIGS_DIR_NAME = "configs"; -export const CCP_CONFIGS_DIR_NAME = "ccp-configs"; -export const FLUENCE_CONFIG_FILE_NAME = `fluence`; export const PROVIDER_CONFIG_FILE_NAME = `provider`; export const PROVIDER_SECRETS_CONFIG_FILE_NAME = `provider-secrets`; export const PROVIDER_ARTIFACTS_CONFIG_FILE_NAME = `provider-artifacts`; -export const WORKERS_CONFIG_FILE_NAME = `workers`; -export const PROJECT_SECRETS_CONFIG_FILE_NAME = `project-secrets`; -export const USER_SECRETS_CONFIG_FILE_NAME = `user-secrets`; export const USER_CONFIG_FILE_NAME = `config`; -export const MODULE_CONFIG_FILE_NAME = `module`; -export const SERVICE_CONFIG_FILE_NAME = `service`; -export const SPELL_CONFIG_FILE_NAME = `spell`; export const ENV_CONFIG_FILE_NAME = `env`; -export const DOCKER_COMPOSE_FILE_NAME = `docker-compose`; +const DOCKER_COMPOSE_FILE_NAME = `docker-compose`; -export const FLUENCE_CONFIG_FULL_FILE_NAME = `${FLUENCE_CONFIG_FILE_NAME}.${YAML_EXT}`; export const PROVIDER_CONFIG_FULL_FILE_NAME = `${PROVIDER_CONFIG_FILE_NAME}.${YAML_EXT}`; export const PROVIDER_SECRETS_CONFIG_FULL_FILE_NAME = `${PROVIDER_SECRETS_CONFIG_FILE_NAME}.${YAML_EXT}`; export const PROVIDER_ARTIFACTS_CONFIG_FULL_FILE_NAME = `${PROVIDER_ARTIFACTS_CONFIG_FILE_NAME}.${YAML_EXT}`; -export const WORKERS_CONFIG_FULL_FILE_NAME = `${WORKERS_CONFIG_FILE_NAME}.${YAML_EXT}`; -export const PROJECT_SECRETS_FULL_CONFIG_FILE_NAME = `${PROJECT_SECRETS_CONFIG_FILE_NAME}.${YAML_EXT}`; -export const USER_SECRETS_CONFIG_FULL_FILE_NAME = `${USER_SECRETS_CONFIG_FILE_NAME}.${YAML_EXT}`; export const USER_CONFIG_FULL_FILE_NAME = `${USER_CONFIG_FILE_NAME}.${YAML_EXT}`; -export const MODULE_CONFIG_FULL_FILE_NAME = `${MODULE_CONFIG_FILE_NAME}.${YAML_EXT}`; -export const SERVICE_CONFIG_FULL_FILE_NAME = `${SERVICE_CONFIG_FILE_NAME}.${YAML_EXT}`; -export const SPELL_CONFIG_FULL_FILE_NAME = `${SPELL_CONFIG_FILE_NAME}.${YAML_EXT}`; export const ENV_CONFIG_FULL_FILE_NAME = `${ENV_CONFIG_FILE_NAME}.${YAML_EXT}`; export const DOCKER_COMPOSE_FULL_FILE_NAME = `${DOCKER_COMPOSE_FILE_NAME}.${YAML_EXT}`; -export const MAIN_AQUA_FILE_NAME = `main.${AQUA_EXT}`; -export const AQUA_SERVICES_FILE_NAME = `services.${AQUA_EXT}`; -export const DEALS_FILE_NAME = "deals"; -export const DEALS_FULL_FILE_NAME = `${DEALS_FILE_NAME}.${AQUA_EXT}`; -export const HOSTS_FILE_NAME = "hosts"; -export const HOSTS_FULL_FILE_NAME = `${HOSTS_FILE_NAME}.${AQUA_EXT}`; -export const SPELL_AQUA_FILE_NAME = `spell.${AQUA_EXT}`; - export const GITIGNORE_FILE_NAME = ".gitignore"; -export const PACKAGE_JSON_FILE_NAME = `package.${JSON_EXT}`; -export const TS_CONFIG_FILE_NAME = `tsconfig.${JSON_EXT}`; -export const EXTENSIONS_JSON_FILE_NAME = `extensions.${JSON_EXT}`; - -export const INDEX_TS_FILE_NAME = `index.${TS_EXT}`; -export const INDEX_JS_FILE_NAME = `index.${JS_EXT}`; -export const INDEX_HTML_FILE_NAME = `index.html`; - -export const SERVER_TS_FILE_NAME = `server.${TS_EXT}`; -export const SERVER_JS_FILE_NAME = `server.${JS_EXT}`; - -export const SERVICE_CONFIG_TOML_POSTFIX = `_Config.${TOML_EXT}`; -export const CARGO_TOML = `Cargo.${TOML_EXT}`; - -export const README_MD_FILE_NAME = `README.md`; - -export const FS_OPTIONS = { - encoding: "utf8", -} as const; - -export const TOP_LEVEL_SCHEMA_ID = "https://fluence.dev/schemas"; - export const AUTO_GENERATED = "auto-generated"; -export const DEFAULT_DEPLOYMENT_NAME = "myDeployment"; -export const DEFAULT_WORKER_NAME = "workerName"; export const NO_INPUT_FLAG_NAME = "no-input"; export const NO_INPUT_FLAG = { @@ -260,7 +121,8 @@ const fluenceEnvFlagAndArg = { }; export const ENV_FLAG_NAME = "env"; -export const ENV_FLAG = { + +const ENV_FLAG = { [ENV_FLAG_NAME]: Flags.string(fluenceEnvFlagAndArg), }; @@ -269,127 +131,24 @@ export const ENV_ARG = { [ENV_ARG_NAME]: Args.string(fluenceEnvFlagAndArg), }; -export const OFF_AQUA_LOGS_FLAG = { - "off-aqua-logs": Flags.boolean({ - default: false, - description: - "Turns off logs from Console.print in aqua and from IPFS service", - }), -}; - -export const USE_F64_FLAG = { - f64: Flags.boolean({ - default: false, - description: - "Convert all numbers to f64. Useful for arrays objects that contain numbers of different types in them. Without this flag, numbers will be converted to u64, i64 or f64 depending on their value", - }), -}; - -export const CUSTOM_TYPES_FLAG = { - types: Flags.string({ - description: - "Experimental! Path to a file with custom types. Must be a list with objects that have 'name' and 'properties'. 'properties' must be a list of all custom type properties", - helpValue: "", - }), -}; - -export const NO_BUILD_FLAG = { - "no-build": Flags.boolean({ - default: false, - description: "Don't build the project before running the command", - }), -}; +export const ALL_FLAG_VALUE = "all"; -export const IMPORT_FLAG = { - import: Flags.string({ - description: - "Path to a directory to import aqua files from. May be used several times", - helpValue: "", - multiple: true, - }), -}; +export const PEER_NAMES_FLAG_NAME = "peer-names"; -export const TRACING_FLAG = { - tracing: Flags.boolean({ - description: "Compile aqua in tracing mode (for debugging purposes)", - default: false, - }), +const PEER_NAMES_FLAG_CONFIG = { + description: `Comma-separated names of peers from ${PROVIDER_CONFIG_FULL_FILE_NAME}. To use all of your peers: --${PEER_NAMES_FLAG_NAME} ${ALL_FLAG_VALUE}`, + helpValue: "", }; -export const ALL_FLAG_VALUE = "all"; - -export const NOX_NAMES_FLAG_NAME = "nox-names"; -export const NOX_NAMES_FLAG_CONFIG = { - description: `Comma-separated names of noxes from ${PROVIDER_CONFIG_FULL_FILE_NAME}. To use all of your noxes: --${NOX_NAMES_FLAG_NAME} ${ALL_FLAG_VALUE}`, - helpValue: "", -}; -export const NOX_NAMES_FLAG = { - [NOX_NAMES_FLAG_NAME]: Flags.string(NOX_NAMES_FLAG_CONFIG), +const PEER_NAMES_FLAG = { + [PEER_NAMES_FLAG_NAME]: Flags.string(PEER_NAMES_FLAG_CONFIG), }; -export const LOG_LEVEL_COMPILER_FLAG_NAME = "log-level-compiler"; - -export const AQUA_LOG_LEVELS = [ - "all", - "trace", - "debug", - "info", - "warn", - "error", - "off", -] as const; - -export type AquaLogLevel = (typeof AQUA_LOG_LEVELS)[number]; -export const isAquaLogLevel = getIsUnion(AQUA_LOG_LEVELS); - -export const aquaLogLevelsString = `Must be one of: ${AQUA_LOG_LEVELS.join( - ", ", -)}`; - -export const INPUT_FLAG_NAME = "input"; -export const COMPILE_AQUA_PROPERTY_NAME = "compileAqua"; -export const INPUT_FLAG_EXPLANATION = `. If --${INPUT_FLAG_NAME} flag is used - then content of '${COMPILE_AQUA_PROPERTY_NAME}' property in ${FLUENCE_CONFIG_FULL_FILE_NAME} will be ignored`; - -export const COMMON_AQUA_COMPILATION_FLAGS = { - ...IMPORT_FLAG, - [INPUT_FLAG_NAME]: Flags.string({ - description: - "Path to an aqua file or a directory that contains your aqua files", - helpValue: "", - char: "i", - }), - const: Flags.string({ - description: "Constants to be passed to the compiler", - helpValue: "", - multiple: true, - }), - [LOG_LEVEL_COMPILER_FLAG_NAME]: Flags.string({ - description: `Set log level for the compiler. ${aquaLogLevelsString}`, - helpValue: "", - }), - "no-relay": Flags.boolean({ - default: false, - description: "Do not generate a pass through the relay node", - }), - "no-xor": Flags.boolean({ - default: false, - description: "Do not generate a wrapper that catches and displays errors", - }), - ...TRACING_FLAG, - "no-empty-response": Flags.boolean({ - default: true, - description: - "Do not generate response call if there are no returned values", - }), -} as const; - -export type CommonAquaCompilationFlags = FromFlagsDef< - typeof COMMON_AQUA_COMPILATION_FLAGS ->; +export const PEERS_FLAG_NAME = "peers"; -export const NOXES_FLAG = { - noxes: Flags.integer({ - description: `Number of Compute Peers to generate when a new ${PROVIDER_CONFIG_FULL_FILE_NAME} is created`, +export const PEERS_FLAG = { + [PEERS_FLAG_NAME]: Flags.integer({ + description: `Number of peers to generate when a new ${PROVIDER_CONFIG_FULL_FILE_NAME} is created`, }), }; @@ -417,6 +176,16 @@ export const OFFER_FLAGS = { }), }; +export const PEER_AND_OFFER_NAMES_FLAGS = { + ...PEER_NAMES_FLAG, + ...OFFER_FLAG, +}; + +export type PeerAndOfferNameFlags = { + [PEER_NAMES_FLAG_NAME]?: string | undefined; + [OFFER_FLAG_NAME]?: string | undefined; +}; + export const PRIV_KEY_FLAG_NAME = "priv-key"; export const PRIV_KEY_FLAG = { @@ -441,7 +210,6 @@ export const CHAIN_FLAGS = { }; export const DEAL_IDS_FLAG_NAME = "deal-ids"; -export const DEPLOYMENT_NAMES_ARG_NAME = "DEPLOYMENT-NAMES"; export const DEAL_IDS_FLAG = { [DEAL_IDS_FLAG_NAME]: Flags.string({ @@ -450,24 +218,6 @@ export const DEAL_IDS_FLAG = { }), }; -export const DEPLOYMENT_NAMES_ARG = { - [DEPLOYMENT_NAMES_ARG_NAME]: Args.string({ - description: `Comma separated names of deployments. Can't be used together with --${DEAL_IDS_FLAG_NAME} flag`, - helpValue: "", - }), -}; - -export const MARINE_BUILD_ARGS_FLAG_NAME = "marine-build-args"; -export const MARINE_BUILD_ARGS_PROPERTY = "marineBuildArgs"; -export const IPFS_ADDR_PROPERTY = "ipfsAddr"; - -export const MARINE_BUILD_ARGS_FLAG = { - [MARINE_BUILD_ARGS_FLAG_NAME]: Flags.string({ - description: `Space separated \`cargo build\` flags and args to pass to marine build. Overrides '${MARINE_BUILD_ARGS_PROPERTY}' property in ${FLUENCE_CONFIG_FULL_FILE_NAME}. Default: ${DEFAULT_MARINE_BUILD_ARGS}`, - helpValue: "<--flag arg>", - }), -}; - export const DOCKER_COMPOSE_FLAGS = { flags: Flags.string({ description: "Space separated flags to pass to `docker compose`", @@ -475,48 +225,16 @@ export const DOCKER_COMPOSE_FLAGS = { }), }; -export const TTL_FLAG_NAME = "ttl"; -export const DIAL_TIMEOUT_FLAG_NAME = "dial-timeout"; - -export const FLUENCE_CLIENT_FLAGS = { - sk: Flags.string({ - char: "k", - description: - "Name of the secret key for js-client inside CLI to use. If not specified, will use the default key for the project. If there is no fluence project or there is no default key, will use user's default key", - helpValue: "", - }), - relay: Flags.string({ - description: "Relay for Fluence js-client to connect to", - helpValue: "", - }), - [TTL_FLAG_NAME]: Flags.integer({ - description: - "Particle Time To Live since 'now'. After that, particle is expired and not processed.", - default: 15_000, - helpValue: "", - }), - [DIAL_TIMEOUT_FLAG_NAME]: Flags.integer({ - description: "Timeout for Fluence js-client to connect to relay peer", - default: 15_000, - helpValue: "", - }), - "particle-id": Flags.boolean({ - default: false, - description: "Print particle ids when running Fluence js-client", - }), - ...ENV_FLAG, -} as const; - export const CC_IDS_FLAG_NAME = "cc-ids"; export const CC_FLAGS = { - [NOX_NAMES_FLAG_NAME]: Flags.string({ - ...NOX_NAMES_FLAG_CONFIG, + [PEER_NAMES_FLAG_NAME]: Flags.string({ + ...PEER_NAMES_FLAG_CONFIG, exclusive: [CC_IDS_FLAG_NAME], }), [CC_IDS_FLAG_NAME]: Flags.string({ description: "Comma separated capacity commitment IDs", - exclusive: [NOX_NAMES_FLAG_NAME], + exclusive: [PEER_NAMES_FLAG_NAME], }), ...OFFER_FLAG, }; @@ -529,307 +247,20 @@ export const JSON_FLAG = { }), }; -export type FluenceClientFlags = FromFlagsDef; - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export type FromFlagsDef> = Omit< - ParserOutput["flags"] extends infer S - ? { - [Property in keyof S]: S[Property] extends Flag ? F : never; - } - : never, - "json" ->; - -export const MODULE_TYPE_RUST = "rust"; -export const MODULE_TYPE_COMPILED = "compiled"; -export const MODULE_TYPES = [MODULE_TYPE_RUST, MODULE_TYPE_COMPILED] as const; -export type ModuleType = (typeof MODULE_TYPES)[number]; - -export const TEMPLATES = ["quickstart", "minimal", "ts", "js"] as const; -export type Template = (typeof TEMPLATES)[number]; -export const isTemplate = getIsUnion(TEMPLATES); - -export const PACKAGE_NAME = "PACKAGE-NAME"; -export const PACKAGE_NAME_AND_VERSION_ARG_NAME = `${PACKAGE_NAME} | PACKAGE-NAME@VERSION`; - export const RECOMMENDED_GITIGNORE_CONTENT = `.idea .DS_Store /${DOT_FLUENCE_DIR_NAME}/${SECRETS_DIR_NAME} /${DOT_FLUENCE_DIR_NAME}/${PROVIDER_SECRETS_CONFIG_FULL_FILE_NAME} /${DOT_FLUENCE_DIR_NAME}/${SCHEMAS_DIR_NAME} -/${DOT_FLUENCE_DIR_NAME}/${TMP_DIR_NAME} -/${DOT_FLUENCE_DIR_NAME}/${SERVICE_CONFIGS_DIR_NAME} -/${DOT_FLUENCE_DIR_NAME}/${AQUA_DEPENDENCIES_DIR_NAME}/${PACKAGE_JSON_FILE_NAME} -${SRC_DIR_NAME}/${FRONTEND_DIR_NAME}/${SRC_DIR_NAME}/${COMPILED_AQUA_DIR_NAME}/ -**/node_modules -**/target/ -.repl_history +.terraform +.terraform.lock.hcl `; export const IS_TTY = Boolean(process.stdout.isTTY && process.stdin.isTTY); export const IS_DEVELOPMENT = process.env["NODE_ENV"] === "development"; -export const MARINE_CARGO_DEPENDENCY = "marine"; -const MREPL_CARGO_DEPENDENCY = "mrepl"; -export const MARINE_RS_SDK_CARGO_DEPENDENCY = "marine-rs-sdk"; -export const MARINE_RS_SDK_TEST_CARGO_DEPENDENCY = "marine-rs-sdk-test"; - -export const AQUA_LIB_NPM_DEPENDENCY = "@fluencelabs/aqua-lib"; -export const JS_CLIENT_NPM_DEPENDENCY = "@fluencelabs/js-client"; -export const TYPESCRIPT_NPM_DEPENDENCY = "typescript"; - -export const marineAndMreplDependencies = [ - MARINE_CARGO_DEPENDENCY, - MREPL_CARGO_DEPENDENCY, -] as const; - -export type MarineOrMrepl = (typeof marineAndMreplDependencies)[number]; -export const isMarineOrMrepl = getIsUnion(marineAndMreplDependencies); - -export const SEPARATOR = `\n\n${color.yellow( - `^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^`, -)}\n\n`; - -export const RUN_DEPLOYED_SERVICES_FUNCTION_NAME = "runDeployedServices"; -export const RUN_DEPLOYED_SERVICES_FUNCTION_CALL = `${RUN_DEPLOYED_SERVICES_FUNCTION_NAME}()`; - -const RUN_DEPLOYED_SERVICE_AQUA = ` --- example of running services deployed using \`${CLI_NAME} deploy\` --- with worker '${DEFAULT_DEPLOYMENT_NAME}' which has service 'MyService' with method 'greeting' - -export runDeployedServices, showSubnet - -data Answer: - answer: ?string - worker: Worker - -func ${RUN_DEPLOYED_SERVICES_FUNCTION_NAME}() -> []Answer: - deals <- Deals.get() - dealId = deals.${DEFAULT_DEPLOYMENT_NAME}!.dealIdOriginal - answers: *Answer - on HOST_PEER_ID: - subnet <- Subnet.resolve(dealId) - if subnet.success == false: - Console.print(["Failed to resolve subnet: ", subnet.error]) - - for w <- subnet.workers: - if w.worker_id == nil: - answers <<- Answer(answer=nil, worker=w) - else: - on w.worker_id! via w.host_id: - answer <- MyService.greeting("fluence") - answers <<- Answer(answer=?[answer], worker=w) - - <- answers - -data WorkerServices: - host_id: string - worker_id: ?string - services: ?[]string - spells: ?[]string - -func showSubnet() -> []WorkerServices: - deals <- Deals.get() - dealId = deals.${DEFAULT_DEPLOYMENT_NAME}!.dealIdOriginal - on HOST_PEER_ID: - subnet <- Subnet.resolve(dealId) - if subnet.success == false: - Console.print(["Failed to resolve subnet: ", subnet.error]) - - services: *WorkerServices - for w <- subnet.workers: - if w.worker_id != nil: - on w.worker_id! via w.host_id: - -- get list of all services on this worker - srvs <- Srv.list() - - -- gather spells and services aliases - spells_aliases: *string - services_aliases: *string - for s <- srvs: - if s.aliases.length != 0: - if s.service_type == "spell": - spells_aliases <<- s.aliases[0] - if s.service_type == "service": - services_aliases <<- s.aliases[0] - - services <<- WorkerServices(host_id=w.host_id, worker_id=w.worker_id, services=?[services_aliases], spells=?[spells_aliases]) - else: - services <<- WorkerServices(host_id=w.host_id, worker_id=nil, services=nil, spells=nil) - - <- services -`; - -const RUN_DEPLOYED_SERVICE_AQUA_COMMENT = aquaComment( - RUN_DEPLOYED_SERVICE_AQUA, -); - -export const getMainAquaFileContent = ( - commentOutRunDeployedServicesAqua: boolean, -) => { - return `aqua Main - -import "${AQUA_LIB_NPM_DEPENDENCY}/builtin.aqua" -import "${AQUA_LIB_NPM_DEPENDENCY}/subnet.aqua" - -use "${DEALS_FULL_FILE_NAME}" -use "${HOSTS_FULL_FILE_NAME}" -import "services.aqua" - --- IMPORTANT: Add exports for all functions that you want to run -export helloWorld, helloWorldRemote, getInfo, getInfos - --- DOCUMENTATION: --- https://fluence.dev - - -${ - commentOutRunDeployedServicesAqua - ? RUN_DEPLOYED_SERVICE_AQUA_COMMENT - : RUN_DEPLOYED_SERVICE_AQUA -} - --- local -func helloWorld(name: string) -> string: - <- Op.concat_strings("Hello, ", name) - --- remote -func helloWorldRemote(name: string) -> string: - on HOST_PEER_ID: - hello_msg <- helloWorld(name) - from_msg <- Op.concat_strings(hello_msg, "! From ") - from_peer_msg <- Op.concat_strings(from_msg, HOST_PEER_ID) - <- from_peer_msg - --- request response -func getInfo() -> Info, PeerId: - on HOST_PEER_ID: - info <- Peer.identify() - <- info, HOST_PEER_ID - --- iterate through several peers -func getInfos(peers: []PeerId) -> []Info: - infos: *Info - for p <- peers: - on p: - infos <- Peer.identify() - <- infos -`; -}; - -export function getSpellAquaFileContent(spellName: string) { - const moduleName = upperFirst(camelCase(spellName)); - - return `aqua ${moduleName} - --- Note: spell main function must be exported -export spell - -import Spell from "@fluencelabs/spell/spell_service.aqua" - -func spell(): - Spell "${spellName}" - Spell.store_log("Spell '${spellName}' is working!") -`; -} - -// TODO: use relative(pathToFluenceProject, pathToAquaFolder). Won't work rn, need to refactor things. -const TEMPLATE_CONTENT_BASE = `- Default Marine service - \`${join( - SRC_DIR_NAME, - SERVICES_DIR_NAME, -)}\`. -- Basic aqua functions - \`${join(SRC_DIR_NAME, AQUA_DIR_NAME)}\`. -- Fluence HTTP Gateway for proxying Aqua execution - \`${join( - SRC_DIR_NAME, - GATEWAY_DIR_NAME, -)}\`.`; - -const TEMPLATE_CONTENT_FRONTEND = `- Fluence frontend template - \`${join( - SRC_DIR_NAME, - FRONTEND_DIR_NAME, -)}\`.`; - -const QUICKSTART_README = `# Fluence Quickstart Template - -## Content - -${TEMPLATE_CONTENT_BASE} - -## Usage - -\`\`\`sh -# You can deploy right away with an example worker that contains an example service -fluence deploy - -# Run the deployed code -fluence run -f 'runDeployedServices()' -\`\`\` -`; - -const MINIMAL_README = `# Fluence Minimal Template - -## Usage - -\`\`\`sh -# Generate a service template and add it to the default worker -fluence service new myService - -# Deploy the default worker -fluence deploy - -# Uncomment \`runDeployedServices\` aqua function in \`src/aqua/main.aqua\` and run it -fluence run -f 'runDeployedServices()' -\`\`\` -`; - -function getTsOrJsReadme(isJS: boolean) { - const jsOrTsString = isJS ? "JavaScript" : "TypeScript"; - return `# Fluence ${jsOrTsString} Template - -## Content - -${TEMPLATE_CONTENT_BASE} -${TEMPLATE_CONTENT_FRONTEND} - -## Usage - -\`\`\`sh -# \`cd\` into \`frontend\` directory -cd src/frontend - -# Install dependencies -npm i - -# Run example code -npm run dev - -# You can also deploy deal and run the deployed code - -# Deploy the default worker -fluence deploy - -# Compile aqua to ${jsOrTsString} so it contains info about deployed services -fluence aqua - -# Try running \`runDeployedServices\` aqua function in the browser -\`\`\` -`; -} - -export const SERVICE_INTERFACE_FILE_HEADER = "aqua Services declares *"; - -export const READMEs: Record = { - quickstart: QUICKSTART_README, - minimal: MINIMAL_README, - ts: getTsOrJsReadme(false), - js: getTsOrJsReadme(true), -}; - export const DEFAULT_OFFER_NAME = "defaultOffer"; export const DEFAULT_CC_STAKER_REWARD = 20; export const DEFAULT_CC_DURATION = "100 days"; export const DURATION_EXAMPLE = "in human-readable format. Example: 1 months 1 days"; - -export const WORKER_SPELL = "worker-spell"; diff --git a/packages/cli/package/src/lib/countly.ts b/packages/cli/package/src/lib/countly.ts index 04fbaa812..82acdca9d 100644 --- a/packages/cli/package/src/lib/countly.ts +++ b/packages/cli/package/src/lib/countly.ts @@ -21,18 +21,11 @@ import { } from "../errorInterceptor.js"; import { commandObj } from "./commandObj.js"; -import type { FluenceConfig } from "./configs/project/fluence.js"; import { initNewUserConfig } from "./configs/user/config/config.js"; import { IS_DEVELOPMENT } from "./const.js"; import { ensureDir, getUserCountlyDir } from "./paths.js"; -type InitCountlyArgs = { - maybeFluenceConfig: FluenceConfig | null; -}; - -export async function initCountly({ - maybeFluenceConfig, -}: InitCountlyArgs): Promise { +export async function initCountly(): Promise { const userCountlyDir = await getUserCountlyDir(); const userConfig = await initNewUserConfig(); @@ -54,81 +47,11 @@ export async function initCountly({ segmentation: { userAgent: commandObj.config.userAgent, platform: commandObj.config.platform, - ...dependenciesToSegmentation( - maybeFluenceConfig?.aquaDependencies, - "aqua", - ), - ...dependenciesToSegmentation( - maybeFluenceConfig?.marineVersion === undefined - ? {} - : { - marine: maybeFluenceConfig.marineVersion, - }, - "marine", - ), - ...dependenciesToSegmentation( - maybeFluenceConfig?.mreplVersion === undefined - ? {} - : { - mrepl: maybeFluenceConfig.mreplVersion, - }, - "mrepl", - ), }, }); } } -const dependenciesToSegmentation = ( - dependencies: Record | null | undefined, - prefix: string, -): Record => { - return Object.entries(dependencies ?? {}).reduce((acc, [dep, version]) => { - return { - ...acc, - [`[${prefix}]${dep}`]: version, - }; - }, {}); -}; - -/** - * Add log that will be sent to Countly together with crash report - * @param message - message to be logged - * @returns void - */ -export const addCountlyLog = async (message: string): Promise => { - if (!(await isCountlyInitialized())) { - return; - } - - const Countly = (await import("countly-sdk-nodejs")).default; - Countly.add_log(message); -}; - -/** - * Add error log - * @param message - message to be logged - * @returns void - */ -export const logErrorToCountly = async (message: string): Promise => { - if (!(await isCountlyInitialized())) { - return; - } - - const Countly = (await import("countly-sdk-nodejs")).default; - Countly.log_error(message); -}; - -export const addCountlyEvent = async (key: string): Promise => { - if (!(await isCountlyInitialized())) { - return; - } - - const Countly = (await import("countly-sdk-nodejs")).default; - - Countly.add_event({ key }); -}; - export const haltCountly = async (): Promise => { if (!(await isCountlyInitialized())) { return; diff --git a/packages/cli/package/src/lib/deal.ts b/packages/cli/package/src/lib/deal.ts deleted file mode 100644 index 98584bdb2..000000000 --- a/packages/cli/package/src/lib/deal.ts +++ /dev/null @@ -1,1019 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import assert from "node:assert"; - -import type { MarketFacet } from "@fluencelabs/deal-ts-clients"; -import { color } from "@oclif/color"; -import type { Typed } from "ethers"; - -import { versions } from "../versions.js"; - -import { - cidStringToCIDV1Struct, - peerIdBase58ToUint8Array, -} from "./chain/conversions.js"; -import { ptFormatWithSymbol, ptParse } from "./chain/currencies.js"; -import { commandObj } from "./commandObj.js"; -import { initNewWorkersConfigReadonly } from "./configs/project/workers.js"; -import { - DEAL_IDS_FLAG_NAME, - DEFAULT_DEAL_ACTIVE_DURATION_FOR_LOCAL_ENV, - DEPLOYMENT_NAMES_ARG_NAME, -} from "./const.js"; -import { dbg } from "./dbg.js"; -import { - type MulticallReadItem, - sign, - getContracts, - getEventValue, - getEventValues, - getReadonlyContracts, - multicallRead, -} from "./dealClient.js"; -import { ensureChainEnv } from "./ensureChainNetwork.js"; -import { - DEFAULT_PAGE_LIMIT, - getDealForMatching, - getOffersForMatching, -} from "./gql/gql.js"; -import type { OffersForMatchingQueryVariables } from "./gql/gqlGenerated.js"; -import { setTryTimeout } from "./helpers/setTryTimeout.js"; -import { stringifyUnknown } from "./helpers/stringifyUnknown.js"; -import { - bigintToStr, - nullableToString, - numToStr, -} from "./helpers/typesafeStringify.js"; -import { commaSepStrToArr, splitErrorsAndResults } from "./helpers/utils.js"; -import { checkboxes, input, list } from "./prompt.js"; -import { ensureFluenceEnv } from "./resolveFluenceEnv.js"; - -type DealCreateArg = { - appCID: string; - minWorkers: number; - targetWorkers: number; - maxWorkersPerProvider: number; - pricePerCuPerEpoch: string; - cuCountPerWorker: number; - effectors: string[]; - initialBalance: string | undefined; - whitelist: string[] | undefined; - blacklist: string[] | undefined; - protocolVersion: number | undefined; - deploymentName?: string; -}; - -export async function dealCreate({ - appCID, - minWorkers, - targetWorkers, - maxWorkersPerProvider, - pricePerCuPerEpoch, - cuCountPerWorker, - effectors, - initialBalance, - whitelist, - blacklist, - protocolVersion, - deploymentName, -}: DealCreateArg) { - const { contracts } = await getContracts(); - const pricePerCuPerEpochBigInt = await ptParse(pricePerCuPerEpoch); - - const minDealDepositedEpochs = - await contracts.diamond.minDealDepositedEpochs(); - - const targetWorkersBigInt = BigInt(targetWorkers); - const cuCountPerWorkerBigInt = BigInt(cuCountPerWorker); - - const minInitialBalanceBigInt = - targetWorkersBigInt * - pricePerCuPerEpochBigInt * - minDealDepositedEpochs * - cuCountPerWorkerBigInt; - - const initialBalanceBigInt = - typeof initialBalance === "string" - ? await ptParse(initialBalance) - : await getDefaultInitialBalance( - minInitialBalanceBigInt, - pricePerCuPerEpochBigInt, - targetWorkersBigInt, - cuCountPerWorkerBigInt, - ); - - if (initialBalanceBigInt < minInitialBalanceBigInt) { - commandObj.error( - `${ - deploymentName === undefined ? "" : `${color.yellow(deploymentName)} :` - }initialBalance ${color.yellow( - initialBalance, - )} is less than minimum initialBalance = targetWorkers * pricePerCuPerEpoch * ${bigintToStr( - minDealDepositedEpochs, - )} = ${color.yellow( - await ptFormatWithSymbol(minInitialBalanceBigInt), - )}. Please, increase initialBalance or decrease targetWorkers or pricePerCuPerEpoch`, - ); - } - - await sign({ - title: `Approve ${await ptFormatWithSymbol(initialBalanceBigInt)} to be deposited to the deal`, - method: contracts.usdc.approve, - args: [contracts.deployment.diamond, initialBalanceBigInt], - }); - - const deployDealTxReceipt = await sign({ - title: `Create deal with appCID: ${appCID}`, - method: contracts.diamond.deployDeal, - args: [ - await cidStringToCIDV1Struct(appCID), - contracts.deployment.usdc, - initialBalanceBigInt, - minWorkers, - targetWorkers, - cuCountPerWorker, - maxWorkersPerProvider, - pricePerCuPerEpochBigInt, - await Promise.all( - effectors.map((cid) => { - return cidStringToCIDV1Struct(cid); - }), - ), - whitelist !== undefined ? 1 : blacklist !== undefined ? 2 : 0, - whitelist !== undefined - ? whitelist - : blacklist !== undefined - ? blacklist - : [], - protocolVersion ?? versions.protocolVersion, - ], - }); - - const dealId = getEventValue({ - contract: contracts.diamond, - txReceipt: deployDealTxReceipt, - eventName: "DealCreated", - value: "deal", - }); - - assert(typeof dealId === "string", "dealId is not a string"); - return dealId; -} - -export async function createAndMatchDealsForPeerIds({ - peerIdsFromFlags, - ...dealCreateArgs -}: Parameters[0] & { peerIdsFromFlags: string }) { - const peerIds = commaSepStrToArr(peerIdsFromFlags); - const { contracts } = await getContracts(); - const { ZeroAddress } = await import("ethers"); - - const offersWithCUs = await Promise.all( - peerIds.map(async (peerId) => { - const peerIdUint8Array = await peerIdBase58ToUint8Array(peerId); - const ccIds = await contracts.diamond.getComputeUnitIds(peerIdUint8Array); - - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - const [{ offerId } = {}, ...computeUnitInfos] = (await multicallRead([ - { - target: contracts.deployment.diamond, - callData: contracts.diamond.interface.encodeFunctionData( - "getComputePeer", - [peerIdUint8Array], - ), - decode(returnData) { - return contracts.diamond.interface.decodeFunctionResult( - "getComputePeer", - returnData, - ); - }, - }, - ...ccIds.map((unitId): MulticallReadItem => { - return { - target: contracts.deployment.diamond, - callData: contracts.diamond.interface.encodeFunctionData( - "getComputeUnit", - [unitId], - ), - decode(returnData) { - return contracts.diamond.interface.decodeFunctionResult( - "getComputeUnit", - returnData, - ); - }, - }; - }), - ])) as [ - ( - | Awaited> - | undefined - ), - ...( - | Awaited> - | undefined - )[], - ]; - - assert(offerId !== undefined, "wasn't able to find offerId"); - - const computeUnits = ccIds - .map((unitId, i) => { - return { - unitId, - deal: - computeUnitInfos[i]?.deal ?? - (() => { - throw new Error( - `Unreachable. Couldn't get deal for compute unit ${unitId}`, - ); - })(), - }; - }) - .filter(({ deal }) => { - return deal === ZeroAddress; - }) - .map(({ unitId }) => { - return unitId; - }); - - return { computeUnits, offerId, peerId }; - }), - ); - - for (const { offerId, computeUnits, peerId } of offersWithCUs) { - const CUs = computeUnits.slice(0, dealCreateArgs.cuCountPerWorker); - - if (CUs.length < dealCreateArgs.cuCountPerWorker) { - commandObj.warn( - `cuCountPerWorker for this deployment is ${color.yellow(dealCreateArgs.cuCountPerWorker)} but there are only ${color.yellow(CUs.length)} compute units without deals available for the peer ${color.yellow(peerId)}. Aborting deal creation for this peer`, - ); - - continue; - } - - try { - const dealAddress = await dealCreate({ - ...dealCreateArgs, - targetWorkers: 1, - minWorkers: 1, - }); - - commandObj.logToStderr(`Deal ${color.yellow(dealAddress)} created`); - - await sign({ - title: `Match deal ${dealAddress} with compute units:\n\n${CUs.join("\n")}\n\nfrom offer ${offerId}`, - method: contracts.diamond.matchDeal, - args: [dealAddress, [offerId], [[CUs]]], - }); - - commandObj.logToStderr( - `Deal ${color.yellow(dealAddress)} matched with peer ${color.yellow(peerId)}. Offer: ${color.yellow(offerId)} (${color.yellow(CUs.length)} compute units)`, - ); - } catch (e) { - commandObj.error( - `Couldn't create or match deal for peer ${color.yellow(peerId)}: ${stringifyUnknown(e)}`, - ); - } - } -} - -async function getDefaultInitialBalance( - minInitialBalanceBigInt: bigint, - pricePerCuPerEpochBigInt: bigint, - targetWorkersBigInt: bigint, - cuCountPerWorker: bigint, -) { - if ((await ensureChainEnv()) === "local") { - const { readonlyContracts } = await getReadonlyContracts(); - - const balance = - (DEFAULT_DEAL_ACTIVE_DURATION_FOR_LOCAL_ENV / - (await readonlyContracts.diamond.epochDuration())) * - targetWorkersBigInt * - pricePerCuPerEpochBigInt * - cuCountPerWorker; - - return balance < minInitialBalanceBigInt - ? minInitialBalanceBigInt - : balance; - } - - return minInitialBalanceBigInt; -} - -type DealUpdateArg = { - dealAddress: string; - appCID: string; -}; - -export async function dealUpdate({ dealAddress, appCID }: DealUpdateArg) { - const { contracts } = await getContracts(); - const deal = contracts.getDeal(dealAddress); - - await sign({ - title: `Update deal with new appCID: ${appCID}`, - method: deal.setAppCID, - args: [await cidStringToCIDV1Struct(appCID)], - }); -} - -export async function match(dealAddress: string) { - const { contracts } = await getContracts(); - dbg(`running getMatchedOffersByDealId with dealAddress: ${dealAddress}`); - - dbg( - `initTimestamp: ${bigintToStr( - await contracts.diamond.initTimestamp(), - )} Current epoch: ${bigintToStr(await contracts.diamond.currentEpoch())}`, - ); - - const matchedOffers = await setTryTimeout( - "get matched offers by deal id", - () => { - return getMatchedOffersByDealId(dealAddress); - }, - (err) => { - commandObj.error( - `Wasn't able to find a match for the deal ${dealAddress}: ${stringifyUnknown(err)}`, - ); - }, - 1000 * 5, // 5 seconds - ); - - if (matchedOffers === null) { - return commandObj.error(`No matched offers for deal ${dealAddress}`); - } - - dbg(`got matchedOffers: ${stringifyUnknown(matchedOffers)}`); - - const matchDealTxReceipt = await sign({ - title: `Match deal ${dealAddress} with offers:\n\n${matchedOffers.offers.join("\n")}`, - method: contracts.diamond.matchDeal, - args: [dealAddress, matchedOffers.offers, matchedOffers.computeUnits], - }); - - const pats = getEventValues({ - contract: contracts.diamond, - txReceipt: matchDealTxReceipt, - eventName: "ComputeUnitsMatched", - value: "unitId", - }); - - dbg(`got pats: ${stringifyUnknown(pats)}`); - - commandObj.logToStderr( - `${color.yellow(pats.length)} workers joined the deal ${color.yellow( - dealAddress, - )}`, - ); -} - -type MatchDealParm = Exclude< - Parameters[T], - Typed ->; - -type GetMatchedOffersOut = { - offers: MatchDealParm<1>; - computeUnits: MatchDealParm<2>; -} | null; - -async function getMatchedOffersByDealId(dealAddress: string) { - const dealId = dealAddress.toLowerCase(); - const { deal, _meta, graphNetworks } = await getDealForMatching(dealId); - - if (deal === null || deal === undefined) { - commandObj.error(`Deal not found. Searched for: ${dealId}`); - } - - const [graphNetwork] = graphNetworks; - - if (graphNetwork === undefined) { - throw new Error("graphNetworks array is empty"); - } - - if ( - graphNetwork.initTimestamp === null || - graphNetwork.initTimestamp === undefined - ) { - throw new Error( - `graphNetwork.initTimestamp is not a number. Got: ${nullableToString(graphNetwork.initTimestamp)}`, - ); - } - - if ( - graphNetwork.coreEpochDuration === null || - graphNetwork.coreEpochDuration === undefined - ) { - throw new Error( - `graphNetwork.coreEpochDuration is not a number. Got: ${nullableToString(graphNetwork.coreEpochDuration)}`, - ); - } - - if ( - graphNetwork.coreMinDealRematchingEpochs === null || - graphNetwork.coreMinDealRematchingEpochs === undefined - ) { - throw new Error( - `graphNetwork.coreMinDealRematchingEpochs is not a number. Got: ${nullableToString(graphNetwork.coreMinDealRematchingEpochs)}`, - ); - } - - if (_meta === null || _meta === undefined) { - throw new Error(`_meta is expected to be ${nullableToString(_meta)}`); - } - - if (_meta.block.timestamp === null || _meta.block.timestamp === undefined) { - throw new Error( - `_meta.block.timestamp is not a number. Got: ${nullableToString(_meta.block.timestamp)}`, - ); - } - - const { initTimestamp, coreEpochDuration, coreMinDealRematchingEpochs } = - graphNetwork; - - if (deal.effectors === null || deal.effectors === undefined) { - throw new Error( - `deal.effectors is ${nullableToString(deal.effectors)} for dealId: ${dealId}. Array is expected.`, - ); - } - - const alreadyMatchedCU = deal.joinedWorkers?.length ?? 0; - - if (alreadyMatchedCU % deal.cuCountPerWorker !== 0) { - throw new Error( - `Unreachable. Deal already has matched compute units, but the number of compute units is not a multiple of cuCountPerWorker. Already matched CU: ${numToStr(alreadyMatchedCU)}, cuCountPerWorker: ${numToStr(deal.cuCountPerWorker)}`, - ); - } - - const alreadyMatchedWorkers = alreadyMatchedCU / deal.cuCountPerWorker; - const targetWorkersToMatch = deal.targetWorkers - alreadyMatchedWorkers; - - if (targetWorkersToMatch < 0) { - throw new Error( - `Deal already has more workers matched than target. In theory this should never be the case. Already matched: ${numToStr(alreadyMatchedWorkers)}, target: ${numToStr(deal.targetWorkers * deal.cuCountPerWorker)}`, - ); - } - - if (targetWorkersToMatch === 0) { - throw new Error(`Deal already has target number of workers matched.`); - } - - const { calculateEpoch } = await import( - "@fluencelabs/deal-ts-clients/dist/dealExplorerClient/utils.js" - ); - - const currentEpoch = calculateEpoch( - _meta.block.timestamp, - initTimestamp, - coreEpochDuration, - ); - - const matchedAtEpoch = - deal.matchedAt !== undefined - ? calculateEpoch(Number(deal.matchedAt), initTimestamp, coreEpochDuration) - : 0; - - const nextEpochToRematch = matchedAtEpoch + coreMinDealRematchingEpochs; - - if (currentEpoch <= nextEpochToRematch) { - throw new Error( - `Deal ${dealId} has been matched recently at: ${deal.matchedAt ?? "unknown"} (${numToStr(matchedAtEpoch)} epoch). Wait for ${numToStr(nextEpochToRematch)} epoch to rematch`, - ); - } - - const minWorkersToMatch = Math.max( - deal.minWorkers - alreadyMatchedWorkers, - 0, - ); - - const { whitelist: providersWhiteList, blacklist: providersBlackList } = - prepareDealProviderAccessLists( - deal.providersAccessType, - deal.providersAccessList, - ); - - // Request page as big as allowed (remember about indexer limit). - // Shortens the query response for that rule as additional query size optimization. - const offersPerPageLimit = Math.min(targetWorkersToMatch, DEFAULT_PAGE_LIMIT); - - // Request page of peers and CUs as big as allowed (remember about indexer limit). - const peersPerPageLimit = Math.min( - deal.maxWorkersPerProvider, - DEFAULT_PAGE_LIMIT, - ); - - const matchedOffers: NonNullable = { - offers: [], - computeUnits: [], - }; - - let workersMatched = 0; - - // Go through indexer pages until the end condition: one of {fulfilled | end of offers, and peers, and CUs.} - let lastPageReached = false; - let offersOffset = 0; - let peersOffset = 0; - let computeUnitsOffset = 0; - - while (!lastPageReached) { - const offers = await getMatchedOffersPage( - { - dealId, - pricePerCuPerEpoch: deal.pricePerCuPerEpoch, - cuCountPerWorker: deal.cuCountPerWorker, - effectors: deal.effectors.map(({ effector: { id } }) => { - return id; - }), - paymentToken: deal.paymentToken.id, - targetWorkersToMatch, - minWorkersToMatch, - maxWorkersPerProvider: deal.maxWorkersPerProvider, - currentEpoch, - providersWhiteList, - providersBlackList, - }, - offersPerPageLimit, - peersPerPageLimit, - offersOffset, - peersOffset, - computeUnitsOffset, - ); - - if (offers.length === 0) { - dbg("Got empty data from indexer, break search."); - break; - } - - // Analyze fetched data to understand if we need to fetch next page and what - // params {offset, ...} to use for the next page. - for (const { peers, id: offerId } of offers) { - // Check if peers are empty and need to fetch next offer page. - // It could happen because we have after fetch filter: not more than cuCountPerWorker per peer - // that filters - if (peers === null || peers === undefined || peers.length === 0) { - offersOffset = offersOffset + DEFAULT_PAGE_LIMIT; - peersOffset = 0; - computeUnitsOffset = 0; - break; - } - - const peersToReturn: NonNullable["computeUnits"][number] = - []; - - for (const { computeUnits } of peers) { - if (computeUnits === null || computeUnits === undefined) { - continue; - } - - if (computeUnits.length >= deal.cuCountPerWorker) { - workersMatched = workersMatched + 1; - - peersToReturn.push( - computeUnits.slice(0, deal.cuCountPerWorker).map((cu) => { - return cu.id; - }), - ); - } - - if (workersMatched === targetWorkersToMatch) { - matchedOffers.offers.push(offerId); - matchedOffers.computeUnits.push(peersToReturn); - return matchedOffers; - } - - if (computeUnits.length < DEFAULT_PAGE_LIMIT) { - if (peers.length < DEFAULT_PAGE_LIMIT) { - if (offers.length < DEFAULT_PAGE_LIMIT) { - lastPageReached = true; - } else { - offersOffset = offersOffset + DEFAULT_PAGE_LIMIT; - peersOffset = 0; - computeUnitsOffset = 0; - } - } else { - peersOffset = peersOffset + DEFAULT_PAGE_LIMIT; - computeUnitsOffset = 0; - } - } else { - computeUnitsOffset = computeUnitsOffset + DEFAULT_PAGE_LIMIT; - } - } - - matchedOffers.offers.push(offerId); - matchedOffers.computeUnits.push(peersToReturn); - } - } - - if (workersMatched < minWorkersToMatch) { - dbg("workersMatched < minWorkersToMatch"); - matchedOffers.offers = []; - matchedOffers.computeUnits = []; - } - - if ( - matchedOffers.offers.length === 0 || - matchedOffers.computeUnits.length === 0 - ) { - return null; - } - - return matchedOffers; -} - -function prepareDealProviderAccessLists( - providersAccessType: number, - providersAccessList: - | { - __typename?: "DealToProvidersAccess"; - provider: { __typename?: "Provider"; id: string }; - }[] - | null - | undefined, -): { whitelist: string[]; blacklist: string[] } { - const res: { whitelist: string[]; blacklist: string[] } = { - whitelist: [], - blacklist: [], - }; - - if ( - providersAccessType === 0 || - providersAccessList === null || - providersAccessList === undefined - ) { - // None - return res; - } - - const providersAccessListStrings = providersAccessList.map((providerObj) => { - return providerObj.provider.id; - }); - - if (providersAccessType === 1) { - // whitelist - res.whitelist = providersAccessListStrings; - } else if (providersAccessType === 2) { - // whitelist - res.blacklist = providersAccessListStrings; - } - - return res; -} - -type GetMatchedOffersIn = { - dealId: string; - pricePerCuPerEpoch: string; - cuCountPerWorker: number; - effectors: string[]; - paymentToken: string; - targetWorkersToMatch: number; - minWorkersToMatch: number; - maxWorkersPerProvider: number; - currentEpoch: number; - providersWhiteList: string[]; - providersBlackList: string[]; -}; - -// must match market.matchDeal logic -async function getMatchedOffersPage( - getMatchedOffersIn: GetMatchedOffersIn, - offersPerPageLimit: number, // Possibility to optimize query size. - peersPerPageLimit: number, // Possibility to control, e.g. maxWorkersPerProvider. - offersOffset: number, - peersOffset: number, - computeUnitsOffset: number, -) { - const currentEpochString = numToStr(getMatchedOffersIn.currentEpoch); - - const filters: NonNullable = { - // TODO: We do not need Offers with ALL peers already linked to the Deal (protocol restriction). - pricePerEpoch_lte: getMatchedOffersIn.pricePerCuPerEpoch, - paymentToken: getMatchedOffersIn.paymentToken.toLowerCase(), - // Check if any of compute units are available in the offer and do not even fetch unrelated offers. - computeUnitsAvailable_gt: 0, - - // Check if provider whitelisted/blacklisted below, and if CC Active below in case without whitelist below. - }; - - const peersFilter: NonNullable< - NonNullable["and"] - >[number] = { - deleted: false, - computeUnits_: { worker: null, deleted: false }, - // Check for CC Active status below and depends on provider whitelist filter. - }; - - // Some filters per peers, capacity commitments and compute units are copied - // and implemented with different fields for the same filtration - it is so - // because in subgraph it is impossible to filter on nested fields - // and we do not want to reduce fetched data size (e.g. do to fetch offers - // with no peers with our conditions) - const indexerGetOffersParams: OffersForMatchingQueryVariables = { - limit: offersPerPageLimit, - filters, - peersFilters: { - and: [ - peersFilter, - { - // We do not need peers that already linked to the Deal (protocol restriction). - or: [ - { joinedDeals_: { deal_not: getMatchedOffersIn.dealId } }, - { isAnyJoinedDeals: false }, - ], - }, - ], - }, - computeUnitsFilters: { worker: null, deleted: false }, - peersLimit: peersPerPageLimit, - // We do not need more than cuCountPerWorker per peer. Apply restriction to already fetched and filtered data. - computeUnitsLimit: getMatchedOffersIn.cuCountPerWorker, - offset: offersOffset, - peersOffset, - computeUnitsOffset, - }; - - if (getMatchedOffersIn.effectors.length > 0) { - filters.effectors_ = { effector_in: getMatchedOffersIn.effectors }; - } - - // Check for blacklisted Providers. - if (getMatchedOffersIn.providersBlackList.length > 0) { - filters.provider_ = { id_not_in: getMatchedOffersIn.providersBlackList }; - } - - // We require rather CU to be in Active CC (and not in blacklist if blacklist exists) - // or CU from Deal whitelist of Providers. - if (getMatchedOffersIn.providersWhiteList.length > 0) { - filters.provider_ = { id_in: getMatchedOffersIn.providersWhiteList }; - } else { - // No whitelist, thus, check for active cc status is required. - // For Peers. - filters.peers_ = { - deleted: false, - // Do not fetch peers with no any of compute units in "active" status at all. - // Check if CU status is Active - if it has current capacity commitment and - // cc.info.startEpoch <= currentEpoch_. - currentCapacityCommitment_not: null, - // Since it is not possible to filter by currentCapacityCommitment_.startEpoch_lt - // we use this help field. - currentCCCollateralDepositedAt_lte: currentEpochString, - currentCCEndEpoch_gt: currentEpochString, - currentCCNextCCFailedEpoch_gt: currentEpochString, - }; - - // For CUs. - // Check if CU status is Active - if it has current capacity commitment and - // cc.info.startEpoch <= currentEpoch_. - peersFilter.currentCapacityCommitment_not = null; - - peersFilter.currentCapacityCommitment_ = { - // Duplication as it is in DealExplorerClient: serializeCapacityCommitmentsFiltersToIndexer. - startEpoch_lte: currentEpochString, - endEpoch_gt: currentEpochString, - // On each submitProof indexer should save nextCCFailedEpoch, and - // in query we relay on that field to filter Failed CC. - nextCCFailedEpoch_gt: currentEpochString, - deleted: false, - // Wait delegation is duplicating startEpoch_lte check, though. - status_not_in: ["WaitDelegation", "Removed", "Failed"], - }; - } - - dbg( - `[getMatchedOffersPage] Requesting indexer for page with page params: ${JSON.stringify( - indexerGetOffersParams, - null, - 2, - )}...`, - ); - - const fetched = await getOffersForMatching(indexerGetOffersParams); - - dbg( - `[getMatchedOffersPage] Fetched data: ${JSON.stringify(fetched, null, 2)}`, - ); - - return fetched.offers; -} - -export type DealNameAndId = { - dealName: string; - dealId: string; -}; - -export async function getDeals({ - args: { [DEPLOYMENT_NAMES_ARG_NAME]: deploymentNames }, - flags: { [DEAL_IDS_FLAG_NAME]: dealIds }, -}: { - args: { - [DEPLOYMENT_NAMES_ARG_NAME]: string | undefined; - }; - flags: { - [DEAL_IDS_FLAG_NAME]: string | undefined; - }; -}): Promise { - if (dealIds !== undefined && deploymentNames !== undefined) { - commandObj.error( - `You can't use both ${color.yellow( - DEPLOYMENT_NAMES_ARG_NAME, - )} arg and ${color.yellow( - `--${DEAL_IDS_FLAG_NAME}`, - )} flag at the same time. Please pick one of them`, - ); - } - - if (dealIds !== undefined) { - return commaSepStrToArr(dealIds).map((dealId) => { - return { dealName: dealId, dealId }; - }); - } - - const workersConfig = await initNewWorkersConfigReadonly(); - const fluenceEnv = await ensureFluenceEnv(); - - if (deploymentNames !== undefined) { - const names = commaSepStrToArr(deploymentNames); - - const [invalidNames, dealNamesAndIdsFromWorkerConfig] = - splitErrorsAndResults(names, (dealName) => { - const { dealIdOriginal: dealId } = - workersConfig.deals?.[fluenceEnv]?.[dealName] ?? {}; - - if (dealId === undefined) { - return { error: dealName }; - } - - return { result: { dealName, dealId } }; - }); - - if (invalidNames.length > 0) { - commandObj.error( - `Couldn't find deployments: ${color.yellow( - invalidNames.join(", "), - )} at ${workersConfig.$getPath()} in ${color.yellow( - `deals.${fluenceEnv}`, - )} property`, - ); - } - - return dealNamesAndIdsFromWorkerConfig; - } - - try { - return await checkboxes({ - message: `Select one or more deployments that you did on ${color.yellow( - fluenceEnv, - )} environment`, - options: Object.entries(workersConfig.deals?.[fluenceEnv] ?? {}).map( - ([dealName, { dealIdOriginal: dealId }]) => { - return { name: dealName, value: { dealName, dealId } }; - }, - ), - validate: (choices: string[]) => { - if (choices.length === 0) { - return "Please select at least one deployment"; - } - - return true; - }, - oneChoiceMessage(choice) { - return `There is currently only one deployment that you did on ${color.yellow( - fluenceEnv, - )} environment: ${color.yellow(choice)}. Do you want to select it`; - }, - onNoChoices() { - throw new Error(NO_DEPLOYMENTS_FOUND_ERROR_MESSAGE); - }, - flagName: DEAL_IDS_FLAG_NAME, - argName: DEPLOYMENT_NAMES_ARG_NAME, - }); - } catch (error) { - if ( - error instanceof Error && - !error.message.includes(NO_DEPLOYMENTS_FOUND_ERROR_MESSAGE) - ) { - throw error; - } - - commandObj.warn( - `No deployments found for ${color.yellow( - fluenceEnv, - )} environment at ${workersConfig.$getPath()}`, - ); - - return commaSepStrToArr( - await input({ - message: "Enter comma-separated list of deal ids", - validate: (val: string) => { - return commaSepStrToArr(val).length === 0 - ? "Please enter at least one deal id" - : true; - }, - }), - ).map((dealId) => { - return { dealName: dealId, dealId }; - }); - } -} - -const DEAL_ID_FLAG_NAME = "deal-id"; -const DEAL_NAME_FLAG_NAME = "name"; - -export async function getDeal({ - flags: { [DEAL_ID_FLAG_NAME]: dealId, [DEAL_NAME_FLAG_NAME]: dealName }, -}: { - flags: { - [DEAL_ID_FLAG_NAME]: string | undefined; - [DEAL_NAME_FLAG_NAME]: string | undefined; - }; -}): Promise { - if (dealId !== undefined && dealName !== undefined) { - commandObj.error( - `You can't use both ${color.yellow( - `--${DEAL_NAME_FLAG_NAME}`, - )} flag and ${color.yellow( - `--${DEAL_IDS_FLAG_NAME}`, - )} flag at the same time. Please pick one of them`, - ); - } - - if (dealId !== undefined) { - return { dealName: dealId, dealId }; - } - - const workersConfig = await initNewWorkersConfigReadonly(); - const fluenceEnv = await ensureFluenceEnv(); - - if (dealName !== undefined) { - const { dealIdOriginal: dealId } = - workersConfig.deals?.[fluenceEnv]?.[dealName] ?? {}; - - if (dealId === undefined) { - return commandObj.error( - `Couldn't find deployment: ${color.yellow( - dealName, - )} at ${workersConfig.$getPath()} in ${color.yellow( - `deals.${fluenceEnv}`, - )} property`, - ); - } - - return { dealName, dealId }; - } - - try { - return await list({ - message: `Select deployment that you did on ${color.yellow( - fluenceEnv, - )} environment`, - options: Object.entries(workersConfig.deals?.[fluenceEnv] ?? {}).map( - ([dealName, { dealIdOriginal: dealId }]) => { - return { name: dealName, value: { dealName, dealId } }; - }, - ), - oneChoiceMessage(choice) { - return `There is currently only one deployment that you did on ${color.yellow( - fluenceEnv, - )} environment: ${color.yellow(choice)}. Do you want to select it`; - }, - onNoChoices() { - throw new Error(NO_DEPLOYMENTS_FOUND_ERROR_MESSAGE); - }, - flagName: DEAL_ID_FLAG_NAME, - }); - } catch (error) { - if ( - error instanceof Error && - !error.message.includes(NO_DEPLOYMENTS_FOUND_ERROR_MESSAGE) - ) { - throw error; - } - - commandObj.warn( - `No deployments found for ${color.yellow( - fluenceEnv, - )} environment at ${workersConfig.$getPath()}`, - ); - - const dealId = await input({ message: "Enter deal id" }); - return { dealName: dealId, dealId }; - } -} - -const NO_DEPLOYMENTS_FOUND_ERROR_MESSAGE = - 'No deployments found for "fluenceEnv"'; diff --git a/packages/cli/package/src/lib/dealClient.ts b/packages/cli/package/src/lib/dealClient.ts index 770c66f75..3081186cd 100644 --- a/packages/cli/package/src/lib/dealClient.ts +++ b/packages/cli/package/src/lib/dealClient.ts @@ -150,7 +150,7 @@ async function createContracts(signerOrProvider: Provider | Signer) { return contracts; } -export async function ensureProvider(): Promise { +async function ensureProvider(): Promise { if (provider === undefined) { const { JsonRpcProvider } = await import("ethers"); diff --git a/packages/cli/package/src/lib/deploy.ts b/packages/cli/package/src/lib/deploy.ts deleted file mode 100644 index 233aa5d7f..000000000 --- a/packages/cli/package/src/lib/deploy.ts +++ /dev/null @@ -1,443 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import assert from "node:assert"; - -import type { DealStatus } from "@fluencelabs/deal-ts-clients"; -import { color } from "@oclif/color"; -import { Flags } from "@oclif/core"; - -import type Deploy from "../commands/deploy.js"; - -import { getChainId } from "./chain/chainConfig.js"; -import { printDealInfo } from "./chain/printDealInfo.js"; -import { commandObj } from "./commandObj.js"; -import type { Upload_deployArgConfig } from "./compiled-aqua/installation-spell/cli.js"; -import { TARGET_WORKERS_DEFAULT } from "./configs/project/fluence.js"; -import { - initNewWorkersConfig, - type Deal, - type WorkersConfigReadonly, -} from "./configs/project/workers.js"; -import { - LOCAL_IPFS_ADDRESS, - FLUENCE_CONFIG_FULL_FILE_NAME, - NO_BUILD_FLAG, - MARINE_BUILD_ARGS_FLAG, - DEFAULT_IPFS_ADDRESS, - IPFS_ADDR_PROPERTY, - DEFAULT_PRICE_PER_CU_PER_EPOCH_DEVELOPER, - CHAIN_FLAGS, - DEPLOYMENT_NAMES_ARG_NAME, - IMPORT_FLAG, -} from "./const.js"; -import { dbg } from "./dbg.js"; -import { dealCreate, dealUpdate, match } from "./deal.js"; -import { createAndMatchDealsForPeerIds } from "./deal.js"; -import { getReadonlyContracts } from "./dealClient.js"; -import { ensureFluenceProject } from "./helpers/ensureFluenceProject.js"; -import { stringifyUnknown } from "./helpers/stringifyUnknown.js"; -import { numToStr } from "./helpers/typesafeStringify.js"; -import { disconnectFluenceClient } from "./jsClient.js"; -import { initCli } from "./lifeCycle.js"; -import { getIpfsClient } from "./localServices/ipfs.js"; -import { confirm } from "./prompt.js"; -import { ensureFluenceEnv } from "./resolveFluenceEnv.js"; - -export const DEPLOY_DESCRIPTION = `Deploy according to 'deployments' property in ${FLUENCE_CONFIG_FULL_FILE_NAME}`; -export const DEPLOY_EXAMPLES = ["<%= config.bin %> <%= command.id %>"]; - -export const DEPLOY_FLAGS = { - ...CHAIN_FLAGS, - ...IMPORT_FLAG, - ...NO_BUILD_FLAG, - ...MARINE_BUILD_ARGS_FLAG, - update: Flags.boolean({ - char: "u", - description: "Update your previous deployment", - default: false, - }), - "peer-ids": Flags.string({ - description: - "Comma separated list of peer ids to deploy to. Creates 1 worker for each peer with 'cuCountPerWorker' number of compute units", - }), -}; - -type DealState = - | "notCreated" - | "notMatched" - | "matched" - | "ended" - | "notEnoughWorkers"; - -export async function deployImpl(this: Deploy, cl: typeof Deploy) { - const { flags, args } = await initCli(this, await this.parse(cl), true); - const fluenceConfig = await ensureFluenceProject(); - const chainNetworkId = await getChainId(); - const workersConfig = await initNewWorkersConfig(); - - const { ensureAquaFileWithWorkerInfo, prepareForDeploy } = await import( - "./deployWorkers.js" - ); - - const fluenceEnv = await ensureFluenceEnv(); - - const uploadArg = await prepareForDeploy({ - deploymentNamesString: args[DEPLOYMENT_NAMES_ARG_NAME], - fluenceEnv, - flags, - }); - - dbg("start running upload"); - - const uploadResult = await upload( - uploadArg, - fluenceConfig[IPFS_ADDR_PROPERTY] ?? - (fluenceEnv === "local" ? LOCAL_IPFS_ADDRESS : DEFAULT_IPFS_ADDRESS), - ); - - for (const { - name: deploymentName, - definition: appCID, - } of uploadResult.workers) { - const deal = fluenceConfig.deployments?.[deploymentName]; - - assert( - deal !== undefined, - "Unreachable. All deployment names are checked in prepareForDeploy. Then they are passed to upload aqua function which returns them back without modification", - ); - - const { - targetWorkers = TARGET_WORKERS_DEFAULT, - minWorkers = targetWorkers, - effectors = [], - pricePerCuPerEpoch = DEFAULT_PRICE_PER_CU_PER_EPOCH_DEVELOPER, - maxWorkersPerProvider = targetWorkers, - cuCountPerWorker = 1, - } = deal; - - let createdDeal = workersConfig.deals?.[fluenceEnv]?.[deploymentName]; - - let dealState = await determineDealState( - createdDeal, - deploymentName, - workersConfig, - ); - - const isDealUpdate = - dealState !== "notCreated" && appCID !== createdDeal?.definition; - - if (isDealUpdate) { - assert( - createdDeal !== undefined, - "Unreachable. isDealUpdate can be true only if createdDeal !== undefined", - ); - - if (!flags.update) { - commandObj.logToStderr( - `\n${color.yellow( - deploymentName, - )} deal is already created. You can use ${color.yellow( - "--update", - )} flag to update what you created previously\n`, - ); - - continue; - } - - commandObj.logToStderr( - `\nUpdating deal for ${color.yellow(deploymentName)}\n`, - ); - - await dealUpdate({ - appCID, - dealAddress: createdDeal.dealIdOriginal, - }); - - if (workersConfig.deals === undefined) { - workersConfig.deals = {}; - } - - let dealsPerEnv = workersConfig.deals[fluenceEnv]; - - if (dealsPerEnv === undefined) { - dealsPerEnv = {}; - workersConfig.deals[fluenceEnv] = dealsPerEnv; - } - - dealsPerEnv[deploymentName] = { - timestamp: new Date().toISOString(), - definition: appCID, - chainNetworkId, - dealIdOriginal: createdDeal.dealIdOriginal, - dealId: createdDeal.dealId, - }; - - await workersConfig.$commit(); - - commandObj.logToStderr( - `\n${color.yellow( - deploymentName, - )} deal updated.\nOld worker definition: ${color.yellow( - createdDeal.definition, - )}\nNew worker definition: ${color.yellow(appCID)}\n`, - ); - } - - if (dealState === "matched") { - commandObj.logToStderr( - `\nDeal ${color.yellow( - deploymentName, - )} already created and matched. Skipping...\n`, - ); - } - - const dealCreateArgs = { - appCID, - minWorkers, - targetWorkers, - maxWorkersPerProvider, - pricePerCuPerEpoch, - cuCountPerWorker, - effectors, - deploymentName, - initialBalance: deal.initialBalance, - whitelist: deal.whitelist, - blacklist: deal.blacklist, - protocolVersion: deal.protocolVersion, - } as const; - - // if peer-ids flag is used we create a deal for each CU of the peer - if (dealState === "notCreated" && flags["peer-ids"] !== undefined) { - dealState = "notMatched"; - } - - if (dealState === "notCreated" && flags["peer-ids"] === undefined) { - if (flags.update) { - commandObj.logToStderr( - `\nSkipping ${color.yellow( - deploymentName, - )} deal update because it is not yet created. You can create it if you remove --update flag\n`, - ); - - continue; - } - - commandObj.logToStderr( - `\nCreating ${color.yellow(deploymentName)} deal\n`, - ); - - const dealIdOriginal = await dealCreate(dealCreateArgs); - - if (workersConfig.deals === undefined) { - workersConfig.deals = {}; - } - - let dealsPerEnv = workersConfig.deals[fluenceEnv]; - - if (dealsPerEnv === undefined) { - dealsPerEnv = {}; - workersConfig.deals[fluenceEnv] = dealsPerEnv; - } - - const timestamp = new Date().toISOString(); - - createdDeal = { - definition: appCID, - timestamp, - dealIdOriginal, - dealId: dealIdOriginal.slice(2).toLowerCase(), - chainNetworkId, - matched: false, - }; - - dealsPerEnv[deploymentName] = createdDeal; - await workersConfig.$commit(); - dealState = "notMatched"; - - commandObj.logToStderr( - `\n${color.yellow( - deploymentName, - )} deal created. Deal id: ${color.yellow( - createdDeal.dealIdOriginal, - )}\n`, - ); - } - - if (dealState === "notMatched" || dealState === "notEnoughWorkers") { - try { - if (flags["peer-ids"] !== undefined) { - await createAndMatchDealsForPeerIds({ - ...dealCreateArgs, - peerIdsFromFlags: flags["peer-ids"], - }); - } else { - assert( - createdDeal !== undefined, - "Unreachable. createdDeal can't be undefined", - ); - - await match(createdDeal.dealIdOriginal); - createdDeal.matched = true; - await workersConfig.$commit(); - dealState = "matched"; - - commandObj.logToStderr( - `\n${color.yellow( - deploymentName, - )} deal matched. Deal id: ${color.yellow( - createdDeal.dealIdOriginal, - )}\n`, - ); - } - } catch (e) { - commandObj.logToStderr( - `Was not able to match deal for deployment ${color.yellow( - deploymentName, - )} ${stringifyUnknown(e)}`, - ); - } - } - - if (createdDeal !== undefined) { - await printDealInfo({ - dealId: createdDeal.dealIdOriginal, - dealName: deploymentName, - }); - } - } - - await ensureAquaFileWithWorkerInfo(); - - commandObj.log( - `\n\nUse ${color.yellow( - "fluence deal info", - )} command to get info about your deals\nUse ${color.yellow( - "fluence deal deposit", - )} command to top up your deals\nUse ${color.yellow( - "fluence deal --help", - )} to see all the commands related to deals`, - ); - - await disconnectFluenceClient(); -} - -async function getDealStatus(dealId: string) { - const { DealStatus } = await import("@fluencelabs/deal-ts-clients"); - const { readonlyContracts } = await getReadonlyContracts(); - const deal = readonlyContracts.getDeal(dealId); - const status = Number(await deal.getStatus()); - - function isDealStatus(status: number): status is DealStatus { - return status in DealStatus; - } - - assert(isDealStatus(status), `Unknown deal status: ${numToStr(status)}`); - return status; -} - -async function determineDealState( - createdDeal: Deal | undefined, - workerName: string, - workersConfig: WorkersConfigReadonly, -): Promise { - if (createdDeal === undefined) { - return "notCreated"; - } - - if (createdDeal.matched === false) { - return "notMatched"; - } - - const status = await getDealStatus(createdDeal.dealIdOriginal); - const { DealStatus } = await import("@fluencelabs/deal-ts-clients"); - const isDealEnded = status === DealStatus.ENDED; - - if ( - isDealEnded && - (await confirm({ - message: `You previously deployed ${color.yellow( - workerName, - )}, but this deal is already ended (at ${workersConfig.$getPath()})\n\nPlease keep this deal id ${color.yellow( - createdDeal.dealIdOriginal, - )} to withdraw the money from it after state overwrite\n\nDo you want to create a new deal and overwrite the old one`, - default: true, - })) - ) { - return "notCreated"; - } - - if (status === DealStatus.NOT_ENOUGH_WORKERS) { - return "notEnoughWorkers"; - } - - return isDealEnded ? "ended" : "matched"; -} - -async function upload(config: Upload_deployArgConfig, ipfsAddress: string) { - const deployDefs = await Promise.all( - config.workers.map(async (w) => { - const definitionCID = await uploadWorkerConfig(ipfsAddress, w.config); - return { - name: w.name, - hosts: w.hosts, - definition: definitionCID, - dummy_deal_id: w.dummy_deal_id, - }; - }), - ); - - return { - installation_script: config.installation_script, - installation_trigger: config.installation_trigger, - workers: deployDefs, - }; -} - -async function uploadWorkerConfig( - ipfs: string, - config: Upload_deployArgConfig["workers"][number]["config"], -) { - const ipfsClient = getIpfsClient(); - - const services = await Promise.all( - config.services.map(async ({ name, total_memory_limit, modules }) => { - return { - name, - total_memory_limit, - modules: await Promise.all( - modules.map(async ({ name, wasm }) => { - return { name, wasm: await ipfsClient.upload(ipfs, wasm) }; - }), - ), - }; - }), - ); - - const spells = await Promise.all( - config.spells.map(async (sp) => { - const script = await ipfsClient.upload_string(ipfs, sp.script); - const cfg_str = JSON.stringify(sp.config); - const config = await ipfsClient.upload_string(ipfs, cfg_str); - const init_args_str = JSON.stringify(sp.init_args); - const init_args = await ipfsClient.upload_string(ipfs, init_args_str); - return { name: sp.name, script, config, init_args }; - }), - ); - - return ipfsClient.upload_string(ipfs, JSON.stringify({ services, spells })); -} diff --git a/packages/cli/package/src/lib/deployWorkers.ts b/packages/cli/package/src/lib/deployWorkers.ts deleted file mode 100644 index 3cddcceb4..000000000 --- a/packages/cli/package/src/lib/deployWorkers.ts +++ /dev/null @@ -1,1118 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import assert from "node:assert"; -import { access, writeFile } from "node:fs/promises"; -import { resolve } from "node:path"; - -import { color } from "@oclif/color"; -import cloneDeep from "lodash-es/cloneDeep.js"; -import merge from "lodash-es/merge.js"; -import sum from "lodash-es/sum.js"; -import xbytes from "xbytes"; -import { yamlDiffPatch } from "yaml-diff-patch"; - -import { importAquaCompiler } from "./aqua.js"; -import { buildModules } from "./buildModules.js"; -import { commandObj, isInteractive } from "./commandObj.js"; -import { compileAquaFromFluenceConfigWithDefaults } from "./compileAquaAndWatch.js"; -import type { Upload_deployArgConfig } from "./compiled-aqua/installation-spell/cli.js"; -import { - type FluenceConfig, - type FluenceConfigReadonly, - assertIsArrayWithHostsOrDeals, - type OverrideModules, -} from "./configs/project/fluence.js"; -import { - initReadonlyModuleConfig, - type OverridableModuleProperties, - type ModuleConfigReadonly, -} from "./configs/project/module.js"; -import { - FACADE_MODULE_NAME, - initReadonlyServiceConfig, - type OverridableServiceProperties, - type ServiceConfigReadonly, -} from "./configs/project/service.js"; -import { - initReadonlySpellConfig, - resolveEndSec, - resolveStartSec, -} from "./configs/project/spell.js"; -import { - type Deal, - type Host, - type WorkersConfigReadonly, - initNewWorkersConfig, -} from "./configs/project/workers.js"; -import { - MODULE_TYPE_RUST, - FS_OPTIONS, - HOSTS_FILE_NAME, - DEALS_FILE_NAME, - FLUENCE_CONFIG_FULL_FILE_NAME, - type FluenceEnv, - COMPUTE_UNIT_MEMORY, - MIN_MEMORY_PER_MODULE, - MIN_MEMORY_PER_MODULE_STR, - DEPLOYMENT_NAMES_ARG_NAME, - MODULE_CONFIG_FULL_FILE_NAME, -} from "./const.js"; -import { getAquaImports } from "./helpers/aquaImports.js"; -import { - downloadModule, - getModuleWasmPath, - getUrlOrAbsolutePath, - isUrl, -} from "./helpers/downloadFile.js"; -import { ensureFluenceProject } from "./helpers/ensureFluenceProject.js"; -import { updateAquaServiceInterfaceFile } from "./helpers/generateServiceInterface.js"; -import { - jsToAqua, - makeOptional, - type CustomTypes, -} from "./helpers/jsToAqua.js"; -import { genServiceConfigToml } from "./helpers/serviceConfigToml.js"; -import { numToStr } from "./helpers/typesafeStringify.js"; -import { commaSepStrToArr, splitErrorsAndResults } from "./helpers/utils.js"; -import { initMarineCli } from "./marineCli.js"; -import { resolvePeerId } from "./multiaddres.js"; -import { - ensureFluenceAquaDealsPath, - ensureFluenceAquaHostsPath, - projectRootDir, -} from "./paths.js"; -import { checkboxes } from "./prompt.js"; -import { ensureFluenceEnv } from "./resolveFluenceEnv.js"; -import { hasKey } from "./typeHelpers.js"; - -const handlePreviouslyDeployedWorkers = async ( - deployedHostsOrDeals: - | WorkersConfigReadonly["deals"] - | WorkersConfigReadonly["hosts"] - | undefined, - workersToDeploy: Array, -) => { - if (deployedHostsOrDeals === undefined) { - return workersToDeploy; - } - - const previouslyDeployedWorkersNames = Object.keys(deployedHostsOrDeals); - - const previouslyDeployedWorkersNamesToBeDeployed = workersToDeploy.filter( - (workerName) => { - return previouslyDeployedWorkersNames.includes(workerName); - }, - ); - - if (previouslyDeployedWorkersNamesToBeDeployed.length === 0) { - return workersToDeploy; - } - - const confirmedWorkersNamesToDeploy = isInteractive - ? await checkboxes({ - message: `There are workers that were deployed previously. Please select the ones you want to update.`, - options: previouslyDeployedWorkersNamesToBeDeployed, - oneChoiceMessage(workerName) { - return `Do you want to redeploy worker ${color.yellow(workerName)}`; - }, - onNoChoices(): Array { - return []; - }, - }) - : previouslyDeployedWorkersNamesToBeDeployed; - - const workerNamesToRemove = previouslyDeployedWorkersNamesToBeDeployed.filter( - (workerName) => { - return !confirmedWorkersNamesToDeploy.includes(workerName); - }, - ); - - if (workerNamesToRemove.length === 0) { - return workersToDeploy; - } - - return workersToDeploy.filter((workerName) => { - return !workerNamesToRemove.includes(workerName); - }); -}; - -type UploadDeploySpellConfig = - Upload_deployArgConfig["workers"][number]["config"]["spells"][number]; - -type UploadDeployServiceConfig = - Upload_deployArgConfig["workers"][number]["config"]["services"][number]; - -type PrepareForDeployArg = { - flags: { - import: Array | undefined; - "no-build": boolean; - "marine-build-args": string | undefined; - }; - fluenceEnv: FluenceEnv; - /** - * only used in build command so all the spells are compiled, all services are built - * and so no error happens if some worker doesn't have any services or spells - */ - isBuildCheck?: boolean; - deploymentNamesString?: string | undefined; - initPeerId?: string; -}; - -export async function prepareForDeploy({ - flags: { - "marine-build-args": marineBuildArgs, - import: aquaImportsFromFlags, - "no-build": noBuild, - }, - fluenceEnv, - isBuildCheck = false, - deploymentNamesString, - initPeerId, -}: PrepareForDeployArg): Promise { - const workersConfig = await initNewWorkersConfig(); - const fluenceConfig = await ensureFluenceProject(); - const isDealDeploy = initPeerId === undefined; - const deploymentsOrHostsString = isDealDeploy ? "deployments" : "hosts"; - const dealsOrHostsString = isDealDeploy ? "deals" : "hosts"; - - const hostsOrDeals = Object.entries( - fluenceConfig[deploymentsOrHostsString] ?? {}, - ); - - assertIsArrayWithHostsOrDeals(hostsOrDeals); - const maybeDeployedHostsOrDeals = workersConfig[dealsOrHostsString]; - - const deploymentsToDeploy = await getDeploymentNames( - deploymentNamesString, - fluenceConfig, - ); - - const { services: servicesFromFluenceConfig = {} } = fluenceConfig; - - const { [deploymentsOrHostsString]: deploymentsFromFluenceConfig = {} } = - fluenceConfig; - - const deploymentsToDeployConfirmed = await handlePreviouslyDeployedWorkers( - maybeDeployedHostsOrDeals, - deploymentsToDeploy, - ); - - const deploymentConfigs = deploymentsToDeployConfirmed.map( - (deploymentName) => { - const deploymentConfig = deploymentsFromFluenceConfig[deploymentName]; - - assert( - deploymentConfig !== undefined, - `Unreachable. deployment names are validated in getDeploymentNames. Looking for ${deploymentName} in ${JSON.stringify( - deploymentsFromFluenceConfig, - )}`, - ); - - if ( - !isBuildCheck && - (deploymentConfig.services ?? []).length === 0 && - (deploymentConfig.spells ?? []).length === 0 - ) { - return commandObj.error( - `All deployments must have at least one service or spell. Deployment ${color.yellow( - deploymentName, - )} listed in ${fluenceConfig.$getPath()} ${color.yellow( - dealsOrHostsString, - )} property does not have any spells or services`, - ); - } - - return { deploymentName, deploymentConfig }; - }, - ); - - const serviceNames = isBuildCheck - ? Object.keys(fluenceConfig.services ?? {}) - : [ - ...new Set( - deploymentConfigs.flatMap(({ deploymentConfig }) => { - return deploymentConfig.services ?? []; - }), - ), - ]; - - const serviceConfigsWithOverrides = await Promise.all( - serviceNames.map(async (serviceName) => { - const service = servicesFromFluenceConfig[serviceName]; - - assert( - service !== undefined, - `Unreachable. can't find service ${serviceName} from 'services' property in ${fluenceConfig.$getPath()}. This has to be checked on config init. Looking for ${serviceName} in ${JSON.stringify( - servicesFromFluenceConfig, - )}`, - ); - - const { get, overrideModules, ...overridableProperties } = service; - - const serviceConfig = await initReadonlyServiceConfig( - get, - projectRootDir, - ); - - if (serviceConfig === null) { - return commandObj.error( - isUrl(get) - ? `Downloaded invalid service ${color.yellow( - serviceName, - )} from ${color.yellow(get)}` - : `Invalid service ${color.yellow(serviceName)} at ${color.yellow( - get, - )}`, - ); - } - - return { - serviceName, - overrideModules, - serviceConfig, - ...overridableProperties, - }; - }), - ); - - const modulesUrls = [ - ...new Set( - serviceConfigsWithOverrides - .flatMap(({ serviceConfig }) => { - return Object.values(serviceConfig.modules).map(({ get }) => { - return get; - }); - }) - .filter((get) => { - return isUrl(get); - }), - ), - ]; - - const downloadedModulesMap = new Map( - await Promise.all( - modulesUrls.map(async (url): Promise<[string, string]> => { - return [url, await downloadModule(url)]; - }), - ), - ); - - const localModuleAbsolutePaths = serviceConfigsWithOverrides - .flatMap(({ serviceConfig }) => { - return Object.values(serviceConfig.modules).map(({ get }) => { - return { - get, - serviceDirPath: serviceConfig.$getDirPath(), - }; - }); - }) - .filter(({ get }) => { - return !isUrl(get); - }) - .map(({ get, serviceDirPath }) => { - return [get, getUrlOrAbsolutePath(get, serviceDirPath)] as const; - }); - - const moduleAbsolutePathOrURLToModuleConfigsMap = new Map< - string, - ModuleConfigReadonly - >( - await Promise.all( - [...downloadedModulesMap.entries(), ...localModuleAbsolutePaths].map( - async ([originalGetValue, moduleAbsolutePath]): Promise< - [string, ModuleConfigReadonly] - > => { - const moduleConfig = - await initReadonlyModuleConfig(moduleAbsolutePath); - - if (moduleConfig === null) { - return commandObj.error( - isUrl(originalGetValue) - ? `Downloaded invalid module from ${color.yellow( - originalGetValue, - )} to ${moduleAbsolutePath}` - : `Invalid module found at ${moduleAbsolutePath}`, - ); - } - - return [ - isUrl(originalGetValue) ? originalGetValue : moduleAbsolutePath, - moduleConfig, - ]; - }, - ), - ), - ); - - if (!noBuild) { - const marineCli = await initMarineCli(); - - await buildModules( - [...moduleAbsolutePathOrURLToModuleConfigsMap.values()], - marineCli, - marineBuildArgs, - ); - - const serviceNamePathToFacadeMap: Record = - Object.fromEntries( - serviceConfigsWithOverrides.map(({ serviceName, serviceConfig }) => { - const { get } = serviceConfig.modules[FACADE_MODULE_NAME]; - - const urlOrAbsolutePath = getUrlOrAbsolutePath( - get, - serviceConfig.$getDirPath(), - ); - - const moduleConfig = - moduleAbsolutePathOrURLToModuleConfigsMap.get(urlOrAbsolutePath); - - assert( - moduleConfig !== undefined, - `Unreachable. Module config for ${urlOrAbsolutePath} can't be undefined`, - ); - - return [serviceName, getModuleWasmPath(moduleConfig)]; - }), - ); - - await updateAquaServiceInterfaceFile(serviceNamePathToFacadeMap, marineCli); - } - - const spellsToCompile = isBuildCheck - ? Object.keys(fluenceConfig.spells ?? {}) - : [ - ...new Set( - deploymentConfigs.flatMap(({ deploymentConfig }) => { - return deploymentConfig.spells ?? []; - }), - ), - ]; - - const spellConfigs = ( - await compileSpells(aquaImportsFromFlags, spellsToCompile) - ).map(({ functions, name, spellConfig, spellAquaFilePath }) => { - const { script } = functions[spellConfig.function] ?? {}; - - if (script === undefined) { - commandObj.error( - `Failed to find spell function ${color.yellow( - spellConfig.function, - )} in aqua file at ${color.yellow(spellAquaFilePath)}`, - ); - } - - return { - name, - config: { - blockchain: { end_block: 0, start_block: 0 }, - connections: { connect: false, disconnect: false }, - clock: - spellConfig.clock?.periodSec === undefined - ? { - start_sec: 0, - end_sec: 0, - period_sec: 0, - } - : { - start_sec: resolveStartSec(spellConfig), - end_sec: resolveEndSec(spellConfig), - period_sec: spellConfig.clock.periodSec, - }, - }, - script, - init_args: spellConfig.initArgs ?? {}, - }; - }); - - const workers: Upload_deployArgConfig["workers"] = await Promise.all( - hostsOrDeals - .filter(([deploymentName]) => { - return deploymentsToDeployConfirmed.includes(deploymentName); - }) - .map(([deploymentName, hostsOrDeals]) => { - return resolveDeployment({ - deploymentName, - hostsOrDeals, - fluenceConfig, - deploymentsFromFluenceConfig, - serviceConfigsWithOverrides, - moduleAbsolutePathOrURLToModuleConfigsMap, - spellConfigs, - initPeerId, - fluenceEnv, - }); - }), - ); - - await validateWasmExist(workers); - - const { deal_install_script } = await import( - "./compiled-aqua/installation-spell/deal_spell.js" - ); - - return { - workers, - installation_script: deal_install_script, - installation_trigger: { - clock: { start_sec: 1676293670, end_sec: 0, period_sec: 600 }, - connections: { connect: false, disconnect: false }, - blockchain: { start_block: 0, end_block: 0 }, - }, - }; -} - -const validateWasmExist = async ( - workers: Upload_deployArgConfig["workers"], -) => { - const errors = ( - await Promise.all( - workers - .flatMap((worker) => { - return worker.config.services.map((service) => { - return { - ...service, - worker: worker.name, - }; - }); - }) - .flatMap((service) => { - return service.modules.map((module) => { - return { - ...module, - service: service.name, - worker: service.worker, - }; - }); - }) - .map(async ({ wasm, name, service, worker }) => { - try { - await access(wasm); - return true; - } catch { - return `wasm file not found at ${color.yellow( - wasm, - )}\nfor deployment: ${color.yellow( - worker, - )}\nservice: ${color.yellow(service)}\nmodule ${color.yellow( - name, - )}\nIf you expect CLI to compile the code of this module, please add ${color.yellow( - `type: ${MODULE_TYPE_RUST}`, - )} to the ${MODULE_CONFIG_FULL_FILE_NAME}`; - } - }), - ) - ).filter((result): result is string => { - return typeof result === "string"; - }); - - if (errors.length > 0) { - commandObj.error(errors.join("\n")); - } -}; - -async function getDeploymentNames( - deploymentNames: string | undefined, - fluenceConfig: FluenceConfigReadonly, -): Promise { - if (deploymentNames !== undefined) { - const names = - deploymentNames === "" ? [] : commaSepStrToArr(deploymentNames); - - const [invalidNames, validDeploymentNames] = splitErrorsAndResults( - names, - (deploymentName) => { - const deployment = fluenceConfig.deployments?.[deploymentName]; - - if (deployment === undefined) { - return { error: deploymentName }; - } - - return { result: deploymentName }; - }, - ); - - if (invalidNames.length > 0) { - commandObj.error( - `Couldn't find deployments in ${fluenceConfig.$getPath()} deployments property: ${color.yellow( - invalidNames.join(", "), - )}`, - ); - } - - return validDeploymentNames; - } - - return checkboxes({ - message: `Select one or more deployments from ${fluenceConfig.$getPath()}`, - options: Object.keys(fluenceConfig.deployments ?? {}), - validate: (choices: string[]) => { - if (choices.length === 0) { - return "Please select at least one deployment"; - } - - return true; - }, - oneChoiceMessage(choice) { - return `One deployment found at ${fluenceConfig.$getPath()}: ${color.yellow( - choice, - )}. Do you want to select it`; - }, - onNoChoices() { - commandObj.error( - `You must have at least one deployment in 'deployments' property at ${fluenceConfig.$getPath()}`, - ); - }, - argName: DEPLOYMENT_NAMES_ARG_NAME, - }); -} - -const emptyDeal: Deal = { - dealId: "", - chainNetworkId: 0, - dealIdOriginal: "", - definition: "", - timestamp: "", -}; - -const emptySpellIds: Host["installation_spells"][number] = { - host_id: "", - spell_id: "", - worker_id: "", -}; - -const emptyHosts: Host = { - definition: "", - installation_spells: [emptySpellIds], - relayId: "", - timestamp: "", - dummyDealId: "", -}; - -export async function ensureAquaFileWithWorkerInfo() { - const workersConfig = await initNewWorkersConfig(); - const fluenceEnv = await ensureFluenceEnv(); - const fluenceConfig = await ensureFluenceProject(); - - const dealWorkers = Object.fromEntries( - Object.entries({ - ...fluenceConfig.deployments, - ...(workersConfig.deals?.[fluenceEnv] ?? {}), - }).map(([workerName, info]) => { - const key = workerName; - // if worker was deployed put deal info, otherwise put null - const maybeDeal = "dealId" in info ? info : null; - const value = makeOptional(maybeDeal, emptyDeal); - return [key, value]; - }), - ); - - const directHostingWorkers = Object.fromEntries( - Object.entries({ - ...fluenceConfig.hosts, - ...(workersConfig.hosts?.[fluenceEnv] ?? {}), - }).map(([workerName, info]) => { - const key = workerName; - // if worker was deployed put hosts info, otherwise put null - const maybeHost = "relayId" in info ? info : null; - const value = makeOptional(maybeHost, emptyHosts); - return [key, value]; - }), - ); - - const customHostsTypes: CustomTypes = [ - { name: "Host", properties: Object.keys(emptyHosts) }, - { name: "SpellLocation", properties: Object.keys(emptySpellIds) }, - ]; - - await writeFile( - await ensureFluenceAquaHostsPath(), - jsToAqua({ - valueToConvert: directHostingWorkers, - fileName: HOSTS_FILE_NAME, - customTypes: customHostsTypes, - }), - FS_OPTIONS, - ); - - const customDealsTypes: CustomTypes = [ - { name: "Deal", properties: Object.keys(emptyDeal) }, - ]; - - await writeFile( - await ensureFluenceAquaDealsPath(), - jsToAqua({ - valueToConvert: dealWorkers, - fileName: DEALS_FILE_NAME, - customTypes: customDealsTypes, - }), - FS_OPTIONS, - ); - - await compileAquaFromFluenceConfigWithDefaults(); -} - -type ResolveDeploymentArgs = { - fluenceConfig: FluenceConfig; - hostsOrDeals: NonNullable< - FluenceConfig["deployments"] | FluenceConfig["hosts"] - >[string]; - deploymentName: string; - deploymentsFromFluenceConfig: NonNullable< - FluenceConfig["deployments"] | FluenceConfig["hosts"] - >; - serviceConfigsWithOverrides: ({ - serviceName: string; - overrideModules: OverrideModules | undefined; - serviceConfig: ServiceConfigReadonly; - } & OverridableServiceProperties)[]; - moduleAbsolutePathOrURLToModuleConfigsMap: Map; - spellConfigs: UploadDeploySpellConfig[]; - initPeerId: string | undefined; - fluenceEnv: FluenceEnv; -}; - -export async function compileSpells( - aquaImportsFromFlags: string[] | undefined = [], - spellNames?: string[], -) { - const fluenceConfig = await ensureFluenceProject(); - - const spellsFromFluenceConfig = ( - spellNames ?? Object.keys(fluenceConfig.spells ?? {}) - ).map((name) => { - return { - spellFromFluenceConfig: fluenceConfig.spells?.[name], - name, - }; - }); - - const spellsNotFoundInFluenceConfig = spellsFromFluenceConfig.filter( - ({ spellFromFluenceConfig }) => { - return spellFromFluenceConfig === undefined; - }, - ); - - if (spellsNotFoundInFluenceConfig.length > 0) { - commandObj.error( - `Can't find the following spells in ${fluenceConfig.$getPath()} 'spells' property: ${color.yellow( - spellsNotFoundInFluenceConfig - .map(({ name }) => { - return name; - }) - .join(", "), - )}`, - ); - } - - const compiledSpells = await Promise.all( - spellsFromFluenceConfig.map(async ({ spellFromFluenceConfig, name }) => { - assert( - spellFromFluenceConfig !== undefined, - `Unreachable. Wasn't able to find spell. Spell existence in ${FLUENCE_CONFIG_FULL_FILE_NAME} must have been checked in the previous step`, - ); - - const { get, ...spellOverridesFromFluenceConfig } = - spellFromFluenceConfig; - - const spellConfig = await initReadonlySpellConfig(get, projectRootDir); - - if (spellConfig === null) { - return commandObj.error( - isUrl(get) - ? `Downloaded invalid spell ${color.yellow(name)}` - : `Invalid spell ${color.yellow(name)} at ${color.yellow(get)}`, - ); - } - - const overriddenSpellConfig = { - ...spellConfig, - ...spellOverridesFromFluenceConfig, - }; - - const spellAquaFilePath = resolve( - spellConfig.$getDirPath(), - spellConfig.aquaFilePath, - ); - - const { compileFromPath } = await importAquaCompiler(); - - // TODO: consider how to compile spells with aqua compilation args - const { errors, functions } = await compileFromPath({ - filePath: spellAquaFilePath, - imports: await getAquaImports(aquaImportsFromFlags), - }); - - return { - errors, - functions, - spellAquaFilePath, - spellConfig: overriddenSpellConfig, - name, - }; - }), - ); - - const compiledSpellsWithErrors = compiledSpells.filter(({ errors }) => { - return errors.length > 0; - }); - - if (compiledSpellsWithErrors.length > 0) { - commandObj.error( - compiledSpellsWithErrors - .map(({ errors, spellAquaFilePath }) => { - return `Failed to compile aqua file with spell at ${color.yellow( - spellAquaFilePath, - )}:\n\n${errors.join("\n")}`; - }) - .join("\n\n"), - ); - } - - return compiledSpells; -} - -async function resolveDeployment({ - hostsOrDeals, - deploymentName, - deploymentsFromFluenceConfig, - serviceConfigsWithOverrides, - moduleAbsolutePathOrURLToModuleConfigsMap, - spellConfigs, - initPeerId, - fluenceEnv, -}: ResolveDeploymentArgs) { - let dummyDealId = "deal_deploy_does_not_need_dummy_deal_id"; - const isDealDeploy = initPeerId === undefined; - - if (!isDealDeploy) { - dummyDealId = - (await initNewWorkersConfig()).hosts?.[fluenceEnv]?.[deploymentName] - ?.dummyDealId ?? - `${deploymentName}_${initPeerId}_${numToStr(Math.random()).slice(2)}`; - } - - const peerIdsOrNamedNodes = - "peerIds" in hostsOrDeals ? hostsOrDeals.peerIds : []; - - const deploymentConfig = deploymentsFromFluenceConfig[deploymentName]; - - assert( - deploymentConfig !== undefined, - `Unreachable. Wasn't able to find deployment. Looking for '${deploymentName}' in ${JSON.stringify( - deploymentsFromFluenceConfig, - )}`, - ); - - const servicesWithUnresolvedMemoryLimitPromises = ( - deploymentConfig.services ?? [] - ).map( - async ( - serviceName, - ): Promise< - Omit & { - total_memory_limit: number | undefined; - } - > => { - const serviceConfigWithOverrides = serviceConfigsWithOverrides.find( - (c) => { - return c.serviceName === serviceName; - }, - ); - - assert( - serviceConfigWithOverrides !== undefined, - `Unreachable. Service should not be undefined because serviceConfigs where created from workerConfig.services. Looking for ${serviceName} in ${JSON.stringify( - serviceConfigsWithOverrides, - )}`, - ); - - const { - overrideModules, - serviceConfig, - ...serviceOverridesFromFluenceYaml - } = serviceConfigWithOverrides; - - const { totalMemoryLimit } = serviceOverridesFromFluenceYaml; - - const { [FACADE_MODULE_NAME]: facadeModule, ...restModules } = - serviceConfig.modules; - - const modules = [ - ...Object.entries(restModules), - [FACADE_MODULE_NAME, facadeModule] as const, - ].map(([name, { get, ...overridesFromService }]) => { - const moduleUrlOrAbsolutePath = getUrlOrAbsolutePath( - get, - serviceConfig.$getDirPath(), - ); - - const moduleConfig = moduleAbsolutePathOrURLToModuleConfigsMap.get( - moduleUrlOrAbsolutePath, - ); - - assert( - moduleConfig !== undefined, - `Unreachable. Module should not be undefined because moduleConfigsMap was created from serviceConfigs.modules. Searching for ${moduleUrlOrAbsolutePath} in ${JSON.stringify( - Object.fromEntries( - moduleAbsolutePathOrURLToModuleConfigsMap.entries(), - ), - )}`, - ); - - const overridesFromFluenceYaml = overrideModules?.[name]; - - const overriddenModuleConfig = overrideModule( - moduleConfig, - overridesFromService, - overridesFromFluenceYaml, - ); - - return { - wasm: getModuleWasmPath(overriddenModuleConfig), - name: overriddenModuleConfig.name, - overriddenModuleConfig, - }; - }); - - const totalMemoryLimitString = - totalMemoryLimit ?? serviceConfig.totalMemoryLimit; - - await genServiceConfigToml( - serviceName, - serviceConfig, - serviceOverridesFromFluenceYaml, - modules.map(({ overriddenModuleConfig }) => { - return overriddenModuleConfig; - }), - ); - - return { - name: serviceName, - modules: modules.map(({ name, wasm }) => { - return { name, wasm }; - }), - total_memory_limit: - totalMemoryLimitString === undefined - ? undefined - : xbytes.parseSize(totalMemoryLimitString), - }; - }, - ); - - const servicesWithUnresolvedMemoryLimit = await Promise.all( - servicesWithUnresolvedMemoryLimitPromises, - ); - - const [ - servicesWithoutSpecifiedMemoryLimit, - servicesWithSpecifiedMemoryLimit, - ] = splitErrorsAndResults(servicesWithUnresolvedMemoryLimit, (service) => { - return hasTotalMemoryLimit(service) - ? { result: service } - : { error: service }; - }); - - const specifiedServicesMemoryLimit = sum( - servicesWithSpecifiedMemoryLimit.map((service) => { - return service.total_memory_limit; - }), - ); - - const workerMemory = - ("cuCountPerWorker" in deploymentConfig - ? deploymentConfig.cuCountPerWorker - : 1) * COMPUTE_UNIT_MEMORY; - - if (specifiedServicesMemoryLimit > workerMemory) { - throwMemoryExceedsError( - deploymentName, - specifiedServicesMemoryLimit, - servicesWithSpecifiedMemoryLimit, - workerMemory, - ); - } - - const remainingMemoryPerService = Math.floor( - (workerMemory - specifiedServicesMemoryLimit) / - servicesWithoutSpecifiedMemoryLimit.length, - ); - - const servicesWithNotValidatedMemoryLimit = [ - ...servicesWithSpecifiedMemoryLimit, - ...servicesWithoutSpecifiedMemoryLimit.map((service) => { - service.total_memory_limit = remainingMemoryPerService; - assert(hasTotalMemoryLimit(service), "Unreachable"); - return service; - }), - ]; - - const [servicesWithNotEnoughMemory, services] = splitErrorsAndResults( - servicesWithNotValidatedMemoryLimit, - (service) => { - const minMemoryForService = - MIN_MEMORY_PER_MODULE * service.modules.length; - - if (service.total_memory_limit < minMemoryForService) { - return { - error: { service, minMemoryForService }, - }; - } - - return { result: service }; - }, - ); - - if (servicesWithNotEnoughMemory.length > 0) { - throwNotEnoughMemoryError( - deploymentName, - servicesWithNotEnoughMemory, - services, - ); - } - - if (services.length > 0) { - commandObj.logToStderr( - `Service memory limits for worker ${color.yellow( - deploymentName, - )}:\n${formatServiceMemoryLimits(services)}`, - ); - } - - const spells = (deploymentConfig.spells ?? []).map((spellName) => { - const spellConfig = spellConfigs.find((c) => { - return c.name === spellName; - }); - - assert( - spellConfig !== undefined, - `Unreachable. Spell should not be undefined because spellConfigs where created from workerConfig.spells. Looking for ${spellName} in ${JSON.stringify( - spellConfigs, - )}`, - ); - - return spellConfig; - }); - - return { - name: deploymentName, - hosts: await Promise.all( - peerIdsOrNamedNodes.map((peerIdOrNamedNode) => { - return resolvePeerId(peerIdOrNamedNode); - }), - ), - config: { - services, - spells, - }, - dummy_deal_id: dummyDealId, - }; -} - -export function overrideModule( - moduleConfig: ModuleConfigReadonly, - moduleConfigOverridesFromServiceYaml: OverridableModuleProperties | undefined, - moduleConfigOverridesFromFluenceYaml: OverridableModuleProperties | undefined, -): ModuleConfigReadonly { - return merge( - cloneDeep(moduleConfig), - moduleConfigOverridesFromServiceYaml ?? {}, - moduleConfigOverridesFromFluenceYaml ?? {}, - ); -} - -function throwNotEnoughMemoryError( - deploymentName: string, - servicesWithNotEnoughMemory: { - service: UploadDeployServiceConfig; - minMemoryForService: number; - }[], - servicesWithSpecifiedMemoryLimit: UploadDeployServiceConfig[], -) { - const formattedServiceMemoryLimits = servicesWithNotEnoughMemory.map( - ({ service, minMemoryForService }) => { - return `${service.name}: ${color.yellow( - xbytes(service.total_memory_limit), - )} < minimum memory limit for this service ${color.yellow( - xbytes(minMemoryForService), - )}`; - }, - ); - - const decreaseOtherServicesMessage = - servicesWithSpecifiedMemoryLimit.length > 0 - ? ` or decrease the totalMemoryLimit for one or more of these service in the worker:\n${formatServiceMemoryLimits( - servicesWithSpecifiedMemoryLimit, - )}` - : ""; - - commandObj.error( - `The following services of the deployment ${color.yellow( - deploymentName, - )} don't have a big enough totalMemoryLimit:\n${formattedServiceMemoryLimits.join( - "\n", - )}\n\nEach service must have at least ${color.yellow( - MIN_MEMORY_PER_MODULE_STR, - )} for each module it has. Please make sure to specify a bigger totalMemoryLimit${decreaseOtherServicesMessage}`, - ); -} - -function throwMemoryExceedsError( - deploymentName: string, - specifiedServicesMemoryLimit: number, - servicesWithSpecifiedMemoryLimit: UploadDeployServiceConfig[], - workerMemory: number, -) { - const formattedServiceMemoryLimit = color.yellow( - xbytes(specifiedServicesMemoryLimit), - ); - - commandObj.error( - `Total memory limit for services in deployment ${color.yellow( - deploymentName, - )} is ${formattedServiceMemoryLimit}, which exceeds per-worker memory limit: ${color.yellow( - xbytes(workerMemory), - )}. Decrease ${color.yellow( - "totalMemoryLimit", - )} in one or more of the following services:\n${formatServiceMemoryLimits( - servicesWithSpecifiedMemoryLimit, - )}`, - ); -} - -function formatServiceMemoryLimits( - servicesWithSpecifiedMemoryLimit: UploadDeployServiceConfig[], -) { - return yamlDiffPatch( - "", - {}, - Object.fromEntries( - servicesWithSpecifiedMemoryLimit.map((service) => { - return [service.name, xbytes(service.total_memory_limit)]; - }), - ), - ); -} - -function hasTotalMemoryLimit( - arg: Record, -): arg is { total_memory_limit: number } { - return ( - hasKey("total_memory_limit", arg) && - typeof arg["total_memory_limit"] === "number" - ); -} diff --git a/packages/cli/package/src/lib/env.ts b/packages/cli/package/src/lib/env.ts deleted file mode 100644 index 681901aec..000000000 --- a/packages/cli/package/src/lib/env.ts +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import { delimiter } from "node:path"; - -export const findEntryInPATH = (entry: string): boolean => { - return process.env.PATH.split(delimiter).includes(entry); -}; - -export const prependEntryToPATH = (entry: string) => { - process.env.PATH = `${entry}${delimiter}${process.env.PATH}`; -}; diff --git a/packages/cli/package/src/lib/execPromise.ts b/packages/cli/package/src/lib/execPromise.ts index 31fba4f3e..59edb3a02 100644 --- a/packages/cli/package/src/lib/execPromise.ts +++ b/packages/cli/package/src/lib/execPromise.ts @@ -21,7 +21,7 @@ import { join } from "path"; import { color } from "@oclif/color"; import { CLIError } from "@oclif/core/errors"; -import { CLI_NAME, MARINE_CARGO_DEPENDENCY } from "./const.js"; +import { CLI_NAME } from "./const.js"; import { dbg } from "./dbg.js"; import { startSpinner, stopSpinner } from "./helpers/spinner.js"; import { numToStr, bufferToStr } from "./helpers/typesafeStringify.js"; @@ -152,22 +152,6 @@ export const execPromise = async ({ clearTimeout(execTimeout); } - if ( - stderr.includes("linker `cc` not found") || - stderr.includes("linking with `cc` failed") - ) { - const expectedErrorMessage = `\n${color.yellow( - MARINE_CARGO_DEPENDENCY, - )} requires ${color.yellow( - "build-essential", - )} to be installed. Please install it and try again.\nOn debian-based systems (e.g. Ubuntu) you can install it using this command:\n\n${color.yellow( - "sudo apt install build-essential", - )}\n`; - - res(new CLIError(expectedErrorMessage)); - return; - } - if (code !== 0) { const commandFailedMessage = getCommandFailedMessage(code); const errorMessage = getErrorMessage(printOutput, stderr); diff --git a/packages/cli/package/src/lib/genManifest.ts b/packages/cli/package/src/lib/genManifest.ts new file mode 100644 index 000000000..0ced6507f --- /dev/null +++ b/packages/cli/package/src/lib/genManifest.ts @@ -0,0 +1,160 @@ +/** + * Fluence CLI + * Copyright (C) 2024 Fluence DAO + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import { stringify } from "yaml"; + +import type { IPSupplies } from "./configs/project/provider/provider1.js"; + +const PRIVATE_KEY_SECRET_REF = "private-key-secret"; + +type GenManifestsArgs = { + chainPrivateKey: string; + ipSupplies: IPSupplies; + httpEndpoint: string; + wsEndpoint: string; + ipfsGatewayEndpoint: string; + peerIdHex: string; + networkId: string; + diamondContract: string; +}; + +export function genManifest({ + chainPrivateKey, + ipSupplies, + httpEndpoint, + wsEndpoint, + ipfsGatewayEndpoint, + peerIdHex, + networkId, + diamondContract, +}: GenManifestsArgs) { + return `--- +# if VM is enabled +apiVersion: kustomize.toolkit.fluxcd.io/v1 +kind: Kustomization +metadata: + name: kubevirt + namespace: flux-system +spec: + interval: 1m + path: "./flux/components/kubevirt/app" + prune: true + sourceRef: + kind: GitRepository + name: spectrum + namespace: flux-system + +--- +# l2 magic +apiVersion: kustomize.toolkit.fluxcd.io/v1 +kind: Kustomization +metadata: + name: cilium-l2 + namespace: flux-system +${stringify({ + spec: { + interval: "1m", + path: "./flux/components/cilium-l2", + prune: true, + sourceRef: { + kind: "GitRepository", + name: "spectrum", + namespace: "flux-system", + }, + patches: [ + { + patch: stringify([ + { + op: "add", + path: "/spec/blocks", + value: ipSupplies, + }, + ]), + target: { + kind: "CiliumLoadBalancerIPPool", + name: "fluence-l2", + namespace: "kube-system", + }, + }, + ], + }, +})} +--- +apiVersion: v1 +kind: Namespace +metadata: + name: lightmare +--- +# lightmare config +apiVersion: v1 +kind: ConfigMap +metadata: + name: chain-adapter-config + namespace: lightmare +${stringify({ + data: { + "values.yaml": stringify({ + operator: { + config: { + chainAdapter: { + httpEndpoint, + wsEndpoint, + ipfsGatewayEndpoint, + peerId: peerIdHex, + networkId, + diamondContract, + privateKeySecretRef: PRIVATE_KEY_SECRET_REF, + }, + }, + image: { + repository: "fluencelabs/lightmare", // optional + tag: "main", // optional + }, + }, + }), + }, +})} +--- +# chain private key +apiVersion: v1 +kind: Secret +metadata: + name: ${PRIVATE_KEY_SECRET_REF} + namespace: lightmare +type: Opaque +${stringify({ + data: { + "chain-private-key": chainPrivateKey, + }, +})} +--- +# lightmare deployment +apiVersion: kustomize.toolkit.fluxcd.io/v1 +kind: Kustomization +metadata: + name: lightmare + namespace: flux-system +spec: + interval: 1m + path: "./flux/core/lightmare/app" + prune: true + sourceRef: + kind: GitRepository + name: spectrum + namespace: flux-system +`; +} diff --git a/packages/cli/package/src/lib/generateNewModule.ts b/packages/cli/package/src/lib/generateNewModule.ts deleted file mode 100644 index 93b05e6d0..000000000 --- a/packages/cli/package/src/lib/generateNewModule.ts +++ /dev/null @@ -1,121 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import { mkdir, writeFile } from "node:fs/promises"; -import { basename, join, relative } from "node:path"; - -import { versions } from "../versions.js"; - -import { initNewReadonlyModuleConfig } from "./configs/project/module.js"; -import { - FS_OPTIONS, - MARINE_RS_SDK_CARGO_DEPENDENCY, - MARINE_RS_SDK_TEST_CARGO_DEPENDENCY, -} from "./const.js"; -import { getServiceConfigTomlPath } from "./helpers/serviceConfigToml.js"; - -export async function generateNewModule( - pathToModuleDir: string, - serviceName: string | undefined, -): Promise { - await mkdir(pathToModuleDir, { recursive: true }); - const name = basename(pathToModuleDir); - const newModuleSrcDirPath = join(pathToModuleDir, "src"); - await mkdir(newModuleSrcDirPath, { recursive: true }); - - await writeFile( - join(newModuleSrcDirPath, "main.rs"), - await getMainRsContent(newModuleSrcDirPath, serviceName), - FS_OPTIONS, - ); - - await writeFile( - join(pathToModuleDir, "Cargo.toml"), - getCargoTomlContent(name), - FS_OPTIONS, - ); - - await initNewReadonlyModuleConfig(pathToModuleDir, name); -} - -async function getMainRsContent( - newModuleSrcDirPath: string, - serviceName: string | undefined, -) { - return `#![allow(non_snake_case)] -use marine_rs_sdk::marine; -use marine_rs_sdk::module_manifest; - -module_manifest!(); - -pub fn main() {} - -#[marine] -pub fn greeting(name: String) -> String { - format!("Hi, {}", name) -}${await getTestExample(newModuleSrcDirPath, serviceName)} -`; -} - -async function getTestExample( - newModuleSrcDirPath: string, - serviceName: string | undefined, -) { - if (serviceName === undefined) { - return ""; - } - - const serviceConfigTomlPath = await getServiceConfigTomlPath(serviceName); - const relativePath = relative(newModuleSrcDirPath, serviceConfigTomlPath); - - return ` - -#[cfg(test)] -mod tests { - use marine_rs_sdk_test::marine_test; - - #[marine_test(config_path = "${relativePath}")] - fn empty_string(greeting: marine_test_env::myService::ModuleInterface) { - let actual = greeting.greeting(String::new()); - assert_eq!(actual, "Hi, "); - } - - #[marine_test(config_path = "${relativePath}")] - fn non_empty_string(greeting: marine_test_env::myService::ModuleInterface) { - let actual = greeting.greeting("name".to_string()); - assert_eq!(actual, "Hi, name"); - } -}`; -} - -const getCargoTomlContent = (name: string): string => { - return `[package] -name = "${name}" -version = "0.1.0" -edition = "2018" - -[[bin]] -name = "${name}" -path = "src/main.rs" - -[dependencies] -${MARINE_RS_SDK_CARGO_DEPENDENCY} = "${versions.cargo[MARINE_RS_SDK_CARGO_DEPENDENCY]}" - -[dev-dependencies] -${MARINE_RS_SDK_TEST_CARGO_DEPENDENCY} = "${versions.cargo[MARINE_RS_SDK_TEST_CARGO_DEPENDENCY]}" -`; -}; diff --git a/packages/cli/package/src/lib/generateUserProviderConfig.ts b/packages/cli/package/src/lib/generateUserProviderConfig.ts index 2bb4b18d1..7e2c75691 100644 --- a/packages/cli/package/src/lib/generateUserProviderConfig.ts +++ b/packages/cli/package/src/lib/generateUserProviderConfig.ts @@ -15,190 +15,8 @@ * along with this program. If not, see . */ -import { color } from "@oclif/color"; - -import { - ccDurationValidator, - getMinCCDuration, - validateAddress, -} from "./chain/chainValidators.js"; -import { isInteractive } from "./commandObj.js"; -import type { ProviderConfig } from "./configs/project/provider/provider.js"; -import type { Offer } from "./configs/project/provider/provider2.js"; -import { - defaultNumberProperties, - type CurrencyProperty, - currencyProperties, - DEFAULT_CC_STAKER_REWARD, - DURATION_EXAMPLE, - DEFAULT_NUMBER_OF_COMPUTE_UNITS_ON_NOX, -} from "./const.js"; -import { bigintToStr, numToStr } from "./helpers/typesafeStringify.js"; -import { commaSepStrToArr } from "./helpers/utils.js"; -import { - validatePercent, - validatePositiveNumberOrEmpty, -} from "./helpers/validations.js"; -import { checkboxes, confirm, input } from "./prompt.js"; - -async function promptToSetNumberProperty( - offer: Offer, - property: CurrencyProperty, -) { - const propertyStr = await input({ - message: `Enter ${color.yellow(property)}`, - default: defaultNumberProperties[property], - }); - - offer[property] = propertyStr; -} - -const DEFAULT_NUMBER_OF_NOXES = 3; +import { PEERS_FLAG_NAME } from "./const.js"; export type ProviderConfigArgs = { - noxes?: number | undefined; - "no-vm"?: boolean | undefined; + [PEERS_FLAG_NAME]?: number | undefined; }; - -export async function addComputePeers( - numberOfNoxes: number | undefined, - providerConfig: ProviderConfig, -) { - let computePeersCounter = 0; - let isAddingMoreComputePeers = true; - const minDuration = await getMinCCDuration(); - const validateCCDuration = await ccDurationValidator(); - - do { - const defaultName = `nox-${numToStr(computePeersCounter)}`; - - let name = - numberOfNoxes === undefined - ? await input({ - message: `Enter name for compute peer`, - default: defaultName, - }) - : defaultName; - - if (name === defaultName) { - name = defaultName; - computePeersCounter = computePeersCounter + 1; - } - - const computeUnitsString = await input({ - message: `Enter number of compute units for ${color.yellow(name)}`, - default: numToStr(DEFAULT_NUMBER_OF_COMPUTE_UNITS_ON_NOX), - validate: validatePositiveNumberOrEmpty, - }); - - const capacityCommitmentDuration = await input({ - message: `Enter capacity commitment duration ${DURATION_EXAMPLE}`, - default: `${bigintToStr(minDuration)} sec`, - validate: validateCCDuration, - }); - - const capacityCommitmentDelegator = await input({ - // default: anybody can activate capacity commitment - // optional - message: `Enter capacity commitment delegator address`, - validate: validateAddress, - }); - - const capacityCommitmentStakerReward = await input({ - message: `Enter capacity commitment staker reward (in %)`, - default: numToStr(DEFAULT_CC_STAKER_REWARD), - validate: validatePercent, - }); - - providerConfig.capacityCommitments[name] = { - duration: capacityCommitmentDuration, - delegator: capacityCommitmentDelegator, - stakerReward: Number(capacityCommitmentStakerReward), - }; - - providerConfig.computePeers[name] = { - computeUnits: Number(computeUnitsString), - }; - - if (numberOfNoxes !== undefined) { - isAddingMoreComputePeers = numberOfNoxes > computePeersCounter; - continue; - } - - if (isInteractive) { - isAddingMoreComputePeers = await confirm({ - message: "Do you want to add more compute peers", - }); - - continue; - } - - isAddingMoreComputePeers = DEFAULT_NUMBER_OF_NOXES > computePeersCounter; - } while (isAddingMoreComputePeers); -} - -export async function addOffers(providerConfig: ProviderConfig) { - let isAddingMoreOffers = true; - let offersCounter = 0; - - do { - const defaultName = - offersCounter === 0 ? "offer" : `offer-${numToStr(offersCounter)}`; - - const name = await input({ - message: `Enter name for offer`, - default: defaultName, - }); - - if (name === defaultName) { - offersCounter = offersCounter + 1; - } - - const computePeerOptions = Object.keys(providerConfig.computePeers); - - const computePeers = isInteractive - ? await checkboxes({ - message: `Select compute peers for ${color.yellow(name)}`, - options: computePeerOptions, - validate: (choices: string[]) => { - if (choices.length === 0) { - return "Please select at least one compute peer"; - } - - return true; - }, - oneChoiceMessage(choice) { - return `Do you want to select ${color.yellow(choice)} compute peer`; - }, - onNoChoices() { - throw new Error("No compute peers selected"); - }, - }) - : computePeerOptions; - - const effectorsString = await input({ - message: "Enter comma-separated list of effector CIDs", - default: "", - }); - - const effectors = - effectorsString === "" ? [] : commaSepStrToArr(effectorsString); - - const offer: Offer = { - ...defaultNumberProperties, - computePeers, - ...(effectors.length > 0 ? { effectors } : {}), - }; - - for (const numberProperty of currencyProperties) { - await promptToSetNumberProperty(offer, numberProperty); - } - - providerConfig.offers[name] = offer; - - isAddingMoreOffers = await confirm({ - message: "Do you want to add more offers", - default: false, - }); - } while (isAddingMoreOffers); -} diff --git a/packages/cli/package/src/lib/gql/gql.ts b/packages/cli/package/src/lib/gql/gql.ts index a6c821db3..96f0ac61b 100644 --- a/packages/cli/package/src/lib/gql/gql.ts +++ b/packages/cli/package/src/lib/gql/gql.ts @@ -19,11 +19,7 @@ import { GraphQLClient } from "graphql-request"; import { getSubgraphUrl } from "../chain/chainConfig.js"; -import { - getSdk as getGqlSdk, - type OffersForMatchingQueryVariables, - type Sdk, -} from "./gqlGenerated.js"; +import { getSdk as getGqlSdk, type Sdk } from "./gqlGenerated.js"; let sdk: Promise | undefined = undefined; @@ -37,55 +33,26 @@ async function getSdk() { return sdk; } -export const DEFAULT_PAGE_LIMIT = 1000; +// const DEFAULT_PAGE_LIMIT = 1000; -async function getAllPages(getter: (skip: number) => Promise) { - let result: T[] = []; - let skip = 0; - let hasMore = true; +// async function getAllPages(getter: (skip: number) => Promise) { +// let result: T[] = []; +// let skip = 0; +// let hasMore = true; - while (hasMore) { - const res = await getter(skip); - result = result.concat(res); +// while (hasMore) { +// const res = await getter(skip); +// result = result.concat(res); - if (res.length === DEFAULT_PAGE_LIMIT) { - skip = skip + DEFAULT_PAGE_LIMIT; - } else { - hasMore = false; - } - } - - return result; -} - -export async function getDealIdsByProviderId(providerId: string) { - const sdk = await getSdk(); - - return getAllPages(async (skip) => { - return ( - await sdk.Deals({ - where: { - joinedWorkers_: { - // TODO: must be just provider_: { id: providerId.toLowerCase() } - peer_: { provider_: { id: providerId.toLowerCase() } }, - }, - }, - skip, - first: DEFAULT_PAGE_LIMIT, - }) - ).deals; - }); -} +// if (res.length === DEFAULT_PAGE_LIMIT) { +// skip = skip + DEFAULT_PAGE_LIMIT; +// } else { +// hasMore = false; +// } +// } -export async function getDealForMatching(dealId: string) { - return (await getSdk()).DealForMatching({ id: dealId }); -} - -export async function getOffersForMatching( - variables: OffersForMatchingQueryVariables, -) { - return (await getSdk()).OffersForMatching(variables); -} +// return result; +// } export async function getOffers(id_in: string[]) { return (await getSdk()).OfferDetails({ where: { id_in, deleted: false } }); diff --git a/packages/cli/package/src/lib/gql/schema.graphql b/packages/cli/package/src/lib/gql/schema.graphql index d76a2c9e3..e4ea7a3b0 100644 --- a/packages/cli/package/src/lib/gql/schema.graphql +++ b/packages/cli/package/src/lib/gql/schema.graphql @@ -1,97 +1,3 @@ -query Deals($where: Deal_filter, $skip: Int, $first: Int) { - deals( - where: $where - skip: $skip - first: $first - orderBy: createdAt - orderDirection: desc - ) { - id - } -} - -query DealForMatching($id: ID!) { - deal(id: $id) { - maxWorkersPerProvider - minWorkers - pricePerCuPerEpoch - matchedAt - paymentToken { - id - } - targetWorkers - cuCountPerWorker - joinedWorkers { - computeUnits { - id - } - } - effectors { - effector { - id - } - } - providersAccessType - providersAccessList { - provider { - id - } - } - } - - _meta { - block { - timestamp - } - } - - graphNetworks(first: 1) { - coreEpochDuration - initTimestamp - coreMinDealRematchingEpochs - } -} - -query OffersForMatching( - $filters: Offer_filter - $peersFilters: Peer_filter - $computeUnitsFilters: ComputeUnit_filter - $peersLimit: Int - $computeUnitsLimit: Int - $offset: Int - $peersOffset: Int - $computeUnitsOffset: Int - $limit: Int - $orderBy: Offer_orderBy - $orderType: OrderDirection -) { - offers( - where: { - and: [ - $filters - # Exclude deleted offers. - { deleted: false } - ] - } - first: $limit - skip: $offset - orderBy: $orderBy - orderDirection: $orderType - ) { - id - peers(where: $peersFilters, first: $peersLimit, skip: $peersOffset) { - id - computeUnits( - where: $computeUnitsFilters - first: $computeUnitsLimit - skip: $computeUnitsOffset - ) { - id - } - } - } -} - query OfferDetails($where: Offer_filter) { offers(where: $where) { id diff --git a/packages/cli/package/src/lib/helpers/aquaImports.ts b/packages/cli/package/src/lib/helpers/aquaImports.ts deleted file mode 100644 index da32bc81c..000000000 --- a/packages/cli/package/src/lib/helpers/aquaImports.ts +++ /dev/null @@ -1,60 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import { access } from "node:fs/promises"; - -import type { GatherImportsResult } from "@fluencelabs/npm-aqua-compiler"; - -import { initFluenceConfig } from "../configs/project/fluence.js"; -import { builtInAquaDependenciesDirPath } from "../npm.js"; -import { - getFluenceAquaDir, - ensureFluenceAquaDependenciesPath, - projectRootDir, -} from "../paths.js"; - -export async function getAquaImports( - aquaImportsFromFlags?: string[], -): Promise { - const fluenceConfig = await initFluenceConfig(); - const fluenceAquaDirPath = getFluenceAquaDir(); - - const globalImports = [ - ...(aquaImportsFromFlags ?? []), - ...(fluenceConfig?.aquaImports ?? []), - ]; - - try { - if (fluenceConfig !== null) { - await access(fluenceAquaDirPath); - globalImports.push(fluenceAquaDirPath); - } - } catch {} - - const { gatherImportsFromNpm } = await import( - "@fluencelabs/npm-aqua-compiler" - ); - - return gatherImportsFromNpm({ - npmProjectDirPath: - fluenceConfig === null - ? builtInAquaDependenciesDirPath - : await ensureFluenceAquaDependenciesPath(), - globalImports, - aquaToCompileDirPath: projectRootDir, - }); -} diff --git a/packages/cli/package/src/lib/helpers/downloadFile.ts b/packages/cli/package/src/lib/helpers/downloadFile.ts deleted file mode 100644 index 4049e959a..000000000 --- a/packages/cli/package/src/lib/helpers/downloadFile.ts +++ /dev/null @@ -1,241 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import crypto from "node:crypto"; -import { access, mkdir, rm, writeFile } from "node:fs/promises"; -import { dirname, isAbsolute, join, resolve } from "node:path"; - -import { color } from "@oclif/color"; - -import { bufferToHex } from "../../common.js"; -import { commandObj } from "../commandObj.js"; -import { getConfigPath } from "../configs/initConfig.js"; -import { - MODULE_CONFIG_FULL_FILE_NAME, - MODULE_TYPE_RUST, - SERVICE_CONFIG_FULL_FILE_NAME, - SPELL_CONFIG_FULL_FILE_NAME, - WASM_EXT, -} from "../const.js"; -import { - ensureFluenceModulesDir, - ensureFluenceServicesDir, - ensureFluenceSpellsDir, - projectRootDir, -} from "../paths.js"; - -function getHashOfString(str: string): Promise { - const md5Hash = crypto.createHash("md5"); - return new Promise((resolve): void => { - md5Hash.on("readable", (): void => { - const data: unknown = md5Hash.read(); - - if (data instanceof Buffer) { - resolve(bufferToHex(data)); - } - }); - - md5Hash.write(str); - md5Hash.end(); - }); -} - -export async function downloadFile( - outputPath: string, - url: string, -): Promise { - const res = await fetch(url); - - if (!res.ok) { - return commandObj.error(`Failed when downloading ${color.yellow(url)}`); - } - - const arrayBuffer = await res.arrayBuffer(); - await mkdir(dirname(outputPath), { recursive: true }); - await writeFile(outputPath, new Uint8Array(arrayBuffer)); - return outputPath; -} - -export const AQUA_NAME_REQUIREMENTS = - "must start with a lowercase letter and contain only letters, numbers, and underscores"; - -export function validateAquaName(text: string): true | string { - return ( - /^[a-z]\w*$/.test(text) || `${color.yellow(text)} ${AQUA_NAME_REQUIREMENTS}` - ); -} - -export function validateAquaTypeName(text: string): true | string { - return ( - /^[A-Z]\w*$/.test(text) || - `${color.yellow( - text, - )} must start with an uppercase letter and contain only letters, numbers, and underscores` - ); -} - -const ARCHIVE_FILE = "archive.tar.gz"; - -async function getDownloadDirPath( - get: string, - pathStart: string, -): Promise { - const hash = await getHashOfString(get); - const cleanPrefix = get.replace(".tar.gz?raw=true", ""); - const withoutTrailingSlash = cleanPrefix.replace(/\/$/, ""); - - const lastPortionOfPath = - withoutTrailingSlash - .split(withoutTrailingSlash.includes("/") ? "/" : "\\") - .slice(-1)[0] ?? ""; - - const filenamify = (await import("filenamify")).default; - - const prefix = - lastPortionOfPath === "" ? "" : `${filenamify(lastPortionOfPath)}_`; - - return join(pathStart, `${prefix}${hash}`); -} - -async function downloadAndDecompress( - get: string, - pathStart: string, -): Promise { - const dirPath = await getDownloadDirPath(get, pathStart); - - try { - await access(dirPath); - return dirPath; - } catch {} - - const archivePath = join(dirPath, ARCHIVE_FILE); - await downloadFile(archivePath, get); - const tar = await import("tar"); - - await tar.x({ - cwd: dirPath, - file: archivePath, - }); - - await rm(archivePath, { force: true }); - return dirPath; -} - -export async function downloadModule(get: string): Promise { - return downloadAndDecompress(get, await ensureFluenceModulesDir()); -} - -async function downloadService(get: string): Promise { - return downloadAndDecompress(get, await ensureFluenceServicesDir()); -} - -async function downloadSpell(get: string): Promise { - return downloadAndDecompress(get, await ensureFluenceSpellsDir()); -} - -async function getModulePathFromUrl(get: string): Promise { - return getDownloadDirPath(get, await ensureFluenceModulesDir()); -} - -async function getServicePathFromUrl(get: string): Promise { - return getDownloadDirPath(get, await ensureFluenceServicesDir()); -} - -export function isUrl(unknown: string): boolean { - return unknown.startsWith("http://") || unknown.startsWith("https://"); -} - -export function getModuleWasmPath(moduleConfig: { - type?: string; - name: string; - $getDirPath: () => string; -}): string { - const fileName = `${moduleConfig.name}.${WASM_EXT}`; - const configDirName = moduleConfig.$getDirPath(); - return moduleConfig.type === MODULE_TYPE_RUST - ? resolve(projectRootDir, "target", "wasm32-wasi", "release", fileName) - : resolve(configDirName, fileName); -} - -export function getUrlOrAbsolutePath( - pathOrUrl: string, - absolutePath: string, -): string { - if (isUrl(pathOrUrl)) { - return pathOrUrl; - } - - if (isAbsolute(pathOrUrl)) { - return pathOrUrl; - } - - return resolve(absolutePath, pathOrUrl); -} - -function ensureOrGetConfigAbsolutePath( - downloadOrGetFunction: (get: string) => Promise, - configName: string, -) { - return async ( - pathOrUrl: string, - absolutePath: string | undefined, - ): Promise => { - const dirOrConfigAbsolutePath = await (async (): Promise => { - if (isUrl(pathOrUrl)) { - return downloadOrGetFunction(pathOrUrl); - } - - if (isAbsolute(pathOrUrl)) { - return pathOrUrl; - } - - if (absolutePath === undefined) { - throw new Error( - `Path ${color.yellow( - pathOrUrl, - )} is not absolute and no absolute path was provided`, - ); - } - - return resolve(absolutePath, pathOrUrl); - })(); - - return getConfigPath(dirOrConfigAbsolutePath, configName).configPath; - }; -} - -export const ensureModuleAbsolutePath = ensureOrGetConfigAbsolutePath( - downloadModule, - MODULE_CONFIG_FULL_FILE_NAME, -); -export const ensureServiceAbsolutePath = ensureOrGetConfigAbsolutePath( - downloadService, - SERVICE_CONFIG_FULL_FILE_NAME, -); -export const ensureSpellAbsolutePath = ensureOrGetConfigAbsolutePath( - downloadSpell, - SPELL_CONFIG_FULL_FILE_NAME, -); - -export const getModuleAbsolutePath = ensureOrGetConfigAbsolutePath( - getModulePathFromUrl, - MODULE_CONFIG_FULL_FILE_NAME, -); -export const getServiceAbsolutePath = ensureOrGetConfigAbsolutePath( - getServicePathFromUrl, - SERVICE_CONFIG_FULL_FILE_NAME, -); diff --git a/packages/cli/package/src/lib/helpers/ensureFluenceProject.ts b/packages/cli/package/src/lib/helpers/ensureFluenceProject.ts deleted file mode 100644 index 3701f88ec..000000000 --- a/packages/cli/package/src/lib/helpers/ensureFluenceProject.ts +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import { commandObj, isInteractive } from "../commandObj.js"; -import { - type FluenceConfig, - initFluenceConfig, -} from "../configs/project/fluence.js"; -import { init } from "../init.js"; -import { confirm } from "../prompt.js"; - -export const ensureFluenceProject = async (): Promise => { - const fluenceConfig = await initFluenceConfig(); - - if (fluenceConfig !== null) { - return fluenceConfig; - } - - const errorMessage = "Not a fluence project"; - - if (!isInteractive) { - commandObj.error(errorMessage); - } - - commandObj.warn(errorMessage); - - const doInit = await confirm({ - message: `Do you want to init fluence project`, - }); - - if (!doInit) { - commandObj.error( - "Initialized fluence project is required in order to continue", - ); - } - - return init(); -}; diff --git a/packages/cli/package/src/lib/helpers/formatAquaLogs.ts b/packages/cli/package/src/lib/helpers/formatAquaLogs.ts deleted file mode 100644 index 2b3472478..000000000 --- a/packages/cli/package/src/lib/helpers/formatAquaLogs.ts +++ /dev/null @@ -1,115 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import { color } from "@oclif/color"; - -import { jsonStringify } from "../../common.js"; -import type { - get_logs, - get_logs_deal, -} from "../compiled-aqua/installation-spell/cli.js"; - -import { LOGS_GET_ERROR_START } from "./utils.js"; - -type FormatAquaLogsType = - | Awaited>[number] - | ({ worker_name: string | undefined } & Awaited< - ReturnType - >[number]["logs"][number]); - -export function formatAquaLogsHeader({ - worker_name, - ...rest -}: { worker_name: string | undefined } & Record< - string, - string | null | undefined ->) { - const formattedWorkerName = - worker_name === undefined ? "" : `${color.yellow(worker_name)} `; - - const formattedHeader = Object.entries(rest) - .map(([key, value]) => { - return `${key}: ${value ?? "unknown"}`; - }) - .join(", "); - - return `${formattedWorkerName}(${formattedHeader}): `; -} - -export function formatAquaLogs({ - logs, - error, - host_id, - spell_id, - worker_id, - worker_name, -}: FormatAquaLogsType): string { - const header = formatAquaLogsHeader({ - worker_name, - host_id, - worker_id, - spell_id, - }); - - if (typeof error === "string") { - const trimmedError = error.trim(); - return `${header}${color.red( - trimmedError === "" - ? `${LOGS_GET_ERROR_START}Unknown error when getting logs` - : trimmedError, - )}`; - } - - const formattedLogs = logs - .map(({ message, timestamp }) => { - const date = new Date(timestamp * 1000) - .toISOString() - .slice(0, 19) - .replace("T", " "); - - return `${color.blue(date)} ${formatAquaLogsMessage(message)}`; - }) - .join("\n"); - - return `${header}\n\n${formattedLogs}`; -} - -function formatAquaLogsMessage(message: string) { - let parsedMessage; - - try { - parsedMessage = JSON.parse(message); - } catch { - return message; - } - - if (Array.isArray(parsedMessage)) { - return parsedMessage - .map((messagePart) => { - return typeof messagePart === "string" - ? messagePart - : jsonStringify(messagePart); - }) - .join(" "); - } - - if (typeof parsedMessage === "string") { - return parsedMessage; - } - - return jsonStringify(message); -} diff --git a/packages/cli/package/src/lib/helpers/generateServiceInterface.ts b/packages/cli/package/src/lib/helpers/generateServiceInterface.ts deleted file mode 100644 index 122bcb1f2..000000000 --- a/packages/cli/package/src/lib/helpers/generateServiceInterface.ts +++ /dev/null @@ -1,192 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import assert from "node:assert"; -import { readFile, writeFile } from "node:fs/promises"; - -import { initFluenceConfig } from "../configs/project/fluence.js"; -import { FS_OPTIONS, SERVICE_INTERFACE_FILE_HEADER } from "../const.js"; -import type { MarineCLI } from "../marineCli.js"; -import { ensureFluenceAquaServicesPath } from "../paths.js"; - -const SERVICE_DEFINITION_SEPARATOR = "\n\n\n"; -const SERVICE_AND_DATA_DEFINITION_SEPARATOR = "\n\n"; - -type GenerateServiceInterfaceArg = { - serviceId: string; - pathToFacadeWasm: string; - marineCli: MarineCLI; -}; - -const generateAquaInterfaceForService = async ({ - serviceId, - pathToFacadeWasm, - marineCli, -}: GenerateServiceInterfaceArg): Promise => { - const interfaceDeclaration = ( - await marineCli({ - args: ["aqua", pathToFacadeWasm], - flags: { service: serviceId, id: serviceId }, - printOutput: false, - }) - ) - .split("declares *")[1] - ?.trim(); - - assert( - interfaceDeclaration !== undefined, - `Failed to generate service interface for ${pathToFacadeWasm}`, - ); - - return interfaceDeclaration; -}; - -const getServiceIdFromServiceInterface = (dataAndServiceDefinition: string) => { - const dataAndServiceDefinitionAr = dataAndServiceDefinition.split( - SERVICE_AND_DATA_DEFINITION_SEPARATOR, - ); - - const serviceDefinition = dataAndServiceDefinitionAr.pop(); - - assert( - serviceDefinition !== undefined && - serviceDefinition.trim().startsWith("service"), - `Failed to parse service definition from ${dataAndServiceDefinition}`, - ); - - const serviceId = serviceDefinition.split('"')[1]; - - assert( - serviceId !== undefined, - `Failed to parse service id from service definition: ${dataAndServiceDefinition}`, - ); - - return serviceId; -}; - -function getServiceInterfaceFileContent(serviceInterfaces: string[]) { - const definitions = new Set(); - - const deduplicatedDefinitions = serviceInterfaces.map((s) => { - return s - .split(SERVICE_AND_DATA_DEFINITION_SEPARATOR) - .filter((d) => { - if (definitions.has(d)) { - return false; - } - - definitions.add(d); - return true; - }) - .join(SERVICE_AND_DATA_DEFINITION_SEPARATOR); - }); - - return ( - [SERVICE_INTERFACE_FILE_HEADER, ...deduplicatedDefinitions].join( - SERVICE_DEFINITION_SEPARATOR, - ) + "\n" - ); -} - -export const updateAquaServiceInterfaceFile = async ( - serviceNamePathToFacadeMap: Record, - marineCli: MarineCLI, -) => { - let previouslyGeneratedInterfacesStr = ""; - - try { - previouslyGeneratedInterfacesStr = await readFile( - await ensureFluenceAquaServicesPath(), - FS_OPTIONS, - ); - } catch {} - - const previouslyGeneratedInterfaces = previouslyGeneratedInterfacesStr - .trim() - .split(SERVICE_DEFINITION_SEPARATOR) - .map((serviceDefinition) => { - return serviceDefinition.trim(); - }) - .filter((serviceDefinition) => { - return ( - serviceDefinition !== "" && - serviceDefinition !== SERVICE_INTERFACE_FILE_HEADER - ); - }); - - const serviceNamesFromFluenceConfig = new Set( - Object.keys((await initFluenceConfig())?.services ?? {}), - ); - - const previouslyGeneratedInterfacesWithIdsPresentInFluenceConfig = - previouslyGeneratedInterfaces - .map((serviceDefinition) => { - return { - serviceId: getServiceIdFromServiceInterface(serviceDefinition), - serviceDefinition, - }; - }) - .filter(({ serviceId }) => { - return serviceNamesFromFluenceConfig.has(serviceId); - }); - - const serviceIdsFromPreviouslyGeneratedInterfaces = new Set( - previouslyGeneratedInterfacesWithIdsPresentInFluenceConfig.map( - ({ serviceId }) => { - return serviceId; - }, - ), - ); - - const generatedServiceInterfaceMap = Object.fromEntries( - await Promise.all( - Object.entries(serviceNamePathToFacadeMap).map( - async ([serviceId, pathToFacadeWasm]) => { - return [ - serviceId, - await generateAquaInterfaceForService({ - serviceId, - pathToFacadeWasm, - marineCli, - }), - ] as const; - }, - ), - ), - ); - - const newServiceInterfaces = Object.entries(generatedServiceInterfaceMap) - .filter(([serviceId]) => { - return !serviceIdsFromPreviouslyGeneratedInterfaces.has(serviceId); - }) - .map(([, serviceDefinition]) => { - return serviceDefinition; - }); - - const serviceInterfacesToWrite = - previouslyGeneratedInterfacesWithIdsPresentInFluenceConfig - .map(({ serviceId, serviceDefinition }) => { - return generatedServiceInterfaceMap[serviceId] ?? serviceDefinition; - }) - .concat(newServiceInterfaces); - - await writeFile( - await ensureFluenceAquaServicesPath(), - getServiceInterfaceFileContent(serviceInterfacesToWrite), - FS_OPTIONS, - ); -}; diff --git a/packages/cli/package/src/lib/helpers/getPeerIdFromSecretKey.ts b/packages/cli/package/src/lib/helpers/getPeerIdFromSecretKey.ts index 74431487d..5db6cedef 100644 --- a/packages/cli/package/src/lib/helpers/getPeerIdFromSecretKey.ts +++ b/packages/cli/package/src/lib/helpers/getPeerIdFromSecretKey.ts @@ -18,7 +18,15 @@ import { base64ToUint8Array } from "../keyPairs.js"; export async function getPeerIdFromSecretKey(secretKey: string) { - const { KeyPair } = await import("@fluencelabs/js-client"); - const keyPair = await KeyPair.fromEd25519SK(base64ToUint8Array(secretKey)); - return keyPair.getPeerId(); + const { generateKeyPairFromSeed } = await import("@libp2p/crypto/keys"); + const { createFromPrivKey } = await import("@libp2p/peer-id-factory"); + + const key = await generateKeyPairFromSeed( + "Ed25519", + base64ToUint8Array(secretKey), + 256, + ); + + // eslint-disable-next-line no-restricted-syntax + return (await createFromPrivKey(key)).toString(); } diff --git a/packages/cli/package/src/lib/helpers/jsToAqua.ts b/packages/cli/package/src/lib/helpers/jsToAqua.ts deleted file mode 100644 index afa7ecaa7..000000000 --- a/packages/cli/package/src/lib/helpers/jsToAqua.ts +++ /dev/null @@ -1,487 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import assert from "assert"; -import { mkdir, readFile, writeFile } from "fs/promises"; -import { dirname, join, parse } from "path"; - -import { color } from "@oclif/color"; -import type { JSONSchemaType } from "ajv"; -import camelCase from "lodash-es/camelCase.js"; -import upperFirst from "lodash-es/upperFirst.js"; - -import { validationErrorToString, ajv } from "../ajvInstance.js"; -import { commandObj } from "../commandObj.js"; -import { AQUA_EXT, FS_OPTIONS } from "../const.js"; -import { input } from "../prompt.js"; - -import { validateAquaTypeName, validateAquaName } from "./downloadFile.js"; -import { stringifyUnknown } from "./stringifyUnknown.js"; -import { boolToStr, numToStr } from "./typesafeStringify.js"; - -/** - * In js object, json or yaml when you want to represent optional value and still generate a type for it you can use this syntax: - * { $$optional: , $$isNil: } - */ -const OPTIONAL = "$$optional"; -/** - * When `$$isNil == true`, the value is represented as `nil` in aqua but it will still have type inferred from `$$optional` value. - */ -const IS_NIL = "$$isNil"; - -type EmptyObject = Record; - -/** - * Empty object and empty array are inferred as nil - * because there is no way to infer a reasonable aqua type from just an empty object or empty array - */ -type NilInAqua = undefined | null | EmptyObject | []; - -/** - * This function is used in order to construct optional syntax in js that is converted to optional values in aqua. - * - * @param value - value to be converted. Can be anything. - * @param valueToInferTypeFrom - a fallback that is used by jsToAqua function to infer the type of the value if value is missing (is null, undefined, empty object or empty array) - * @returns - js object with special syntax that is converted to optional value in aqua inside jsToAqua function - * - * @example - * const v = makeOptional(1, 1); - * // v = { $$optional: 1 } - * @example - * const v = makeOptional(undefined, 1); - * // v = { $$optional: 1, $$isNil: true } - * @example - * const v = makeOptional(null, 1); - * // v = { $$optional: 1, $$isNil: true } - */ -export const makeOptional = ( - value: T | NilInAqua, - valueToInferTypeFrom: T, -): { [OPTIONAL]: T; [IS_NIL]?: boolean } => { - const optional: { [OPTIONAL]: T; [IS_NIL]?: true } = { - [OPTIONAL]: isNilInAqua(value) ? valueToInferTypeFrom : value, - }; - - if (isNilInAqua(value)) { - optional[IS_NIL] = true; - } - - return optional; -}; - -function isNilInAqua(v: unknown): v is NilInAqua { - return ( - v === undefined || - v === null || - (typeof v === "object" && Object.keys(v).length === 0) || - (Array.isArray(v) && v.length === 0) - ); -} - -function dedupeTypeDefs(typeDefs: string): string { - return [...new Set(typeDefs.split("\n\n"))].join("\n\n"); -} - -function toAquaType(s: string): string | Error { - const aquaType = upperFirst(camelCase(s)); - const validity = validateAquaTypeName(aquaType); - - if (typeof validity === "string") { - return new Error(validity); - } - - return aquaType; -} - -export type JsToAquaArg = { - valueToConvert: unknown; - fileName: string; - useF64ForAllNumbers?: boolean; - customTypes?: CustomTypes; -}; - -export const jsToAqua = ({ - valueToConvert, - fileName, - useF64ForAllNumbers = false, - customTypes = [], -}: JsToAquaArg): string => { - const moduleName = toAquaType(fileName); - - if (moduleName instanceof Error) { - return commandObj.error( - `file name must start with a letter. Got: ${color.yellow(fileName)}`, - ); - } - - const customTypesWithAquaNames = customTypes.map((v) => { - return { ...v, name: toAquaType(v.name) }; - }); - - const customTypeErrors = customTypesWithAquaNames.filter( - (v): v is { name: Error; properties: Array } => { - return v.name instanceof Error; - }, - ); - - if (customTypeErrors.length > 0) { - return commandObj.error(customTypeErrors.join("\n\n")); - } - - const sortedCustomTypes = customTypesWithAquaNames.map( - ({ properties, name }) => { - assert(typeof name === "string", "Checked for errors above"); - return { properties: JSON.stringify(properties.sort()), name }; - }, - ); - - const { type, value, typeDefs } = jsToAquaImpl({ - fieldName: moduleName, - nestingLevel: 1, - currentNesting: "", - level: "top", - valueToConvert, - useF64ForAllNumbers, - sortedCustomTypes, - }); - - return `aqua ${moduleName} declares *\n\n${ - typeDefs === undefined ? "" : `${dedupeTypeDefs(typeDefs)}\n\n` - }func get() -> ${type}:\n <- ${value}\n`; -}; - -const NIL = { type: "?u8", value: "nil" } as const; - -const NUMBER_TYPES = ["u64", "i64", "f64"] as const; - -const INDENTATION = " ".repeat(4); - -type JsToAquaImplArg = { - valueToConvert: unknown; - fieldName: string; - currentNesting: string; - useF64ForAllNumbers: boolean; - nestingLevel: number; - sortedCustomTypes: Array<{ name: string; properties: string }>; - /** - * "top" - is a top level type returned from the get() function of the module - * "second" - are all the types of objects that are direct children of the top level type. Their names are not prefixed - * "rest" - all the types. Their names are prefixed with parent type names - */ - level: "top" | "second" | "rest"; -}; - -export const jsToAquaImpl = ({ - valueToConvert, - fieldName, - currentNesting, - useF64ForAllNumbers, - nestingLevel, - sortedCustomTypes, - level, -}: JsToAquaImplArg): { - type: string; - value: string; - typeDefs?: string | undefined; -} => { - const error = (message: string) => { - return commandObj.error( - `Failed converting to aqua. ${message}. At ${color.yellow( - currentNesting === "" ? "" : `${currentNesting}.`, - )}${color.yellow(fieldName)}: ${stringifyUnknown(valueToConvert)}`, - ); - }; - - if (typeof valueToConvert === "string") { - return { type: "string", value: `"${valueToConvert}"` }; - } - - if (typeof valueToConvert === "number") { - const isInteger = Number.isInteger(valueToConvert); - - const type = (() => { - if (useF64ForAllNumbers || !isInteger) { - return "f64"; - } - - if (valueToConvert < 0) { - return "i64"; - } - - return "u64"; - })(); - - const stringNumber = numToStr(valueToConvert); - - const value = - type === "f64" && isInteger ? `${stringNumber}.0` : stringNumber; - - return { type, value }; - } - - if (typeof valueToConvert === "boolean") { - return { type: "bool", value: boolToStr(valueToConvert) }; - } - - if (isNilInAqua(valueToConvert)) { - return NIL; - } - - const newNestingLevel = nestingLevel + 1; - const prevIndent = INDENTATION.repeat(nestingLevel); - const newIndent = INDENTATION.repeat(newNestingLevel); - - if (Array.isArray(valueToConvert)) { - const mappedToAqua = valueToConvert.map((valueToConvert) => { - return jsToAquaImpl({ - nestingLevel: newNestingLevel, - valueToConvert, - fieldName, - currentNesting, - useF64ForAllNumbers, - sortedCustomTypes, - level, - }); - }); - - const firstElementType = mappedToAqua[0]?.type ?? NIL.type; - const isNumberType = NUMBER_TYPES.includes(firstElementType); - - const type = isNumberType - ? mappedToAqua.reduce( - (acc, { type }) => { - if (!NUMBER_TYPES.includes(type)) { - return error("All array elements must be of the same type"); - } - - if (acc === "f64" || type === "f64") { - return "f64"; - } - - if (acc === "i64" || type === "i64") { - return "i64"; - } - - return "u64"; - }, - useF64ForAllNumbers ? "f64" : "u64", - ) - : firstElementType; - - const { typeDefs } = mappedToAqua[0] ?? {}; - - if ( - !isNumberType && - (!mappedToAqua.every((val) => { - return val.type === type; - }) || - !mappedToAqua.every((val) => { - return val.typeDefs === typeDefs; - })) - ) { - return error("All array elements must be of the same type"); - } - - return { - type: `[]${type}`, - value: `[\n${newIndent}${mappedToAqua - .map(({ value }) => { - return value; - }) - .join(`,\n${newIndent}`)}\n${prevIndent}]`, - typeDefs, - }; - } - - if (typeof valueToConvert === "object") { - assert( - valueToConvert !== null, - "we checked v is not null with isNilInAqua", - ); - - const newName = toAquaType(fieldName); - - if (newName instanceof Error) { - return error( - `Name must start with a letter. Got: ${color.yellow(newName)}`, - ); - } - - // Check "top" level type name is not clashing with the "second" level - if (level === "second" && newName === currentNesting) { - return error( - `Either rename your file so it is not called as your top-level object property ${color.yellow( - newName, - )} or pass a custom type name to be used instead`, - ); - } - - const objectEntries: [string, unknown][] = Object.entries(valueToConvert); - const objectProperties = JSON.stringify(Object.keys(valueToConvert).sort()); - - const { name: type } = sortedCustomTypes.find(({ properties }) => { - return properties === objectProperties; - }) ?? { - // Don't nest type names for top-level and direct child types - name: level === "rest" ? `${currentNesting}${newName}` : newName, - }; - - if (OPTIONAL in valueToConvert) { - const { type, value, typeDefs } = jsToAquaImpl({ - valueToConvert: valueToConvert[OPTIONAL], - fieldName, - currentNesting, - useF64ForAllNumbers, - nestingLevel, - sortedCustomTypes, - level, - }); - - return { - type: `?${type}`, - value: - IS_NIL in valueToConvert && valueToConvert[IS_NIL] === true - ? NIL.value - : `?[${value}]`, - typeDefs, - }; - } - - const { keyTypes, keyDataTypes, entries } = objectEntries.reduce<{ - keyTypes: string[]; - keyDataTypes: string[]; - entries: string[]; - }>( - ({ keyTypes, keyDataTypes, entries }, [fieldName, valueToConvert]) => { - const { - type: innerType, - value, - typeDefs, - } = jsToAquaImpl({ - currentNesting: type, - nestingLevel: newNestingLevel, - // each time we nest objects - the level changes: - // "top" -> "second" -> "rest" - level: level === "top" ? "second" : "rest", - valueToConvert, - fieldName, - useF64ForAllNumbers, - sortedCustomTypes, - }); - - const camelCasedKey = camelCase(fieldName); - const keyValidity = validateAquaName(camelCasedKey); - - if (typeof keyValidity === "string") { - return error(`Invalid key ${color.yellow(fieldName)} ${keyValidity}`); - } - - return { - keyTypes: [...keyTypes, ` ${camelCasedKey}: ${innerType}`], - keyDataTypes: - typeDefs === undefined ? keyDataTypes : [...keyDataTypes, typeDefs], - entries: [...entries, `\n${newIndent}${camelCasedKey}=${value}`], - }; - }, - { keyTypes: [], keyDataTypes: [], entries: [] }, - ); - - return { - type, - value: `${type}(${entries.join(",")}\n${INDENTATION.repeat( - nestingLevel, - )})`, - typeDefs: `${ - keyDataTypes.length === 0 - ? "" - : `${keyDataTypes.sort().join("\n\n")}\n\n` - }data ${type}:\n${keyTypes.sort().join("\n")}`, - }; - } - - return error(`Unsupported type: ${typeof valueToConvert}`); -}; - -export type CustomTypes = Array<{ name: string; properties: Array }>; - -const customTypesSchema: JSONSchemaType = { - type: "array", - items: { - type: "object", - properties: { - name: { type: "string" }, - properties: { - type: "array", - items: { type: "string" }, - }, - }, - required: ["name", "properties"], - }, -}; - -const customTypesValidator = ajv.compile(customTypesSchema); - -export async function fileToAqua( - inputPathArg: string | undefined, - outputDirPathArg: string | undefined, - useF64ForAllNumbers: boolean, - customTypesPath: string | undefined, - parseFn: (content: string) => unknown, -) { - let customTypes: CustomTypes = []; - - if (customTypesPath !== undefined) { - const content = await readFile(customTypesPath, FS_OPTIONS); - const parsedContent = parseFn(content); - - if (!customTypesValidator(parsedContent)) { - return commandObj.error( - `Invalid custom types file ${color.yellow( - customTypesPath, - )}: ${await validationErrorToString(customTypesValidator.errors)}`, - ); - } - - customTypes = parsedContent; - } - - const inputPath = - inputPathArg ?? (await input({ message: "Enter path to input file" })); - - const content = await readFile(inputPath, FS_OPTIONS); - const valueToConvert = parseFn(content); - - const inputPathDir = dirname(inputPath); - const fileName = parse(inputPath).name; - const fileNameWithExt = `${fileName}.${AQUA_EXT}`; - let outputPath = join(inputPathDir, fileNameWithExt); - - if (typeof outputDirPathArg === "string") { - await mkdir(outputDirPathArg, { recursive: true }); - outputPath = join(outputDirPathArg, fileNameWithExt); - } - - const aqua = jsToAqua({ - valueToConvert, - fileName, - useF64ForAllNumbers, - customTypes, - }); - - await writeFile(outputPath, aqua, FS_OPTIONS); - commandObj.logToStderr(`Created aqua file at ${color.yellow(outputPath)}`); -} diff --git a/packages/cli/package/src/lib/helpers/packModule.ts b/packages/cli/package/src/lib/helpers/packModule.ts deleted file mode 100644 index 52a331ccc..000000000 --- a/packages/cli/package/src/lib/helpers/packModule.ts +++ /dev/null @@ -1,168 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import { copyFile, mkdir, readFile } from "node:fs/promises"; -import { join, relative } from "node:path"; - -import { parse } from "@iarna/toml"; -import type { JSONSchemaType } from "ajv"; - -import { ajv, validationErrorToString } from "../ajvInstance.js"; -import { buildModules } from "../buildModules.js"; -import { commandObj } from "../commandObj.js"; -import { - type ModuleConfigReadonly, - initNewModuleConfig, -} from "../configs/project/module.js"; -import { - CARGO_TOML, - DEFAULT_IPFS_ADDRESS, - FS_OPTIONS, - MODULE_CONFIG_FULL_FILE_NAME, - WASM_EXT, -} from "../const.js"; -import { createIPFSClient } from "../localServices/ipfs.js"; -import type { MarineCLI } from "../marineCli.js"; -import { ensureFluenceTmpModulePath } from "../paths.js"; - -import { getModuleWasmPath } from "./downloadFile.js"; - -type PackModuleArgs = { - moduleConfig: ModuleConfigReadonly; - marineCli: MarineCLI; - marineBuildArgs: string | undefined; - destination: string; - bindingCrate: string | undefined; -}; - -export async function packModule({ - moduleConfig, - marineCli, - marineBuildArgs, - destination, - bindingCrate, -}: PackModuleArgs) { - await buildModules([moduleConfig], marineCli, marineBuildArgs); - const wasmPath = getModuleWasmPath(moduleConfig); - const tmpModuleDirPath = await ensureFluenceTmpModulePath(); - - const tmpModuleConfigDirPath = join( - tmpModuleDirPath, - MODULE_CONFIG_FULL_FILE_NAME, - ); - - await copyFile(moduleConfig.$getPath(), tmpModuleConfigDirPath); - - const tmpWasmPath = join( - tmpModuleDirPath, - `${moduleConfig.name}.${WASM_EXT}`, - ); - - await copyFile(wasmPath, tmpWasmPath); - - const moduleToPackConfig = await initNewModuleConfig( - tmpModuleConfigDirPath, - moduleConfig.name, - ); - - delete moduleToPackConfig.type; - // Have to disable this cause ipfs lib types look like any with "nodenext" moduleResolution - /* eslint-disable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call */ - const ipfsClient = await createIPFSClient(DEFAULT_IPFS_ADDRESS); - - const { cid } = await ipfsClient.add(await readFile(tmpWasmPath), { - cidVersion: 1, - onlyHash: true, - }); - - // eslint-disable-next-line no-restricted-syntax - moduleToPackConfig.cid = cid.toString(); - /* eslint-enable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call */ - - const resolvedBindingCrate = await resolveBindingCrate(bindingCrate); - - if (resolvedBindingCrate !== undefined) { - moduleToPackConfig.rustBindingCrate = resolvedBindingCrate; - } - - await moduleToPackConfig.$commit(); - - await mkdir(destination, { recursive: true }); - const tar = await import("tar"); - - await tar.c( - { - file: join(destination, `${moduleConfig.name}.tar.gz`), - gzip: true, - cwd: tmpModuleDirPath, - }, - [ - relative(tmpModuleDirPath, tmpModuleConfigDirPath), - relative(tmpModuleDirPath, tmpWasmPath), - ], - ); -} - -type ConfigToml = { - package: { - name: string; - version: string; - }; -}; - -const configTomlSchema: JSONSchemaType = { - type: "object", - properties: { - package: { - type: "object", - properties: { - name: { type: "string" }, - version: { type: "string" }, - }, - required: ["name", "version"], - }, - }, - required: ["package"], -}; - -const validateConfigToml = ajv.compile(configTomlSchema); - -async function resolveBindingCrate(bindingCrate: string | undefined) { - if (bindingCrate === undefined) { - return undefined; - } - - const bindingCrateFileContent = await readFile( - join(bindingCrate, CARGO_TOML), - FS_OPTIONS, - ); - - const parsedBindingCrate: unknown = parse(bindingCrateFileContent); - - if (!validateConfigToml(parsedBindingCrate)) { - return commandObj.error( - `Invalid binding crate ${CARGO_TOML} file. Errors: ${await validationErrorToString( - validateConfigToml.errors, - )}`, - ); - } - - return { - name: parsedBindingCrate.package.name, - version: parsedBindingCrate.package.version, - }; -} diff --git a/packages/cli/package/src/lib/helpers/serviceConfigToml.ts b/packages/cli/package/src/lib/helpers/serviceConfigToml.ts deleted file mode 100644 index 5d8822766..000000000 --- a/packages/cli/package/src/lib/helpers/serviceConfigToml.ts +++ /dev/null @@ -1,169 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import { writeFile } from "node:fs/promises"; -import { join } from "node:path"; - -import type { ModuleConfigReadonly } from "../configs/project/module.js"; -import type { - ServiceConfigReadonly, - OverridableServiceProperties, -} from "../configs/project/service.js"; -import { - COMPUTE_UNIT_MEMORY_STR, - FS_OPTIONS, - SERVICE_CONFIG_TOML_POSTFIX, -} from "../const.js"; -import { - ensureFluenceServiceConfigsDir, - ensureFluenceTmpVolumesModuleDir, - ensureFluenceTmpVolumesParticlesDir, - ensureFluenceTmpVolumesServiceDir, -} from "../paths.js"; - -import { getModuleWasmPath } from "./downloadFile.js"; - -/** - * Generates a service configuration TOML file. - * @param serviceName - Name from fluence.yaml or if it's missing - name from the serviceConfig - * @param serviceConfig - The service configuration. - * @param serviceOverridesFromFluenceYaml - The service overrides from fluence.yaml. - * @param allServiceModuleConfigs - An array of module configurations. - * @returns The path of the created service configuration TOML file. - */ -export async function genServiceConfigToml( - serviceName: string, - serviceConfig: ServiceConfigReadonly, - serviceOverridesFromFluenceYaml: OverridableServiceProperties | undefined, - allServiceModuleConfigs: Array, -) { - const serviceConfigTomlPath = await getServiceConfigTomlPath(serviceName); - - const serviceConfigToml = await createServiceConfigToml( - serviceName, - serviceConfig, - serviceOverridesFromFluenceYaml, - allServiceModuleConfigs, - ); - - const stringifyToTOML = (await import("@iarna/toml/stringify.js")).default; - - await writeFile( - serviceConfigTomlPath, - stringifyToTOML(serviceConfigToml), - FS_OPTIONS, - ); - - return serviceConfigTomlPath; -} - -export async function getServiceConfigTomlPath(serviceName: string) { - const serviceConfigsDir = await ensureFluenceServiceConfigsDir(); - return join( - serviceConfigsDir, - `${serviceName}${SERVICE_CONFIG_TOML_POSTFIX}`, - ); -} - -async function createServiceConfigToml( - serviceName: string, - serviceConfig: ServiceConfigReadonly, - serviceOverridesFromFluenceYaml: OverridableServiceProperties | undefined, - allServiceModuleConfigs: Array, -) { - const totalMemoryLimit = - serviceOverridesFromFluenceYaml?.totalMemoryLimit ?? - serviceConfig.totalMemoryLimit; - - return { - total_memory_limit: - totalMemoryLimit === undefined - ? // TODO: replace COMPUTE_UNIT_MEMORY_STR with calculated memory limit - COMPUTE_UNIT_MEMORY_STR - : totalMemoryLimit, - module: await createModuleConfigsToml(serviceName, allServiceModuleConfigs), - }; -} - -type TomlModuleConfig = { - name: string; - load_from?: string; - logger_enabled?: boolean; - logging_mask?: number; - wasi?: { - mapped_dirs?: Record; - envs?: Record; - }; - mounted_binaries?: Record; -}; - -function createModuleConfigsToml( - serviceName: string, - moduleConfigs: Array, -) { - return Promise.all( - moduleConfigs.map(async (moduleConfig) => { - const { - name, - repl: { loggerEnabled, loggingMask } = {}, - effects: { binaries } = {}, - } = moduleConfig; - - const load_from = getModuleWasmPath(moduleConfig); - - const tomlModuleConfig: TomlModuleConfig = { - name, - load_from, - }; - - if (loggerEnabled !== undefined) { - tomlModuleConfig.logger_enabled = loggerEnabled; - } - - if (loggingMask !== undefined) { - tomlModuleConfig.logging_mask = Number(loggingMask); - } - - if (binaries !== undefined) { - tomlModuleConfig.mounted_binaries = binaries; - } - - tomlModuleConfig.wasi = { - mapped_dirs: { - "/tmp": await ensureFluenceTmpVolumesServiceDir(serviceName, "tmp"), - "/storage": await ensureFluenceTmpVolumesServiceDir( - serviceName, - "storage", - ), - "/tmp/module": await ensureFluenceTmpVolumesModuleDir( - serviceName, - name, - join("tmp", "module"), - ), - "/storage/module": await ensureFluenceTmpVolumesModuleDir( - serviceName, - name, - join("storage", "module"), - ), - "/tmp/vault": await ensureFluenceTmpVolumesParticlesDir(), - }, - }; - - return tomlModuleConfig; - }), - ); -} diff --git a/packages/cli/package/src/lib/helpers/utils.ts b/packages/cli/package/src/lib/helpers/utils.ts index 28214a571..183cdb0ff 100644 --- a/packages/cli/package/src/lib/helpers/utils.ts +++ b/packages/cli/package/src/lib/helpers/utils.ts @@ -30,24 +30,6 @@ export function commaSepStrToArr(commaSepStr: string) { }); } -function comment(commentToken: string) { - return (text: string): string => { - return text - .split("\n") - .map((line) => { - return line === "" - ? "" - : line.replaceAll(/(^|\n)\s*/g, (spaces) => { - return `${spaces}${commentToken} `; - }); - }) - .join("\n"); - }; -} - -export const jsComment = comment("//"); -export const aquaComment = comment("--"); - function flagToArg( flagName: string, flagValue: string | number | boolean | undefined, @@ -96,9 +78,6 @@ export function removeProperties( ); } -export const LOGS_RESOLVE_SUBNET_ERROR_START = `Failed to resolve subnet:`; -export const LOGS_GET_ERROR_START = `Failed to get logs:`; - export function splitErrorsAndResults( array: Array, splitter: ( diff --git a/packages/cli/package/src/lib/helpers/validations.ts b/packages/cli/package/src/lib/helpers/validations.ts index 7f698bb1a..084a8c033 100644 --- a/packages/cli/package/src/lib/helpers/validations.ts +++ b/packages/cli/package/src/lib/helpers/validations.ts @@ -76,10 +76,10 @@ export const validateBatchAsync = async ( return errors.length === 0 ? true : errors.join("\n"); }; -export const isExactVersion = async (version: string): Promise => { +async function isExactVersion(version: string): Promise { const semver = await import("semver"); return semver.clean(version) === version; -}; +} export async function validateVersionsIsExact( dependency: string, diff --git a/packages/cli/package/src/lib/init.ts b/packages/cli/package/src/lib/init.ts deleted file mode 100644 index 0d14b4d03..000000000 --- a/packages/cli/package/src/lib/init.ts +++ /dev/null @@ -1,916 +0,0 @@ -/** - * Fluence CLI - * Copyright (C) 2024 Fluence DAO - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import { existsSync } from "node:fs"; -import { mkdir, readdir, stat, writeFile } from "node:fs/promises"; -import { join, relative, resolve, sep } from "node:path"; -import { sep as posixSep } from "node:path/posix"; -import { cwd } from "node:process"; - -import { color } from "@oclif/color"; - -import { jsonStringify } from "../common.js"; -import { - initNewFluenceConfig, - type FluenceConfig, -} from "../lib/configs/project/fluence.js"; -import { - type Template, - FS_OPTIONS, - RECOMMENDED_GITIGNORE_CONTENT, - TEMPLATES, - isTemplate, - CLI_NAME_FULL, - getMainAquaFileContent, - READMEs, - JS_CLIENT_NPM_DEPENDENCY, - README_MD_FILE_NAME, - TYPESCRIPT_NPM_DEPENDENCY, -} from "../lib/const.js"; -import { - ensureFrontendCompiledAquaPath, - ensureFluenceAquaServicesPath, - ensureServicesDir, - ensureVSCodeExtensionsJsonPath, - getGitignorePath, - setProjectRootDir, - getREADMEPath, - projectRootDir, - getFrontendPackageJSONPath, - getFrontendSrcPath, - getIndexHTMLPath, - getFrontendTsConfigPath, - getFrontendPath, - getFrontendCompiledAquaPath, - ensureGatewayCompiledAquaPath, - getGatewayPackageJSONPath, - getGatewaySrcPath, - getGatewayTsConfigPath, - getGatewayPath, - ensureAquaDir, - ensureDir, -} from "../lib/paths.js"; -import { confirm, input, list } from "../lib/prompt.js"; -import CLIPackageJSON from "../versions/cli.package.json" with { type: "json" }; - -import { addService } from "./addService.js"; -import { compileToFiles } from "./aqua.js"; -import { commandObj, isInteractive } from "./commandObj.js"; -import { initNewEnvConfig, initEnvConfig } from "./configs/project/env/env.js"; -import { - ensureComputerPeerConfigs, - initNewProviderConfig, -} from "./configs/project/provider/provider.js"; -import { initNewReadonlyServiceConfig } from "./configs/project/service.js"; -import { - COMPILE_AQUA_PROPERTY_NAME, - SERVICE_INTERFACE_FILE_HEADER, -} from "./const.js"; -import { addCountlyEvent } from "./countly.js"; -import { generateNewModule } from "./generateNewModule.js"; -import type { ProviderConfigArgs } from "./generateUserProviderConfig.js"; -import { getAquaImports } from "./helpers/aquaImports.js"; -import { ensureFluenceProject } from "./helpers/ensureFluenceProject.js"; -import { initMarineCli } from "./marineCli.js"; -import { updateRelaysJSON } from "./multiaddres.js"; -import { copyDefaultDependencies } from "./npm.js"; -import { getFrontendIndexTSorJSPath, ensureAquaMainPath } from "./paths.js"; -import { ensureFluenceEnv } from "./resolveFluenceEnv.js"; - -const selectTemplate = (): Promise