diff --git a/docs/features.mdx b/docs/features.mdx index 5dcf71f..395e2a0 100644 --- a/docs/features.mdx +++ b/docs/features.mdx @@ -36,7 +36,7 @@ Our MCP server, easily runnable with `npx -y @hyperweb/mcp-server@latest`, provi - **Starship (`setupStarship`, `generateStarshipConfig`, `verifyStarshipConfig`)** - Provides guidance on setting up Starship, a unified development and testing environment for Cosmos chains. - - Features a powerful configuration generator (`generateStarshipConfig`) that creates detailed `config.yaml` files based on user inputs for chains, relayers, explorers, and more. + - Features a powerful configuration helper (`generateStarshipConfig`) that provides comprehensive documentation and type definitions to assist in creating detailed `config.yaml` files. - Includes a validation tool (`verifyStarshipConfig`) to parse and check Starship YAML configurations against the official schema, ensuring correctness before deployment. --- diff --git a/docs/references.mdx b/docs/references.mdx index 478aff8..04b51c2 100644 --- a/docs/references.mdx +++ b/docs/references.mdx @@ -65,57 +65,16 @@ Tools for interacting with Starship, the unified development and testing environ ### `generateStarshipConfig` -- **Description**: Generates a Starship configuration file in YAML format based on detailed input options then creates the file (e.g., `starship/config.yaml`) in the workspace. +- **Description**: This tool helps generate Starship configuration files by providing comprehensive documentation and type definitions. - **Source**: `src/starship/tools/starship-config-gen.ts` -- **Parameters (StarshipConfigInput)**: - - `configFilePath` (string, required): The absolute path to the Starship configuration file (e.g., `<current_working_directory>/starship/config.yaml`). - - `configName` (string, optional, default: "starship"): Top-level configuration name. - - `configVersion` (string, optional, default: "1.6.0"): Top-level configuration version. - - `chains` (array of ChainConfig, required, min: 1): List of chain configurations. Each `ChainConfig` includes: - - `id` (string, required): Unique identifier for the chain. - - `name` (string, required): Type of chain (e.g., 'osmosis', 'custom'). - - `numValidators` (integer, required, positive): Number of validators. - - `image` (string, optional): Override default Docker image. - - `home` (string, optional): Home directory path (for `name: 'custom'`). - - `binary` (string, optional): Binary name (for `name: 'custom'`). - - `prefix` (string, optional): Address prefix (for `name: 'custom'`). - - `denom` (string, optional): Primary denomination (for `name: 'custom'`). - - `coins` (string, optional): Genesis coins. - - `hdPath` (string, optional): HD path. - - `coinType` (integer, optional): Coin type. - - `repo` (string URL, optional): Git repository URL. - - `ports` (object, optional): Port forwarding (`rest`, `rpc`, `grpc`, `faucet`, `exposer`). - - `resources` (object, optional): CPU/memory allocation (`cpu`, `memory`). - - `faucet` (object, optional): Faucet config (`enabled`, `type`, `image`, `concurrency`, `resources`). - - `build` (object, optional): On-the-fly build config (`enabled`, `source`). - - `upgrade` (object, optional): Upgrade prep config (`enabled`, `type`, `genesis`, `upgrades` array). - - `genesis` (object, optional): `genesis.json` patches (`app_state`). - - `scripts` (object, optional): Override setup scripts. - - `cometmock` (object, optional): CometMock config (`enabled`, `image`). - - `env` (array of EnvVar, optional): Custom environment variables (`name`, `value`). - - `ics` (object, optional): ICS config for consumer chains (`enabled`, `provider`). - - `balances` (array of BalanceEntry, optional): Initial balances (`address`, `amount`). - - `readinessProbe` (object, optional): Custom readiness probe. - - `config` (object, optional): Ethereum-specific config (if `name` is 'ethereum'). - - `relayers` (array of RelayerConfig, optional): List of relayer configurations. Each `RelayerConfig` includes: - - `name` (string, required): Name for the relayer instance. - - `type` (enum: `hermes` | `ts-relayer` | `go-relayer` | `neutron-query-relayer`, required): Type of relayer. - - `image` (string, optional): Override default Docker image. - - `replicas` (integer, optional, default: 1): Number of replicas (currently 1). - - `chains` (array of string, required, min: 2): Chain IDs to connect. - - `config` (object, optional): Hermes specific `config.toml` overrides. - - `ports` (object, optional): Hermes specific port forwarding (`rest`, `exposer`). - - `ics` (object, optional): Hermes specific ICS setup. - - `explorer` (ExplorerConfig, optional): Explorer configuration (`enabled`, `type`, `ports`, `resources`, `image`). - - `registry` (RegistryConfig, optional): Registry service configuration (`enabled`, `localhost`, `ports`, `resources`, `image`). -- **Output**: Text confirmation with the generated YAML content and writes the file to the specified `configFilePath`. +- **Parameters**: None. +- **Output**: Provides text content containing a comprehensive guide for generating a Starship configuration file. ### `verifyStarshipConfig` - **Description**: Parses and validates a Starship configuration YAML string against the known schema. -- **Source**: `src/starship/tools/starship-config-gen.ts` -- **Parameters (VerifyStarshipConfigInput)**: - - `configFilePath` (string, required): The absolute path to the Starship configuration file. +- **Source**: `src/starship/tools/starship-config-verify.ts` +- **Parameters**: - `yamlContent` (string, required): The Starship configuration content in YAML format as a string. - **Output**: Text indicating if the configuration is valid or invalid, with error details if invalid. diff --git a/package.json b/package.json index 979eada..a13b9ba 100644 --- a/package.json +++ b/package.json @@ -4,9 +4,10 @@ "description": "Model Context Protocol Hyperweb products", "main": "build/index.js", "scripts": { - "build": "tsc && npm run copy-prompts && chmod 755 build/index.js", - "copy-prompts": "./scripts/copy-prompts.sh", + "build": "rm -rf build && tsc && npm run copy-assets && chmod 755 build/index.js", + "copy-assets": "./scripts/copy-assets.sh", "categorize-snippets": "tsx src/interchainjs/scripts/categorize-snippets.ts", + "fetch-config-docs": "tsx src/starship/scripts/fetch-config-docs.ts", "clean": "rimraf build", "test": "vitest", "test:watch": "vitest --watch", @@ -26,6 +27,7 @@ "@biomejs/biome": "1.9.4", "@types/js-yaml": "^4.0.9", "@types/node": "^22.13.10", + "@types/node-fetch": "^2.6.12", "rimraf": "^6.0.1", "tsx": "^4.19.3", "typescript": "^5.8.2", @@ -44,6 +46,7 @@ "@modelcontextprotocol/sdk": "^1.7.0", "dotenv": "^16.4.7", "js-yaml": "^4.1.0", + "node-fetch": "^3.3.2", "zod": "^3.24.2" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 29c0000..63efd75 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,6 +17,9 @@ importers: js-yaml: specifier: ^4.1.0 version: 4.1.0 + node-fetch: + specifier: ^3.3.2 + version: 3.3.2 zod: specifier: ^3.24.2 version: 3.24.2 @@ -30,6 +33,9 @@ importers: '@types/node': specifier: ^22.13.10 version: 22.13.10 + '@types/node-fetch': + specifier: ^2.6.12 + version: 2.6.12 rimraf: specifier: ^6.0.1 version: 6.0.1 @@ -365,6 +371,9 @@ packages: '@types/js-yaml@4.0.9': resolution: {integrity: sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==} + '@types/node-fetch@2.6.12': + resolution: {integrity: sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==} + '@types/node@22.13.10': resolution: {integrity: sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw==} @@ -424,6 +433,9 @@ packages: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -465,6 +477,10 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + content-disposition@1.0.0: resolution: {integrity: sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==} engines: {node: '>= 0.6'} @@ -489,6 +505,10 @@ packages: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} + data-uri-to-buffer@4.0.1: + resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} + engines: {node: '>= 12'} + debug@4.3.6: resolution: {integrity: sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==} engines: {node: '>=6.0'} @@ -511,6 +531,10 @@ packages: resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} engines: {node: '>=6'} + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + depd@2.0.0: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} engines: {node: '>= 0.8'} @@ -558,6 +582,10 @@ packages: resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} engines: {node: '>= 0.4'} + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + esbuild@0.25.1: resolution: {integrity: sha512-BGO5LtrGC7vxnqucAe/rmvKdJllfGaYWdyABvyMoXQlfYMb2bbRuReWR5tEGE//4LcNJj9XrkovTqNYRFZHAMQ==} engines: {node: '>=18'} @@ -595,6 +623,10 @@ packages: resolution: {integrity: sha512-ORF7g6qGnD+YtUG9yx4DFoqCShNMmUKiXuT5oWMHiOvt/4WFbHC6yCwQMTSBMno7AqntNCAzzcnnjowRkTL9eQ==} engines: {node: '>= 18'} + fetch-blob@3.2.0: + resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} + engines: {node: ^12.20 || >= 14.13} + finalhandler@2.1.0: resolution: {integrity: sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==} engines: {node: '>= 0.8'} @@ -603,6 +635,14 @@ packages: resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} engines: {node: '>=14'} + form-data@4.0.2: + resolution: {integrity: sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==} + engines: {node: '>= 6'} + + formdata-polyfill@4.0.10: + resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} + engines: {node: '>=12.20.0'} + forwarded@0.2.0: resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} engines: {node: '>= 0.6'} @@ -647,6 +687,10 @@ packages: resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} engines: {node: '>= 0.4'} + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + hasown@2.0.2: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} @@ -753,6 +797,15 @@ packages: resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} engines: {node: '>= 0.6'} + node-domexception@1.0.0: + resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} + engines: {node: '>=10.5.0'} + deprecated: Use your platform's native DOMException instead + + node-fetch@3.3.2: + resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} @@ -1044,6 +1097,10 @@ packages: jsdom: optional: true + web-streams-polyfill@3.3.3: + resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} + engines: {node: '>= 8'} + which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -1274,6 +1331,11 @@ snapshots: '@types/js-yaml@4.0.9': {} + '@types/node-fetch@2.6.12': + dependencies: + '@types/node': 22.13.10 + form-data: 4.0.2 + '@types/node@22.13.10': dependencies: undici-types: 6.20.0 @@ -1337,6 +1399,8 @@ snapshots: assertion-error@2.0.1: {} + asynckit@0.4.0: {} + balanced-match@1.0.2: {} body-parser@2.1.0: @@ -1387,6 +1451,10 @@ snapshots: color-name@1.1.4: {} + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + content-disposition@1.0.0: dependencies: safe-buffer: 5.2.1 @@ -1408,6 +1476,8 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 + data-uri-to-buffer@4.0.1: {} + debug@4.3.6: dependencies: ms: 2.1.2 @@ -1418,6 +1488,8 @@ snapshots: deep-eql@5.0.2: {} + delayed-stream@1.0.0: {} + depd@2.0.0: {} destroy@1.2.0: {} @@ -1450,6 +1522,13 @@ snapshots: dependencies: es-errors: 1.3.0 + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + esbuild@0.25.1: optionalDependencies: '@esbuild/aix-ppc64': 0.25.1 @@ -1535,6 +1614,11 @@ snapshots: transitivePeerDependencies: - supports-color + fetch-blob@3.2.0: + dependencies: + node-domexception: 1.0.0 + web-streams-polyfill: 3.3.3 + finalhandler@2.1.0: dependencies: debug: 4.4.0 @@ -1551,6 +1635,17 @@ snapshots: cross-spawn: 7.0.6 signal-exit: 4.1.0 + form-data@4.0.2: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + mime-types: 2.1.35 + + formdata-polyfill@4.0.10: + dependencies: + fetch-blob: 3.2.0 + forwarded@0.2.0: {} fresh@0.5.2: {} @@ -1597,6 +1692,10 @@ snapshots: has-symbols@1.1.0: {} + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + hasown@2.0.2: dependencies: function-bind: 1.1.2 @@ -1677,6 +1776,14 @@ snapshots: negotiator@1.0.0: {} + node-domexception@1.0.0: {} + + node-fetch@3.3.2: + dependencies: + data-uri-to-buffer: 4.0.1 + fetch-blob: 3.2.0 + formdata-polyfill: 4.0.10 + object-assign@4.1.1: {} object-inspect@1.13.4: {} @@ -1979,6 +2086,8 @@ snapshots: - tsx - yaml + web-streams-polyfill@3.3.3: {} + which@2.0.2: dependencies: isexe: 2.0.0 diff --git a/scripts/copy-assets.sh b/scripts/copy-assets.sh new file mode 100755 index 0000000..7ff6346 --- /dev/null +++ b/scripts/copy-assets.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +# Ensure the script fails on any error +set -e + +# Define the directories to copy (add more as needed) +ASSET_DIRS=( + "prompts" + "data" + "migration-examples" +) + +# Process each asset directory type +for DIR_TYPE in "${ASSET_DIRS[@]}"; do + + # Find all directories of current type under src + FOUND_DIRS=$(find src -type d -name "$DIR_TYPE") + + # Process each found directory + for FOUND_DIR in $FOUND_DIRS; do + # Get the relative path without 'src/' + REL_PATH=$(dirname "$FOUND_DIR" | sed 's|^src/||') + + # Create the target directory + TARGET_DIR="build/$REL_PATH/$DIR_TYPE" + + # Create directory if it doesn't exist + mkdir -p "$TARGET_DIR" + + # Copy the contents + cp -r "$FOUND_DIR"/* "$TARGET_DIR/" 2>/dev/null || true + done +done + +echo "Assets copied successfully" diff --git a/scripts/copy-prompts.sh b/scripts/copy-prompts.sh deleted file mode 100755 index bede0b5..0000000 --- a/scripts/copy-prompts.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/bash - -# Ensure the script fails on any error -set -e - -# Find all prompts directories under src -PROMPTS_DIRS=$(find src -type d -name prompts) - -# Process each prompts directory -for PROMPT_DIR in $PROMPTS_DIRS; do - # Get the relative path without 'src/' - REL_PATH=$(dirname "$PROMPT_DIR" | sed 's|^src/||') - - # Create the target directory - mkdir -p "build/$REL_PATH" - - # Copy the prompts - cp -r "$PROMPT_DIR" "build/$REL_PATH/" -done - -# Copy migration-examples directory -if [ -d "src/interchainjs/migration-examples" ]; then - mkdir -p "build/interchainjs" - cp -r "src/interchainjs/migration-examples" "build/interchainjs/" -fi diff --git a/src/index.ts b/src/index.ts index aefd879..1cd0108 100644 --- a/src/index.ts +++ b/src/index.ts @@ -13,6 +13,7 @@ import { registerStarshipSetupTool } from './starship/tools/starship-setup.js'; import { registerUseChainRegistryTool } from './chain-registry/tools/use-chain-registry.js'; import { registerUseInterchainKitTool } from './interchain-kit/tools/use-interchain-kit.js'; import { registerMigrateFromCosmosKitToInterchainKitTool } from './interchain-kit/tools/migrate-from-cosmos-kit-to-interchain-kit.js'; +import { registerStarshipConfigVerifyTool } from './starship/tools/starship-config-verify.js'; // Get package.json version const __filename = fileURLToPath(import.meta.url); @@ -31,6 +32,7 @@ async function main() { // Starship registerStarshipSetupTool(server); registerStarshipConfigGenTool(server); + registerStarshipConfigVerifyTool(server); // InterchainJS registerMigrateToInterchainjsTool(server); diff --git a/src/starship/data/config-types.ts b/src/starship/data/config-types.ts new file mode 100644 index 0000000..0483506 --- /dev/null +++ b/src/starship/data/config-types.ts @@ -0,0 +1,279 @@ +// Top-level configuration interface +export interface StarshipConfig { + /** Top-level configuration name */ + name: string; + /** Top-level configuration version (default: '1.6.0') */ + version: string; + /** List of chain configurations (minimum: 1) */ + chains: ChainOutput[]; + /** List of relayer configurations (optional) */ + relayers?: RelayerOutput[]; + /** Explorer configuration (optional) */ + explorer?: ExplorerOutput; + /** Registry service configuration (optional) */ + registry?: RegistryOutput; +} + +// --- Output Type Definitions --- + +/** Port forwarding configuration for the chain's genesis node */ +export interface PortConfig { + /** REST port */ + rest?: number; + /** RPC port */ + rpc?: number; + /** gRPC port */ + grpc?: number; + /** Faucet port */ + faucet?: number; + /** Exposer sidecar port */ + exposer?: number; +} + +/** Resource allocation for CPU and memory */ +interface ResourceConfig { + /** CPU resource allocation (e.g., '0.5', '1') */ + cpu?: string; + /** Memory resource allocation (e.g., '200M', '1Gi') */ + memory?: string; +} + +/** Configuration for the chain's faucet service */ +interface FaucetConfig { + /** Whether to enable the faucet (default: true) */ + enabled?: boolean; + /** Type of faucet to use (default: 'cosmjs', allowed: ['cosmjs', 'starship']) */ + type?: 'cosmjs' | 'starship'; + /** Custom Docker image for the faucet */ + image?: string; + /** Number of concurrent requests the faucet can handle */ + concurrency?: number; + /** Resource allocation for the faucet */ + resources?: ResourceConfig; +} + +/** Configuration for building chain binaries during setup */ +interface BuildConfig { + /** Whether to enable building the chain binaries on the fly */ + enabled: boolean; + /** Source to build from (tag, commit, or branch) */ + source: string; +} + +interface UpgradeEntry { + /** Upgrade proposal name (e.g., 'v14') */ + name: string; + /** Upgrade chain version (tag, branch, commit) */ + version: string; +} + +/** Configuration for preparing chain software upgrades using Cosmovisor */ +interface UpgradeConfig { + /** Whether to enable upgrade preparation */ + enabled: boolean; + /** Type must be 'build' for building upgrade versions */ + type: 'build'; + /** Current chain version (tag, branch, commit) */ + genesis: string; + /** List of upgrade versions to prepare (minimum: 1) */ + upgrades: UpgradeEntry[]; +} + +/** Patch configuration for the chain's genesis.json file */ +export interface GenesisConfig { + /** Patches to apply to the 'app_state' section of genesis.json */ + app_state?: Record; +} + +interface ScriptEntry { + /** Path to the script file, relative to config */ + file: string; +} + +/** Override default scripts used during chain setup (Requires using scripts/install.sh) */ +interface ScriptsConfig { + createGenesis?: ScriptEntry; + updateGenesis?: ScriptEntry; + updateConfig?: ScriptEntry; + createValidator?: ScriptEntry; + transferTokens?: ScriptEntry; + buildChain?: ScriptEntry; +} + +/** Configuration for running the chain with CometMock */ +interface CometMockConfig { + /** Whether to enable CometMock */ + enabled: boolean; + /** Custom Docker image for CometMock (e.g., version tag) */ + image?: string; +} + +/** Environment variable definition */ +interface EnvVar { + /** Name of the environment variable */ + name: string; + /** Value of the environment variable */ + value: string; +} + +/** Interchain Security (ICS) configuration (for consumer chains only) */ +interface IcsConfig { + /** Whether to enable Interchain Security */ + enabled: boolean; + /** Chain ID of the ICS provider chain for this consumer */ + provider: string; +} + +/** Initial balance for a specific address */ +interface BalanceEntry { + /** Address to receive the balance */ + address: string; + /** Amount and denom (e.g., '10000uosmo') */ + amount: string; +} + +/** Custom readiness probe configuration for chain pods */ +interface ReadinessProbeExec { + /** Command array to execute */ + command: string[]; +} +interface ReadinessProbeConfig { + exec?: ReadinessProbeExec; + /** Delay before the first probe */ + initialDelaySeconds?: number; + /** How often to perform the probe */ + periodSeconds?: number; +} + +/** Ethereum-specific configuration (only applicable if name is 'ethereum') */ +interface EthSubConfig { + /** Whether to enable this component (default: true) */ + enabled?: boolean; + /** Custom Docker image for this component */ + image?: string; + /** Number of validators (default: 1, must be positive) */ + numValidator?: number; +} + +interface EthConfigOutput { + beacon?: EthSubConfig; + validator?: EthSubConfig; + prysmctl?: { image?: string }; +} + +export interface ChainOutput { + /** Unique identifier for the chain (e.g., osmosis-1) */ + id: string; + /** Type of chain (e.g., 'osmosis', 'cosmoshub') or 'custom' for full manual configuration */ + name: string; + /** Number of validators for the chain (must be >= 1) */ + numValidators: number; + /** Override default Docker image for the chain (e.g., 'ghcr.io/osmosis-labs/osmosis:v25.0.0') */ + image?: string; + /** Home directory path (needed for name: 'custom') */ + home?: string; + /** Binary name (needed for name: 'custom') */ + binary?: string; + /** Address prefix (needed for name: 'custom') */ + prefix?: string; + /** Primary denomination (needed for name: 'custom') */ + denom?: string; + /** Genesis coins (e.g., '1000uosmo,1000uion') */ + coins?: string; + /** HD path (e.g., "m/44'/118'/0'/0/0") */ + hdPath?: string; + /** Coin type (e.g., 118) */ + coinType?: number; + /** Git repository URL (needed for name: 'custom' or build/upgrade) */ + repo?: string; + ports?: PortConfig; + resources?: ResourceConfig; + faucet?: FaucetConfig; + build?: BuildConfig; + upgrade?: UpgradeConfig; + genesis?: GenesisConfig; + scripts?: ScriptsConfig; + cometmock?: CometMockConfig; + /** Custom environment variables for chain containers */ + env?: EnvVar[]; + ics?: IcsConfig; + /** Set initial balances for specific addresses */ + balances?: BalanceEntry[]; + readinessProbe?: ReadinessProbeConfig; + config?: EthConfigOutput; +} + +/** Port forwarding for the relayer (Hermes only) */ +export interface RelayerPortsConfig { + /** Hermes REST API port */ + rest?: number; + /** Relayer exposer service port */ + exposer?: number; +} + +/** Interchain Security (ICS) setup for the Hermes relayer connection */ +interface RelayerHermesConfig { + /** Hermes relayer specific configuration overrides (refer to Hermes config.toml) */ + config?: Record; + ports?: RelayerPortsConfig; + /** ICS configuration for Hermes */ + ics?: { + /** Whether to enable ICS setup for the relayer */ + enabled: boolean; + /** Chain ID of the ICS consumer chain */ + consumer: string; + /** Chain ID of the ICS provider chain */ + provider: string; + }; +} + +export interface RelayerOutput { + /** Name for the relayer instance (e.g., osmo-gaia) */ + name: string; + /** Type of the relayer (allowed: ['hermes', 'ts-relayer', 'go-relayer', 'neutron-query-relayer']) */ + type: 'hermes' | 'ts-relayer' | 'go-relayer' | 'neutron-query-relayer'; + /** Override default Docker image for the relayer */ + image?: string; + /** Number of replicas (currently only 1 supported, default: 1) */ + replicas?: number; + /** List of chain IDs to connect with this relayer (minimum: 2) */ + chains: string[]; + /** Hermes specific configuration */ + config?: Record; + /** Hermes specific ports */ + ports?: RelayerPortsConfig; + /** Hermes specific ICS configuration */ + ics?: { enabled: boolean; consumer: string; provider: string }; +} + +export interface ExplorerPortsConfig { + /** Local port for explorer UI */ + rest?: number; +} + +export interface ExplorerOutput { + /** Whether to enable the explorer */ + enabled: boolean; + /** Type of explorer (currently only 'ping-pub', default: 'ping-pub') */ + type?: 'ping-pub'; + ports?: ExplorerPortsConfig; + resources?: ResourceConfig; + /** Override default Docker image for the explorer */ + image?: string; +} + +export interface RegistryPortsConfig { + /** Local port for registry API */ + rest?: number; +} + +export interface RegistryOutput { + /** Whether to enable the registry service */ + enabled: boolean; + /** Set API endpoints to localhost using chain ports (default: true) */ + localhost?: boolean; + ports?: RegistryPortsConfig; + resources?: ResourceConfig; + /** Override default Docker image for the registry service */ + image?: string; +} diff --git a/src/starship/data/example-tests/.gitkeep b/src/starship/data/example-tests/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/starship/data/starship-config-format.md b/src/starship/data/starship-config-docs.md similarity index 59% rename from src/starship/data/starship-config-format.md rename to src/starship/data/starship-config-docs.md index fbc1886..0165284 100644 --- a/src/starship/data/starship-config-format.md +++ b/src/starship/data/starship-config-docs.md @@ -1,18 +1,18 @@ -# Starship MCP Tool - Configuration generation format and guide +# Configuration In Starship one can define the infra required with a simple config file. The config file one specifies is merged with the default [`values.yaml`](https://github.com/hyperweb-io/starship/blob/main/starship/charts/devnet/values.yaml) file before the infra is spun up. All supported directives are present in the default `values.yaml`. We will go over most used ones here. Here is a basic example that will spin up: -- 2 chains with 2 validators each -- Hermes relayer between them (by default will create the IBC-transfer ports and channels) -- Explorer instance: ping-pub with the 2 chains configuration -- Registry service: Analogous to cosmos/chain-registry, following the same schemas +* 2 chains with 2 validators each +* Hermes relayer between them (by default will create the IBC-transfer ports and channels) +* Explorer instance: ping-pub with the 2 chains configuration +* Registry service: Analogous to cosmos/chain-registry, following the same schemas ```yaml name: starship -version: 1.6.0 +version: 1.7.0 chains: - id: osmosis-1 @@ -50,95 +50,93 @@ registry: Note this is a basic configuration file. We have various other directives and operations we can perform just with the config file directives that we will go into details into. -## Chains YAML Syntax +---------------------------------------- + +# Chains YAML Syntax Here we will go into details of the `chains` top level directive in the Starship config file. -### `id` +## `id` ID of the chain, this is used as the `chain-id` of the chain - ```yaml chains: - - id: osmosis-1 - ... - - id: gaia-2 - ... - - id: juno-1 - ... +- id: osmosis-1 + ... +- id: gaia-2 + ... +- id: juno-1 + ... ``` -### `name` +## `name` Type of chain is a short hand to use the default key values for a chain found [here](https://github.com/hyperweb-io/starship/blob/main/starship/charts/devnet/values.yaml#L54...#L244) - ```yaml chains: - - id: osmosis-1 - name: osmosis - ... - - id: gaia-2 - name: cosmoshub - ... +- id: osmosis-1 + name: osmosis + ... +- id: gaia-2 + name: cosmoshub + ... ``` One can override the default values supported by `defaultChain` by simply mentioning it in the chain directive. -Here is how one can set the docker image of the default values: - +Here is how one can set the docker image of the default values ```yaml chains: - - id: osmosis-1 - name: osmosis - image: ghcr.io/hyperweb-io/starship/osmosis:v25.0.0 - coins: 100000000000000uosmo - ... +- id: osmosis-1 + name: osmosis + image: ghcr.io/hyperweb-io/starship/osmosis:v25.0.0 + coins: 100000000000000uosmo + ... ``` ### `name: custom` Optionally one can define the type to `custom`, and pass all the default params directly into the chain directive. This is useful when one is trying setup a chain not supported in the `defaultChains`. - ```yaml chains: - - id: osmosis-1 - name: custom - image: ghcr.io/hyperweb-io/starship/osmosis:v25.0.0 - home: /root/.osmosisd - binary: osmosisd - prefix: osmo - denom: uosmo - coins: 100000000000000uosmo,100000000000000uion - hdPath: m/44'/118'/0'/0/0 - coinType: 118 - repo: https://github.com/osmosis-labs/osmosis - ... -``` - -### `image` (optional) +- id: osmosis-1 + name: custom + image: ghcr.io/hyperweb-io/starship/osmosis:v25.0.0 + home: /root/.osmosisd + binary: osmosisd + prefix: osmo + denom: uosmo + coins: 100000000000000uosmo,100000000000000uion + hdPath: m/44'/118'/0'/0/0 + coinType: 118 + repo: https://github.com/osmosis-labs/osmosis + ... +``` + +## `image` (optional) Already mentioned above, but here we will go deeper into how the docker images are used for the chain. By default this value is taken from the `name` directive. This is the standard way of running chains at specific versions. ```yaml chains: - - id: osmosis-1 - name: osmosis - image: "" - ... +- id: osmosis-1 + name: osmosis + image: "" + ... ``` > One can use any docker image as long as it follows the following rules: -> - Packages `curl,make,bash,jq,sed` -> - Chain binaries installed in $PATH +> * Packages `curl,make,bash,jq,sed` +> * Chain binaries installed in $PATH We create docker images with this [Dockerfile](https://github.com/hyperweb-io/starship/blob/main/starship/docker/chains/Dockerfile), using Heighliner base images. All supported docker images can be found [here](https://github.com/orgs/hyperweb-io/packages?repo_name=starship) -### `numValidators` +## `numValidators` Number of validators to run for the chain. It must be greater than 1, can go up to as many resources available. ```yaml chains: - - id: osmosis-1 - name: osmosis - numValidators: 2 +- id: osmosis-1 + name: osmosis + numValidators: 2 ``` > Note: The first node spun up is a `genesis` node, which spins up before all other validators. Once the genesis node starts @@ -147,54 +145,53 @@ chains: If number of validators is more than the mnemonics available in the [`keys.json`](https://github.com/hyperweb-io/starship/blob/main/starship/charts/devnet/configs/keys.json#L9) file which is 5, then other validators are created with random mnemonic on initialization. -### `ports` +## `ports` Ports directive in the `chains` directive is used for `kubectl port-forward` forwarding kubernetes service ports to local host. - ```yaml chains: - - id: osmosis-1 - name: osmosis - numValidators: 2 - ports: - rest: 1317 # Rest endpoint of the Genesis validator node (most used) - rpc: 26657 # RPC endpoint of the genesis validator node (most used) - grpc: 9091 # GRPC endpoint of the genesis validator node (less used) - faucet: 8001 # Faucet running next to the genesis node (most used) - exposer: 9090 # Exposer sidecar port (less used) +- id: osmosis-1 + name: osmosis + numValidators: 2 + ports: + rest: 1317 # Rest endpoint of the Genesis validator node (most used) + rpc: 26657 # RPC endpoint of the genesis validator node (most used) + grpc: 9091 # GRPC endpoint of the genesis validator node (less used) + faucet: 8001 # Faucet running next to the genesis node (most used) + exposer: 9090 # Exposer sidecar port (less used) ``` +> Note: Websockets are exposed at `ws://localhost:26657/websocket` for cosmos chains. + Available endpoints for extra services: -- `exposer`: https://github.com/hyperweb-io/starship/blob/main/starship/proto/exposer/service.proto#L15 -- `faucet`: https://github.com/cosmos/cosmjs/blob/main/packages/faucet/README.md#using-the-faucet +* `exposer`: https://github.com/hyperweb-io/starship/blob/main/starship/proto/exposer/service.proto#L15 +* `faucet`: https://github.com/cosmos/cosmjs/blob/main/packages/faucet/README.md#using-the-faucet > Note: Make sure the ports are not overlapping in the config file -### `resources` (optional) +## `resources` (optional) Resource directive is something with which you can control how much resources to provide to each of the chain node. - ```yaml chains: - - id: osmosis-1 - name: osmosis - numValidators: 1 - resources: - cpu: 1 # 1 CPU - memory: 1Gi # 1 GB +- id: osmosis-1 + name: osmosis + numValidators: 1 + resources: + cpu: 1 # 1 CPU + memory: 1Gi # 1 GB ``` Main benefit of this directive is to closely control the resources provided for each of the validator node. One can provide fractional cpus and memory as per the resource constraints of the system Usually when running in the CI or locally you can provide partial resources like following: - ```yaml chains: - - id: osmosis-1 - name: osmosis - numValidators: 1 - resources: - cpu: "0.2" # 0.2 CPU - memory: "400M" # 0.4 GB +- id: osmosis-1 + name: osmosis + numValidators: 1 + resources: + cpu: "0.2" # 0.2 CPU + memory: "400M" # 0.4 GB ``` For more details on the resource directive have a look at [kubernetes resources](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/#meaning-of-cpu) @@ -202,107 +199,103 @@ For more details on the resource directive have a look at [kubernetes resources] > Note: If one provides less resources, the devnet will be stable for shorter timeframe. Nodes will start dying or restarting > We recommend to specify resources as per your requirements -### `faucet` (optional) +## `faucet` (optional) Every genesis node runs a faucet. We support 2 types of faucet: -- [starship faucet](https://github.com/hyperweb-io/starship/tree/main/starship/faucet), by default -- [cosmjs faucet](https://github.com/cosmos/cosmjs/tree/main/packages/faucet) +* [starship faucet](https://github.com/hyperweb-io/starship/tree/main/starship/faucet), by default +* [cosmjs faucet](https://github.com/cosmos/cosmjs/tree/main/packages/faucet) Setting for cosmjs-faucet [here](https://github.com/hyperweb-io/starship/blob/main/starship/charts/devnet/values.yaml#L32...#L38) but can be overridden with this directive, specially for compatible cosmjs version with the chain version. ```yaml chains: - - id: osmosis-1 - name: osmosis - numValidators: 1 - faucet: - enabled: true # optional, default is true, need to specify if want to disable faucet, set to false - type: cosmjs # default is cosmjs, allowed [cosmjs, starship] - image: ghcr.io/hyperweb-io/starship/cosmjs-faucet:v0.30.1 # optional, default image set if not specified - concurrency: 2 # optional, default is 5 - resources: - cpu: "0.3" - memory: "600M" +- id: osmosis-1 + name: osmosis + numValidators: 1 + faucet: + enabled: true # optional, default is true, need to specify if want to disable faucet, set to false + type: cosmjs # default is cosmjs, allowed [cosmjs, starship] + image: ghcr.io/hyperweb-io/starship/cosmjs-faucet:v0.30.1 # optional, default image set if not specified + concurrency: 2 # optional, default is 5 + resources: + cpu: "0.3" + memory: "600M" ``` -By default, we enable cosmjs faucet. In case `faucet.enabled` is set to `false`, then we manually add +By default, we enable cosmjs faucet. Incase `faucet.enabled` is set to `false`, then we manually add validator and relayer keys at gentx on genesis. This will take longer during initialization. > Note `concurrency` in `faucet` is the number of concurrent requests the faucet can handle. -> If you are running a chain with less resources, or want faster startup time, -> then you can reduce the `concurrency` to a lower number. +If you are running a chain with less resources, or want faster startup time, +then you can reduce the `concurrency` to a lower number. -### `build` (optional) +## `build` (optional) With the `build` directive in the chain, one can basically build chain binaries on the fly before starting the chain. When the directive is `enabled`, then the docker image is set to a [`runner` docker image](https://github.com/hyperweb-io/starship/blob/main/starship/docker/starship/runner/Dockerfile) which is a basic alpine image with starship dependencies. - ```yaml chains: - - id: osmosis-1 - name: osmosis - numValidators: 2 - build: - enabled: true - source: v15.0.0 # can be a tag or a commit or a branch +- id: osmosis-1 + name: osmosis + numValidators: 2 + build: + enabled: true + source: v15.0.0 # can be a tag or a commit or a branch ``` - We fetch the source from the `repo` defined in the `defaultChains`. If you are using the `name: custom`, then you need to specify `repo` directive, from where to get the source. -### `upgrade` (optional) +## `upgrade` (optional) If you want to perform a software upgrade on a chain, then this directive is here help. This will not perform the chain upgrade, but prepare the chain nodes to be able to do an actual software upgrade. - ```yaml chains: - - id: osmosis-1 - name: osmosis - numValidators: 2 - upgrade: - enabled: true # enable the directive - type: build # type build specifies, this will be similar to the `build` directive - genesis: v13.0.0 # current chain version, tag, branch or commit - upgrades: - - name: v14 # upgrade proposal name - version: v14.0.0 # upgrade chain version, tag, branch or commit - - name: v15 - version: v15.0.0-rc1 # next chain upgrade version +- id: osmosis-1 + name: osmosis + numValidators: 2 + upgrade: + enabled: true # enable the directive + type: build # type build specifies, this will be similar to the `build` directive + genesis: v13.0.0 # current chain version, tag, branch or commit + upgrades: + - name: v14 # upgrade proposal name + version: v14.0.0 # upgrade chain version, tag, branch or commit + - name: v15 + version: v15.0.0-rc1 # next chain upgrade version ``` We use [cosmovisor](https://docs.cosmos.network/v0.47/tooling/cosmovisor) to run the validators when this directive is enabled, which allows for external software-upgrade-proposal. -### `genesis` (optional) +## `genesis` (optional) Patch `genesis.json` file directly from the config file using this directive. Once the genesis node creates the genesis file, then the patch for genesis is applied. - ```yaml chains: - - id: osmosis-1 - name: osmosis - numValidators: 2 - genesis: - app_state: - staking: - params: - unbonding_time: "5s" +- id: osmosis-1 + name: osmosis + numValidators: 2 + genesis: + app_state: + staking: + params: + unbonding_time: "5s" ``` -### `scripts` (optional) +## `scripts` (optional) Scripts directive will replace the default scripts with the given scripts. In order to use this directive, one must use [`scripts/install.sh`](https://github.com/hyperweb-io/starship/blob/main/starship/scripts/install.sh) script for running the helm chart. -> Since the local scripts are converted into configmaps that are then created in the kubernetes cluster, there is a limit of 1MB +> Since the local scripts are converted into configmaps that are then created in the kubernetes cluster, there is a limit of 1MBi Types of scripts that are allowed inside the `scripts` dir are: -- `createGenesis`: Script used in the genesis node that will create the `genesis.json` file. Default: [`create-genesis.sh`](https://github.com/hyperweb-io/starship/blob/main/starship/charts/devnet/scripts/default/create-genesis.sh) -- `updateGenesis`: Script used to make custom changes to the genesis file. Default: [`update-genesis.sh`](https://github.com/hyperweb-io/starship/blob/main/starship/charts/devnet/scripts/default/update-genesis.sh) -- `updateConfig`: Script used to make custom changes to the config files. Default: [`update-config.sh`](https://github.com/hyperweb-io/starship/blob/main/starship/charts/devnet/scripts/default/update-config.sh) -- `createValidator`: Script to run the `create-validator` txn on the validator nodes after spinup. Note this script is run as part of the `PostStartHook` of the validator pods. Default: [`create-validator.sh`](https://github.com/hyperweb-io/starship/blob/main/starship/charts/devnet/scripts/default/create-validator.sh) -- `transferTokens`: Script run as part of the Starship glue code. This script is related to the faucet that we run. Default: [`transfer-tokens.sh`](https://github.com/hyperweb-io/starship/blob/main/charts/starship/devnet/scripts/default/transfer-tokens.sh) -- `buildChain`: Script that builds chain binaries, based on the `build` directive. Default: [`build-chain.sh`](https://github.com/hyperweb-io/starship/blob/main/starship/charts/devnet/scripts/default/build-chain.sh) +* `createGenesis`: Script used in the genesis node that will create the `genesis.json` file. Default: [`create-genesis.sh`](https://github.com/hyperweb-io/starship/blob/main/starship/charts/devnet/scripts/default/create-genesis.sh) +* `updateGenesis`: Script used to make custom changes to the genesis file. Default: [`update-genesis.sh`](https://github.com/hyperweb-io/starship/blob/main/starship/charts/devnet/scripts/default/update-genesis.sh) +* `updateConfig`: Script used to make custom changes to the config files. Default: [`update-config.sh`](https://github.com/hyperweb-io/starship/blob/main/starship/charts/devnet/scripts/default/update-config.sh) +* `createValidator`: Script to run the `create-validator` txn on the validator nodes after spinup. Note this script is run as part of the `PostStartHook` of the validator pods. Default: [`create-validator.sh`](https://github.com/hyperweb-io/starship/blob/main/starship/charts/devnet/scripts/default/create-validator.sh) +* `transferTokens`: Script run as part of the Starship glue code. This script is related to the faucet that we run. Default: [`transfer-tokens.sh`](https://github.com/hyperweb-io/starship/blob/main/charts/starship/devnet/scripts/default/transfer-tokens.sh) +* `buildChain`: Script that builds chain binaries, based on the `build` directive. Default: [`build-chain.sh`](https://github.com/hyperweb-io/starship/blob/main/starship/charts/devnet/scripts/default/build-chain.sh) One can choose the sub-scripts that need to be overwritten, default scripts will be used instead. @@ -327,7 +320,6 @@ chains: > Note `file` path is relative to the config file itself Dir structure should look something like: - ```bash config.yaml scripts/ @@ -338,159 +330,295 @@ scripts/ transfer-custom-tokens.sh ``` -### `cometmock` (optional) +## `cometmock` (optional) A dedicated feature flag, that will run the chain with InformalSystem's [CometMock](https://github.com/informalsystems/CometMock) binary. With `cometmock` enabled we will run all validators of the chain with out of process cometbft, that connect with the cometmock pod. -Enabling cometmock is really simple with following: - +Enabling cometmock is really simple with following ```yaml chains: - - id: cosmoshub-4 - name: cosmoshub - numValidators: 4 - cometmock: - enabled: true - image: ghcr.io/informalsystems/cometmock:v0.37.x # optional, default is 0.37.x +- id: cosmoshub-4 + name: cosmoshub + numValidators: 4 + cometmock: + enabled: true + image: ghcr.io/informalsystems/cometmock:v0.37.x ## optional, default is 0.37.x ``` By default we use the cometbft `v0.37.x` version, but this can be set to other versions from cometmock [releases](https://github.com/informalsystems/CometMock/pkgs/container/cometmock). -Notable other versions are: `v0.34.x` as well as `v0.38.x`. +Noteable other versions are: `v0.34.x` as well as `v0.38.x`. If `cometmock` is enabled, during port-forwarding, we forward the `ports.rpc` to the cometmock endpoint. For in-cluster communication, make sure to connect to the cometmock service instead of validator service for rpc connections. -### `env` (optional) +## `env` (optional) The `env` directive allows you to set custom environment variables for the validator and genesis containers. This can be useful for enabling features like debug logging or configuring specific settings. The `env` directive expects an array of objects, each containing a `name` and `value` field. Here's an example of how to use the `env` directive: - ```yaml chains: - - id: agoriclocal - name: agoric - numValidators: 1 - env: - - name: DEBUG - value: SwingSet:vat,SwingSet:ls +- id: agoriclocal + name: agoric + numValidators: 1 + env: + - name: DEBUG + value: SwingSet:vat,SwingSet:ls ``` ### Learn more -To learn more about CometMock, have a look at the [blog](https://informal.systems/blog/cometmock) that explains its working. +To more about CometMock, have a look at the [blog](https://informal.systems/blog/cometmock) that explains its working -> Note: Current support of cometmock has been tested with multiple chains with version `0.34.x` and `0.37.x`. +> Note: Current support of cometmock has been tested with multiple chains with verion `0.34.x` and `0.37.x`. -### `ics` (optional) -The `ics` directive allows you to enable interchain security on the chain. This is useful when you want to test a chain which is ICS enabled. +## `ics` (optional) +The `ics` directive allows you to enable interchain security on the chain. This is useful when you want to test a chain which is ICS enabled ```yaml chains: - - id: neutron-1 - name: neutron - numValidators: 1 - ics: - enabled: true - provider: cosmoshub-4 # chain id of the provider chain - - id: cosmoshub-4 - name: cosmoshub - numValidators: 1 +- id: neutron-1 + name: neutron + numValidators: 1 + ics: + enabled: true + provider: cosmoshub-4 # chain id of the provider chain +- id: cosmoshub-4 + name: cosmoshub + numValidators: 1 ``` > Note: To complete the ICS setup one also needs to setup the relayer with ics enabled. Have a look at the [relayer](https://docs.cosmology.zone/starship/config/relayers#ics) -### `balances` (optional) +## `balances` (optional) The `balances` directive allows you to set the initial balances for the chain addresses. This is useful when you want to test a chain with specific initial balances. ```yaml chains: - - id: osmosis-1 - name: osmosis - numValidators: 1 - balances: - - address: cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5 - amount: 100000000000000uosmo - - address: cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5 - amount: 100000000000000uion +- id: osmosis-1 + name: osmosis + numValidators: 1 + balances: + - address: cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5 + amount: 100000000000000uosmo + - address: cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5 + amount: 100000000000000uion ``` -### `readinessProbe` (optional) +## `readinessProbe` (optional) The `readinessProbe` directive allows you to set the readiness probe for the chain pods. This is useful when you want to test a chain with specific readiness probe settings. -Note, this is the same as the [kubernetes readiness probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/). +Note, this is the same as the [kubernetes readiness probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) ```yaml chains: - - id: osmosis-1 - name: osmosis - numValidators: 1 - readinessProbe: - exec: - command: - - bash - - -c - - | - $CHAIN_BIN status 2>&1 | jq -e '.SyncInfo.catching_up == false' - initialDelaySeconds: 60 - periodSeconds: 20 +- id: osmosis-1 + name: osmosis + numValidators: 1 + readinessProbe: + exec: + command: + - bash + - -c + - | + $CHAIN_BIN status 2>&1 | jq -e '.SyncInfo.catching_up == false' + initialDelaySeconds: 60 + periodSeconds: 20 ``` -## Ethereum Chain Configuration in Starship +---------------------------------------- -Ethereum support in Starship allows users to deploy Ethereum execution and consensus (beacon) nodes using simple configurations. -This section details how to configure Ethereum-based chains within Starship. +# Relayers YAML Syntax -### Ethereum Execution Chain +We will go into details of the `relayers` top level directive in the Starship config file. -The Ethereum Execution Chain, or Ethereum client, runs the execution layer of the Ethereum network, handling smart contract execution and maintaining state. +> Note: By default the `relayers` will open up a `transfer` port + +## `name` +Name of the relayer, this is used as a unique identifier for the relayer. +```yaml +relayers: +- name: hermes-osmo-juno + ... +``` + +## `type` +Type of the relayer to spin up. Currently we support: +* [hermes](https://github.com/informalsystems/hermes) +* [ts-relayer](https://github.com/confio/ts-relayer) +* [go-relayer](https://github.com/cosmos/relayer) +* [neutron-query-relayer](https://github.com/neutron-org/neutron-query-relayer) + +```yaml +relayers: +- name: hermes-osmo-juno + type: hermes + ... +- name: ts-relayer-cosmos-juno + type: ts-relayer + ... +``` + +## `image` (optional) +If one wants to use a specific version of the relayer we support, +then it can be mentioned via this image directive. + +Supported version of relayers can be found: +* [`hermes` versions](https://github.com/hyperweb-io/starship/pkgs/container/starship%2Fhermes/versions?filters%5Bversion_type%5D=tagged) +* [`ts-relayer` versions](https://github.com/hyperweb-io/starship/pkgs/container/starship%2Fts-relayer/versions?filters%5Bversion_type%5D=tagged) +* [`go-relayer` versions](https://github.com/hyperweb-io/starship/pkgs/container/starship%2Fgo-relayer/versions?filters%5Bversion_type%5D=tagged) +* [`neutron-query-relayer` versions](https://github.com/hyperweb-io/starship/pkgs/container/starship%2Fneutron-query-relayer/versions?filters%5Bversion_type%5D=tagged) + +```yaml +relayers: +- name: hermes-osmo-juno + type: hermes + image: ghcr.io/hyperweb-io/starship/hermes:1.10.0 + ... +- name: ts-relayer-cosmos-juno + type: ts-relayer + image: ghcr.io/hyperweb-io/starship/ts-relayer:0.9.0 + ... +``` + +## `replicas` +Number of relayers to spin up. Currently we just support 1 replica for now. +```yaml +relayers: +- name: hermes-osmo-juno + type: hermes + replicas: 1 + ... +``` + +## `chains` +List of chains to connect to each other, via the Relayer. +Must be `id` of chains defined in the `chains` directive. -#### Config ```yaml chains: - - id: 1337 - name: ethereum - numValidators: 1 - ports: - rest: 8545 - rpc: 8551 +- name: osmosis-1 + ... +- name: juno-2 + ... +- name: gaia-4 + ... + +relayers: +- name: hermes-osmo-juno + type: hermes + replicas: 1 + chains: + - osmosis-1 + - juno-2 +- name: ts-relayer-osmo-gaia + type: ts-relayer + replicas: 1 + chains: + - osmosis-1 + - gaia-4 +``` + +## `config` (optional, hermes only) + +For `type: hermes` relayer, we support overriding the default config params with the `config` directive. +User specified overrides are performed at the start. + +> Note: Currently we only allow overrides of non chain specific values + +```yaml +relayers: +- name: hermes-osmo-juno + type: hermes + replicas: 1 + chains: + - osmosis-1 + - juno-1 + config: + global: + log_level: "error" + rest: + enabled: false + telemetry: + enabled: false + ## event_source is a custom configuration, in hermes is set of each of the chains + ## one just needs to mention the mode (push or pull), and we do the rest + ## For reference: https://github.com/informalsystems/hermes/releases/tag/v1.6.0 + event_source: + mode: "pull" # default is "push" +``` + +All allowed config params can be found in the [default values](https://github.com/hyperweb-io/starship/pull/185/files#diff-b6d755bc9ec9f0229fa0fc7beff17964f31889df81dbd5a16764ed8cdecbfa96R286-R310) +(Basically everything in [config.toml](https://github.com/informalsystems/hermes/blob/master/config.toml#L8...L111) file for the hermes relayer) + +## `ports` (optional, hermes only) +Ports directive in the `relayers` directive is used for `kubectl port-forward` forwarding kubernetes service ports to local host. +```yaml +relayers: +- name: hermes-osmo-juno + type: hermes + replicas: 1 + chains: + - osmosis-1 + - juno-1 + ports: + rest: 3001 + exposer: 3002 ``` -#### Optional Configs +Available endpoints on +* `rest` endpoints: https://hermes.informal.systems/documentation/rest-api.html?highlight=rest#rest-api +* `exposer` endpoint + * `/create_channel` endpoint: Supports post request. Proto definition: https://github.com/hyperweb-io/starship/blob/main/starship/proto/exposer/service.proto#L47 + +One can use the exposer endpoint on the relayer to create channel from outside. +Note: Still an experimental feature on exposer + +## `ics` (optional, hermes only) +ICS directive in the `relayers` directive is used for `hermes` relayer to specify the ICS setup to use + ```yaml chains: - - id: 1337 - name: ethereum - numValidators: 1 - ports: - rest: 8545 - rpc: 8551 - image: ghcr.io/hyperweb-io/starship/ethereum/client-go:latest - config: - beacon: - enabled: true - image: "ghcr.io/hyperweb-io/starship/prysm/beacon-chain:v5.2.0" - numValidator: 1 - validator: - enabled: true - image: "ghcr.io/hyperweb-io/starship/prysm/validator:v5.2.0" - numValidator: 1 - prysmctl: - image: "ghcr.io/hyperweb-io/starship/prysm/cmd/prysmctl:v5.2.0" +- id: neutron-1 + name: neutron + numValidators: 1 + ics: + enabled: true + provider: cosmoshub-4 # chain id of the provider chain +- id: cosmoshub-4 + name: cosmoshub + numValidators: 1 + +relayers: +- name: neutron-cosmos + type: hermes + replicas: 1 + chains: + - neutron-1 + - cosmoshub-4 + ics: + enabled: true + consumer: neutron-1 + provider: cosmoshub-4 ``` -## Feature Toggles +If `ics` is enabled, then the `consumer` and `provider` chain must be specified. With that, we automagically open the consumer and provider ports for the relayer. + +---------------------------------------- + +# Feature Toggles Starship allows you to have toggles for some additional services to run with your infra setup. A few of them are mentioned below. -### Registry +## Registry Following the schema of the `cosmos/chain-registry` repos, if enabled this service spins up a rest api chain-registry service for the infra you spin up. -#### Syntax +### Syntax + ```yaml registry: enabled: true # enable registry service @@ -507,29 +635,30 @@ registry: > Note: `registry.localhost` is set to true by default, meaning if the ports are specified in chains[].ports, then we set the various api endpoints of the chain registry to `localhost:`. Make sure `rest`, `grpc`, `rpc` are set as [chain ports](https://starship.cosmology.tech/config/chains#ports). If set to false, then it is set to kubernetes internal DNS endpoints. -#### Usage +### Usage Here is a list of available endpoints and how to use them: -| Endpoint | Returns | -|----------|---------| -| `/chain_ids` | List of all chain-ids in the current setup | -| `/chains` | List of all chain items | -| `/chains/{chain}` | Chain schema for the given chain ids (`name` in the `chains` directive) | -| `/chains/{chain}/assets` | Assets of the given chain | -| `/chains/{chain}/keys` | List of mnemonics used for the setup | -| `/ibc` | List all ibc info for all chains | -| `/ibc/{chain_1}/{chain_2}` | IBC information between the 2 chains specified | +| Endpoint | Returns +| ----------------------------- |---------------------------------------------------------------------------- +| `/chain_ids` | List of all chain-ids in the current setup +|`/chains` | List of all chain items +| `/chains/{chain}` | Chain schema for the given chain ids (`name` in the `chains` directive) +| `/chains/{chain}/assets` | Assets of the given chain +| `/chains/{chain}/keys` | List of mnemonics used for the setup +| `/ibc` | List all ibc info for all chains +| `/ibc/{chain_1}/{chain_2}` | IBC information between the 2 chains specified Proto definition for the service is [here](https://github.com/hyperweb-io/starship/blob/main/starship/proto/registry/service.proto) -### Explorer +## Explorer In order to provide a full fledged emulation environment, we have a handy toggle to spin up an explorer for the infra. -Currently we support only Ping Pub explorer, but we will be adding more. +Currently we support only Ping Pub explorer, but we will be adding more + +### Syntax -#### Syntax ```yaml explorer: enabled: true # enable registry service @@ -546,18 +675,20 @@ explorer: Available versions for the explorer can be found [here](https://github.com/hyperweb-io/starship/pkgs/container/starship%2Fexposer/versions?filters%5Bversion_type%5D=tagged) -#### Usage +### Usage After performing `port-forward`, open explorer at: http://localhost:8080 -### Ingress +## Ingress In order to get external traffic into Starship, one can use the `ingress` directive to create ingress rules on the domain. -> NOTE: Prerequisites include installing `cert-issuer` and `nginx-ingress` controller in the k8s cluster. -> Domain specified, needs to be pointing to the k8s cluster in which Starship is deployed. +> NOTE: Prerequists include installing `cert-issuer` and `nginx-ingress` controller in the k8s cluster. +Domain specified, needs to be pointing to the k8s cluster in which Starship is deployed +For Starship devnets, we use: [`cluster-setup.sh`](https://github.com/hyperweb-io/starship/blob/main/starship/scripts/cluster-setup.sh) +as the setup script. Need to run just once. -#### Syntax +### Syntax ```yaml ingress: enabled: true @@ -570,192 +701,92 @@ ingress: ``` Above will create following endpoints with the domain, and valid certs: -- Explorer at: `https://explorer.` (if enabled) -- Registry at: `https://registry.` (if enabled) -- Chains at: - - RPC endpoint: `https://rpc.-genesis.` - - Rest endpoint: `https://rest.-genesis.` - - Chain exposer: `https://rest.-genesis./exposer` - - Chain faucet: `https://rest.-genesis./faucet` -- Relayers at: - - Rest endpoint: `https://rest.-.` - - Exposer endpoint: `https://rest.-./exposer` - -## Relayers YAML Syntax - -We will go into details of the `relayers` top level directive in the Starship config file. - -> Note: By default the `relayers` will open up a `transfer` port. - -### `name` -Name of the relayer, this is used as a unique identifier for the relayer. - -```yaml -relayers: - - name: hermes-osmo-juno - ... -``` - -### `type` -Type of the relayer to spin up. Currently we support: -- [hermes](https://github.com/informalsystems/hermes) -- [ts-relayer](https://github.com/confio/ts-relayer) -- [go-relayer](https://github.com/cosmos/relayer) -- [neutron-query-relayer](https://github.com/neutron-org/neutron-query-relayer) - -```yaml -relayers: - - name: hermes-osmo-juno - type: hermes - ... - - name: ts-relayer-cosmos-juno - type: ts-relayer - ... -``` - -### `image` (optional) -If one wants to use a specific version of the relayer we support, -then it can be mentioned via this image directive. - -Supported version of relayers can be found: -- [`hermes` versions](https://github.com/hyperweb-io/starship/pkgs/container/starship%2Fhermes/versions?filters%5Bversion_type%5D=tagged) -- [`ts-relayer` versions](https://github.com/hyperweb-io/starship/pkgs/container/starship%2Fts-relayer/versions?filters%5Bversion_type%5D=tagged) -- [`go-relayer` versions](https://github.com/hyperweb-io/starship/pkgs/container/starship%2Fgo-relayer/versions?filters%5Bversion_type%5D=tagged) -- [`neutron-query-relayer` versions](https://github.com/hyperweb-io/starship/pkgs/container/starship%2Fneutron-query-relayer/versions?filters%5Bversion_type%5D=tagged) - -```yaml -relayers: - - name: hermes-osmo-juno - type: hermes - image: ghcr.io/hyperweb-io/starship/hermes:1.10.0 - ... - - name: ts-relayer-cosmos-juno - type: ts-relayer - image: ghcr.io/hyperweb-io/starship/ts-relayer:0.9.0 - ... -``` - -### `replicas` -Number of relayers to spin up. Currently we just support 1 replica for now. - -```yaml -relayers: - - name: hermes-osmo-juno - type: hermes +* Explorer at: `https://explorere.` (if enabled) +* Registry at: `https://registry.` (if enabled) +* Chains at: + * RPC endpoint: `https://rpc.-genesis.` + * Rest endpoint: `https://rest.-genesis.` + * Chain exposer: `https://rest.-genesis./exposer` + * Chain faucet: `https://rest.-genesis./faucet` +* Relayers at: + * Rest endpoint: `https://rest.-.` + * Exposer endpoint: `https://rest.-./exposer` + +## Frontends +Starship provides flexible frontend services that can be configured to run alongside your infrastructure. +Frontend services can be customized with different images, ports, and resource allocations. + +### Syntax + +```yaml +frontends: + - name: my-frontend + type: custom + image: ghcr.io/hyperweb-io/starship/frontend:latest replicas: 1 - ... + ports: + rest: 3000 + env: + API_URL: http://localhost:8080 + resources: # optional, defaults will be used + cpu: 0.5 + memory: 512Mi ``` -### `chains` -List of chains to connect to each other, via the Relayer. -Must be `id` of chains defined in the `chains` directive. +### Configuration Options -```yaml -chains: - - name: osmosis-1 - ... - - name: juno-2 - ... - - name: gaia-4 - ... +| Field | Type | Description | Default | +|-------|------|-------------|---------| +| `name` | string | Name of the frontend service | Required | +| `type` | string | Type of frontend service (e.g., custom) | Required | +| `image` | string | Docker image for the frontend service | Required | +| `replicas` | integer | Number of frontend service replicas | Required | +| `ports.rest` | integer | Port for HTTP/REST traffic | Required | +| `env` | object | Environment variables for the frontend service | {} | +| `resources` | object | Resource limits and requests | Not required | -relayers: - - name: hermes-osmo-juno - type: hermes - replicas: 1 - chains: - - osmosis-1 - - juno-2 - - name: ts-relayer-osmo-gaia - type: ts-relayer - replicas: 1 - chains: - - osmosis-1 - - gaia-4 -``` - -### `config` (optional, hermes only) +---------------------------------------- -For `type: hermes` relayer, we support overriding the default config params with the `config` directive. -User specified overrides are performed at the start. - -> Note: Currently we only allow overrides of non chain specific values +# Ethereum Chain Configuration in Starship -```yaml -relayers: - - name: hermes-osmo-juno - type: hermes - replicas: 1 - chains: - - osmosis-1 - - juno-1 - config: - global: - log_level: "error" - rest: - enabled: false - telemetry: - enabled: false - ## event_source is a custom configuration, in hermes is set of each of the chains - ## one just needs to mention the mode (push or pull), and we do the rest - ## For reference: https://github.com/informalsystems/hermes/releases/tag/v1.6.0 - event_source: - mode: "pull" # default is "push" -``` +Ethereum support in Starship allows users to deploy Ethereum execution and consensus (beacon) nodes using simple configurations. +This section details how to configure Ethereum-based chains within Starship. -All allowed config params can be found in the [default values](https://github.com/hyperweb-io/starship/pull/185/files#diff-b6d755bc9ec9f0229fa0fc7beff17964f31889df81dbd5a16764ed8cdecbfa96R286-R310) -(Basically everything in [config.toml](https://github.com/informalsystems/hermes/blob/master/config.toml#L8...L111) file for the hermes relayer) +## Ethereum Execution Chain -### `ports` (optional, hermes only) -Ports directive in the `relayers` directive is used for `kubectl port-forward` forwarding kubernetes service ports to local host. +The Ethereum Execution Chain, or Ethereum client, runs the execution layer of the Ethereum network, handling smart contract execution and maintaining state. +### Config ```yaml -relayers: - - name: hermes-osmo-juno - type: hermes - replicas: 1 - chains: - - osmosis-1 - - juno-1 +chains: + - id: 1337 + name: ethereum + numValidators: 1 ports: - rest: 3001 - exposer: 3002 + rest: 8545 + rpc: 8551 + ws: 8546 # optional ``` -Available endpoints on: -- `rest` endpoints: https://hermes.informal.systems/documentation/rest-api.html?highlight=rest#rest-api -- `exposer` endpoint - - `/create_channel` endpoint: Supports post request. Proto definition: https://github.com/hyperweb-io/starship/blob/main/starship/proto/exposer/service.proto#L47 - -One can use the exposer endpoint on the relayer to create channel from outside. -Note: Still an experimental feature on exposer - -### `ics` (optional, hermes only) -ICS directive in the `relayers` directive is used for `hermes` relayer to specify the ICS setup to use. - +### Optional Configs ```yaml chains: - - id: neutron-1 - name: neutron - numValidators: 1 - ics: - enabled: true - provider: cosmoshub-4 # chain id of the provider chain - - id: cosmoshub-4 - name: cosmoshub + - id: 1337 + name: ethereum numValidators: 1 - -relayers: - - name: neutron-cosmos - type: hermes - replicas: 1 - chains: - - neutron-1 - - cosmoshub-4 - ics: - enabled: true - consumer: neutron-1 - provider: cosmoshub-4 -``` - -If `ics` is enabled, then the `consumer` and `provider` chain must be specified. With that, we automatically open the consumer and provider ports for the relayer. \ No newline at end of file + ports: + rest: 8545 + rpc: 8551 + image: ghcr.io/hyperweb-io/starship/ethereum/client-go:latest + config: + beacon: + enabled: true + image: "ghcr.io/hyperweb-io/starship/prysm/beacon-chain:v5.2.0" + numValidator: 1 + validator: + enabled: true + image: "ghcr.io/hyperweb-io/starship/prysm/validator:v5.2.0" + numValidator: 1 + prysmctl: + image: "ghcr.io/hyperweb-io/starship/prysm/cmd/prysmctl:v5.2.0" +``` \ No newline at end of file diff --git a/src/starship/prompts/generate-starship-config.md b/src/starship/prompts/generate-starship-config.md new file mode 100644 index 0000000..c8b7d73 --- /dev/null +++ b/src/starship/prompts/generate-starship-config.md @@ -0,0 +1,67 @@ + +You are an AI assistant specialized in generating Starship configurations. You will help users create and modify Starship configurations based on their requirements while following the documentation and type definitions provided. + + + +Your task is to help generate or modify Starship configurations. Follow these steps systematically: + +1. Analyze the user's requirements carefully +2. Identify the necessary components (chains, relayers, explorer, registry) +3. Generate a valid YAML configuration following the schema +4. Validate the configuration against type definitions +5. Run verifyStarshipConfig to ensure the configuration is valid + +The configuration should be saved to 'starship/config.yaml' in project root unless the user specifies a different path. + +After generating the configuration: + +1. ALWAYS run verifyStarshipConfig to validate it +2. If verification fails, fix the issues and verify again +3. Only provide the configuration to the user after it passes verification + + + + +- Generate ONLY what is explicitly requested +- Don't add configurations that are not explicitly requested +- Ensure all required fields are provided +- Follow the exact type definitions +- Use default values unless specifically overridden +- Maintain compatibility between interconnected components +- Ensure port numbers don't conflict +- Keep resource requests reasonable for local development +- Don't include sensitive data in the configuration + + + +{{STARSHIP_CONFIG_DOCS}} + + + +{{STARSHIP_CONFIG_TYPES}} + + + +Before running verifyStarshipConfig, verify: +- [ ] All required fields are present +- [ ] Port numbers don't conflict +- [ ] Resource requests are appropriate +- [ ] Chain IDs are unique +- [ ] Dependencies between components are satisfied +- [ ] Scripts and paths exist +- [ ] Version compatibility is maintained + + + +When responding to a user request: +1. Confirm understanding of the requirements +2. Generate the YAML configuration +3. Run verifyStarshipConfig +4. If verification passes: + - Output the final configuration to the specified file (starship/config.yaml) + - Note any important considerations +5. If verification fails: + - Fix the issues + - Run verification again + - Output the corrected configuration to the specified file (starship/config.yaml) + diff --git a/src/starship/scripts/fetch-config-docs.ts b/src/starship/scripts/fetch-config-docs.ts new file mode 100644 index 0000000..e98bb3c --- /dev/null +++ b/src/starship/scripts/fetch-config-docs.ts @@ -0,0 +1,57 @@ +import fs from 'fs/promises'; +import path from 'path'; +import fetch from 'node-fetch'; +import { fileURLToPath } from 'url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +const BASE_URL = 'https://raw.githubusercontent.com/hyperweb-io/starship/main/docs/config'; +const OUTPUT_DIR = path.join(__dirname, '..', 'data'); +const OUTPUT_FILE = 'starship-config-docs.md'; +const DELIMITER = '\n\n----------------------------------------\n\n'; + +// Define files to fetch in desired order (index.mdx first) +const FILES_TO_FETCH = ['index.mdx', 'chains.mdx', 'relayers.mdx', 'features.mdx', 'ethereum.mdx']; + +async function fetchContent(fileName: string): Promise { + const url = `${BASE_URL}/${fileName}`; + const response = await fetch(url); + + if (!response.ok) { + throw new Error(`Failed to fetch ${fileName}: ${response.statusText}`); + } + + return response.text(); +} + +async function fetchAndSaveDocs() { + try { + // Ensure output directory exists + await fs.mkdir(OUTPUT_DIR, { recursive: true }); + + console.log('Fetching config docs...\n'); + + // Fetch all files + const contents = await Promise.all( + FILES_TO_FETCH.map(async (fileName) => { + const content = await fetchContent(fileName); + console.log(`Successfully fetched ${fileName}`); + return content.trim(); + }) + ); + + const combinedContent = contents.join(DELIMITER); + + // Save to output file + const outputPath = path.join(OUTPUT_DIR, OUTPUT_FILE); + await fs.writeFile(outputPath, combinedContent, 'utf-8'); + + console.log(`\nSaved combined config docs to ${outputPath}`); + } catch (error) { + console.error('Error processing config docs:', error); + process.exit(1); + } +} + +fetchAndSaveDocs(); diff --git a/src/starship/tools/starship-config-gen.test.ts b/src/starship/tools/starship-config-gen.test.ts deleted file mode 100644 index fb27670..0000000 --- a/src/starship/tools/starship-config-gen.test.ts +++ /dev/null @@ -1,763 +0,0 @@ -import { describe, expect, it } from "vitest"; -import { - type StarshipConfigInput, - generateStarshipYaml, -} from "./starship-config-gen.js"; // Adjust the import path as necessary - -describe("generateStarshipYaml", () => { - // Helper to normalize YAML for comparison (removes indentation inconsistencies) - const normalizeYaml = (yaml: string) => - yaml - .split("\n") - .map((line) => line.trim()) - .filter((line) => line) - .join("\n"); - - it("should generate a basic YAML config with chains only", () => { - const input: StarshipConfigInput = { - configFilePath: "/tmp/starship/config.yaml", - configName: "test-ship", - configVersion: "1.0.0", - chains: [ - { - id: "gaia-1", - name: "gaia", - numValidators: 1, - ports: { rest: 1317, rpc: 26657 }, - }, - ], - }; - - const expectedYaml = `name: test-ship -version: 1.0.0 -chains: - - id: gaia-1 - name: gaia - numValidators: 1 - ports: - rest: 1317 - rpc: 26657 -`; - const resultYaml = generateStarshipYaml(input); - expect(normalizeYaml(resultYaml)).toEqual(normalizeYaml(expectedYaml)); - }); - - it("should include relayers, explorer, and registry when provided", () => { - const input: StarshipConfigInput = { - configFilePath: "/tmp/starship/config.yaml", - configName: "full-ship", - configVersion: "1.5.0", - chains: [ - { - id: "osmosis-1", - name: "osmosis", - numValidators: 2, - ports: { rest: 1313, rpc: 26653 }, - }, - { - id: "gaia-1", - name: "gaia", - numValidators: 2, - ports: { rest: 1317, rpc: 26657 }, - }, - ], - relayers: [ - { - name: "osmo-gaia", - type: "hermes", - replicas: 1, - chains: ["osmosis-1", "gaia-1"], - }, - ], - explorer: { - enabled: true, - type: "ping-pub", // Added default type to fix original test - ports: { rest: 8080 }, - }, - registry: { - enabled: true, - localhost: true, // Added default value to fix original test - ports: { rest: 8081 }, - }, - }; - - const expectedYaml = `name: full-ship -version: 1.5.0 -chains: - - id: osmosis-1 - name: osmosis - numValidators: 2 - ports: - rest: 1313 - rpc: 26653 - - id: gaia-1 - name: gaia - numValidators: 2 - ports: - rest: 1317 - rpc: 26657 -relayers: - - name: osmo-gaia - type: hermes - replicas: 1 - chains: - - osmosis-1 - - gaia-1 -explorer: - enabled: true - type: ping-pub - ports: - rest: 8080 -registry: - enabled: true - localhost: true - ports: - rest: 8081 -`; - const resultYaml = generateStarshipYaml(input); - expect(normalizeYaml(resultYaml)).toEqual(normalizeYaml(expectedYaml)); - }); - - it("should handle missing optional fields gracefully", () => { - const inputWithDefaultsApplied: StarshipConfigInput = { - configFilePath: "/tmp/starship/config.yaml", - configName: "starship", - configVersion: "1.6.0", - chains: [ - { id: "juno-1", name: "juno", numValidators: 1 }, // No ports - ], - }; - - const expectedYaml = `name: starship -version: 1.6.0 -chains: - - id: juno-1 - name: juno - numValidators: 1 -`; - const resultYaml = generateStarshipYaml(inputWithDefaultsApplied); - expect(normalizeYaml(resultYaml)).toEqual(normalizeYaml(expectedYaml)); - }); - - it("should include chain-level optional fields like image, resources, faucet", () => { - const input: StarshipConfigInput = { - configFilePath: "/tmp/starship/config.yaml", - configName: "starship", - configVersion: "1.6.0", - chains: [ - { - id: "osmosis-custom", - name: "osmosis", - numValidators: 1, - image: "ghcr.io/hyperweb-io/starship/osmosis:v25.0.0", - resources: { cpu: "0.5", memory: "512M" }, - faucet: { - enabled: true, - type: "starship", - concurrency: 3, - }, - }, - ], - }; - - const expectedYaml = `name: starship -version: 1.6.0 -chains: - - id: osmosis-custom - name: osmosis - numValidators: 1 - image: ghcr.io/hyperweb-io/starship/osmosis:v25.0.0 - resources: - cpu: '0.5' - memory: 512M - faucet: - enabled: true - type: starship - concurrency: 3 -`; - const resultYaml = generateStarshipYaml(input); - expect(normalizeYaml(resultYaml)).toEqual(normalizeYaml(expectedYaml)); - }); - - it("should include custom chain configuration", () => { - const input: StarshipConfigInput = { - configFilePath: "/tmp/starship/config.yaml", - configName: "starship", - configVersion: "1.6.0", - chains: [ - { - id: "custom-chain-1", - name: "custom", - numValidators: 1, - image: "my/custom-image:latest", - home: "/root/.customd", - binary: "customd", - prefix: "custom", - denom: "ucustom", - coins: "1000000000ucustom", - hdPath: "m/44'/999'/0'/0/0", - coinType: 999, - repo: "https://github.com/my/custom-chain", - }, - ], - }; - - const expectedYaml = `name: starship -version: 1.6.0 -chains: - - id: custom-chain-1 - name: custom - numValidators: 1 - image: my/custom-image:latest - home: /root/.customd - binary: customd - prefix: custom - denom: ucustom - coins: 1000000000ucustom - hdPath: m/44'/999'/0'/0/0 - coinType: 999 - repo: https://github.com/my/custom-chain -`; - const resultYaml = generateStarshipYaml(input); - expect(normalizeYaml(resultYaml)).toEqual(normalizeYaml(expectedYaml)); - }); - - it("should include build configuration", () => { - const input: StarshipConfigInput = { - configFilePath: "/tmp/starship/config.yaml", - configName: "starship", - configVersion: "1.6.0", - chains: [ - { - id: "osmosis-build", - name: "osmosis", - numValidators: 1, - build: { - enabled: true, - source: "v15.0.0", - }, - // Need repo if using build - repo: "https://github.com/osmosis-labs/osmosis", - }, - ], - }; - const expectedYaml = `name: starship -version: 1.6.0 -chains: - - id: osmosis-build - name: osmosis - numValidators: 1 - repo: https://github.com/osmosis-labs/osmosis - build: - enabled: true - source: v15.0.0 -`; - const resultYaml = generateStarshipYaml(input); - expect(normalizeYaml(resultYaml)).toEqual(normalizeYaml(expectedYaml)); - }); - - it("should include upgrade configuration", () => { - const input: StarshipConfigInput = { - configFilePath: "/tmp/starship/config.yaml", - configName: "starship", - configVersion: "1.6.0", - chains: [ - { - id: "osmosis-upgrade", - name: "osmosis", - numValidators: 2, - upgrade: { - enabled: true, - type: "build", - genesis: "v13.0.0", - upgrades: [ - { name: "v14", version: "v14.0.0" }, - { name: "v15", version: "v15.0.0-rc1" }, - ], - }, - // Need repo if using upgrade type build - repo: "https://github.com/osmosis-labs/osmosis", - }, - ], - }; - const expectedYaml = `name: starship -version: 1.6.0 -chains: - - id: osmosis-upgrade - name: osmosis - numValidators: 2 - repo: https://github.com/osmosis-labs/osmosis - upgrade: - enabled: true - type: build - genesis: v13.0.0 - upgrades: - - name: v14 - version: v14.0.0 - - name: v15 - version: v15.0.0-rc1 -`; - const resultYaml = generateStarshipYaml(input); - expect(normalizeYaml(resultYaml)).toEqual(normalizeYaml(expectedYaml)); - }); - - it("should include genesis patching configuration", () => { - const input: StarshipConfigInput = { - configFilePath: "/tmp/starship/config.yaml", - configName: "starship", - configVersion: "1.6.0", - chains: [ - { - id: "osmosis-genesis", - name: "osmosis", - numValidators: 1, - genesis: { - app_state: { - staking: { - params: { - unbonding_time: "5s", - }, - }, - }, - }, - }, - ], - }; - const expectedYaml = `name: starship -version: 1.6.0 -chains: - - id: osmosis-genesis - name: osmosis - numValidators: 1 - genesis: - app_state: - staking: - params: - unbonding_time: 5s -`; - const resultYaml = generateStarshipYaml(input); - expect(normalizeYaml(resultYaml)).toEqual(normalizeYaml(expectedYaml)); - }); - - it("should include chain env variables", () => { - const input: StarshipConfigInput = { - configFilePath: "/tmp/starship/config.yaml", - configName: "starship", - configVersion: "1.6.0", - chains: [ - { - id: "debug-chain", - name: "gaia", - numValidators: 1, - env: [ - { name: "DEBUG", value: "SwingSet:vat,SwingSet:ls" }, - { name: "ANOTHER_VAR", value: "some_value" }, - ], - }, - ], - }; - const expectedYaml = `name: starship -version: 1.6.0 -chains: - - id: debug-chain - name: gaia - numValidators: 1 - env: - - name: DEBUG - value: SwingSet:vat,SwingSet:ls - - name: ANOTHER_VAR - value: some_value -`; - const resultYaml = generateStarshipYaml(input); - expect(normalizeYaml(resultYaml)).toEqual(normalizeYaml(expectedYaml)); - }); - - it("should include ICS configuration for chain and relayer", () => { - const input: StarshipConfigInput = { - configFilePath: "/tmp/starship/config.yaml", - configName: "starship", - configVersion: "1.6.0", - chains: [ - { - id: "neutron-1", - name: "neutron", - numValidators: 1, - ics: { - enabled: true, - provider: "cosmoshub-4", - }, - }, - { - id: "cosmoshub-4", - name: "cosmoshub", - numValidators: 1, - }, - ], - relayers: [ - { - name: "neutron-cosmos", - type: "hermes", - replicas: 1, - chains: ["neutron-1", "cosmoshub-4"], - ics: { - enabled: true, - consumer: "neutron-1", - provider: "cosmoshub-4", - }, - }, - ], - }; - const expectedYaml = `name: starship -version: 1.6.0 -chains: - - id: neutron-1 - name: neutron - numValidators: 1 - ics: - enabled: true - provider: cosmoshub-4 - - id: cosmoshub-4 - name: cosmoshub - numValidators: 1 -relayers: - - name: neutron-cosmos - type: hermes - replicas: 1 - chains: - - neutron-1 - - cosmoshub-4 - ics: - enabled: true - consumer: neutron-1 - provider: cosmoshub-4 -`; - const resultYaml = generateStarshipYaml(input); - expect(normalizeYaml(resultYaml)).toEqual(normalizeYaml(expectedYaml)); - }); - - it("should include custom balances configuration", () => { - const input: StarshipConfigInput = { - configFilePath: "/tmp/starship/config.yaml", - configName: "starship", - configVersion: "1.6.0", - chains: [ - { - id: "rich-chain", - name: "osmosis", - numValidators: 1, - balances: [ - { - address: "cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5", - amount: "100000000000000uosmo", - }, - { - address: "osmo1anotheraddress", // Different address example - amount: "5000000uion", - }, - ], - }, - ], - }; - const expectedYaml = `name: starship -version: 1.6.0 -chains: - - id: rich-chain - name: osmosis - numValidators: 1 - balances: - - address: cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5 - amount: 100000000000000uosmo - - address: osmo1anotheraddress - amount: 5000000uion -`; - const resultYaml = generateStarshipYaml(input); - expect(normalizeYaml(resultYaml)).toEqual(normalizeYaml(expectedYaml)); - }); - - it("should include Ethereum configuration", () => { - const input: StarshipConfigInput = { - configFilePath: "/tmp/starship/config.yaml", - configName: "starship", - configVersion: "1.6.0", - chains: [ - { - id: "1337", - name: "ethereum", - numValidators: 1, - ports: { rest: 8545, rpc: 8551 }, - config: { - beacon: { - enabled: true, - image: "ghcr.io/hyperweb-io/starship/prysm/beacon-chain:v5.2.0", - numValidator: 1, - }, - validator: { - enabled: true, - image: "ghcr.io/hyperweb-io/starship/prysm/validator:v5.2.0", - numValidator: 1, - }, - prysmctl: { - image: "ghcr.io/hyperweb-io/starship/prysm/cmd/prysmctl:v5.2.0", - }, - }, - }, - ], - }; - - const expectedYaml = `name: starship -version: 1.6.0 -chains: - - id: '1337' - name: ethereum - numValidators: 1 - ports: - rest: 8545 - rpc: 8551 - config: - beacon: - enabled: true - image: ghcr.io/hyperweb-io/starship/prysm/beacon-chain:v5.2.0 - numValidator: 1 - validator: - enabled: true - image: ghcr.io/hyperweb-io/starship/prysm/validator:v5.2.0 - numValidator: 1 - prysmctl: - image: ghcr.io/hyperweb-io/starship/prysm/cmd/prysmctl:v5.2.0 -`; - const resultYaml = generateStarshipYaml(input); - expect(normalizeYaml(resultYaml)).toEqual(normalizeYaml(expectedYaml)); - }); - - it("should include Hermes relayer specific config and ports", () => { - const input: StarshipConfigInput = { - configFilePath: "/tmp/starship/config.yaml", - configName: "starship", - configVersion: "1.6.0", - chains: [ - { id: "chain-a", name: "gaia", numValidators: 1 }, - { id: "chain-b", name: "osmosis", numValidators: 1 }, - ], - relayers: [ - { - name: "hermes-custom", - type: "hermes", - replicas: 1, - chains: ["chain-a", "chain-b"], - ports: { - rest: 3001, - exposer: 3002, - }, - config: { - global: { - log_level: "error", - }, - mode: { - clients: { - enabled: true, - }, - }, - event_source: { - mode: "pull", - }, - }, - }, - ], - }; - const expectedYaml = `name: starship -version: 1.6.0 -chains: - - id: chain-a - name: gaia - numValidators: 1 - - id: chain-b - name: osmosis - numValidators: 1 -relayers: - - name: hermes-custom - type: hermes - replicas: 1 - chains: - - chain-a - - chain-b - config: - global: - log_level: error - mode: - clients: - enabled: true - event_source: - mode: pull - ports: - rest: 3001 - exposer: 3002 -`; - const resultYaml = generateStarshipYaml(input); - expect(normalizeYaml(resultYaml)).toEqual(normalizeYaml(expectedYaml)); - }); - - it("should automatically resolve overlapping port conflicts by incrementing", () => { - // Input with overlapping port 1317 - const input: StarshipConfigInput = { - configFilePath: "/tmp/starship/config.yaml", - configName: "overlap-resolve-ship", - configVersion: "1.0.0", - chains: [ - { - id: "chain-a", - name: "gaia", - numValidators: 1, - ports: { rest: 1317, rpc: 26657 }, // Request 1317, 26657 - }, - { - id: "chain-b", - name: "osmosis", - numValidators: 1, - ports: { rest: 8080, rpc: 1317 }, // Request 8080, 1317 (conflict) - }, - ], - relayers: [ - { - name: "hermes-overlap", - type: "hermes", - replicas: 1, - chains: ["chain-a", "chain-b"], - ports: { rest: 1317, exposer: 9090 }, // Request 1317 (conflict), 9090 - }, - ], - explorer: { - enabled: true, - type: "ping-pub", - ports: { rest: 1317 }, // Request 1317 (conflict) - }, - registry: { - enabled: true, - localhost: true, - ports: { rest: 1317 }, // Request 1317 (conflict) - }, - }; - - // Expected output with resolved ports (1317 -> 1318, 1319, 1320, ...) - const expectedYaml = `name: overlap-resolve-ship -version: 1.0.0 -chains: - - id: chain-a - name: gaia - numValidators: 1 - ports: - rest: 1317 # Assigned first - rpc: 26657 # Assigned - - id: chain-b - name: osmosis - numValidators: 1 - ports: - rest: 8080 # Assigned - rpc: 1318 # Incremented from 1317 -relayers: - - name: hermes-overlap - type: hermes - replicas: 1 - chains: - - chain-a - - chain-b - ports: - rest: 1319 # Incremented from 1317 - exposer: 9090 # Assigned -explorer: - enabled: true - type: ping-pub - ports: - rest: 1320 # Incremented from 1317 -registry: - enabled: true - localhost: true - ports: - rest: 1321 # Incremented from 1317 -`; - const resultYaml = generateStarshipYaml(input); - // Normalize YAML to ignore comments and spacing differences - const normalizeYamlForTest = (yaml: string) => - yaml - .split("\n") - .map((line) => line.replace(/#.*$/, "").trim()) // Remove comments and trim - .filter((line) => line) - .join("\n"); - - expect(normalizeYamlForTest(resultYaml)).toEqual( - normalizeYamlForTest(expectedYaml), - ); - }); - - it("should resolve scattered overlapping ports correctly", () => { - // Input where port 1317 is requested again after other ports are assigned - const input: StarshipConfigInput = { - configFilePath: "/tmp/starship/config.yaml", - configName: "scattered-overlap-ship", - configVersion: "1.0.0", - chains: [ - { - id: "chain-a", - name: "gaia", - numValidators: 1, - ports: { rest: 1317, rpc: 26657 }, // Request 1317, 26657 - }, - { - id: "chain-b", - name: "osmosis", - numValidators: 1, - ports: { rest: 8080 }, // Request 8080 (no conflict yet) - }, - { - id: "chain-c", - name: "juno", - numValidators: 1, - ports: { rest: 1317 }, // Request 1317 again (conflict) - }, - ], - registry: { - enabled: true, - localhost: true, - ports: { rest: 8080 }, // Request 8080 again (conflict) - }, - }; - - // Expected output with resolved scattered ports - const expectedYaml = `name: scattered-overlap-ship -version: 1.0.0 -chains: - - id: chain-a - name: gaia - numValidators: 1 - ports: - rest: 1317 # Assigned first - rpc: 26657 # Assigned - - id: chain-b - name: osmosis - numValidators: 1 - ports: - rest: 8080 # Assigned - - id: chain-c - name: juno - numValidators: 1 - ports: - rest: 1318 # Incremented from 1317 -registry: - enabled: true - localhost: true - ports: - rest: 8081 # Incremented from 8080 -`; - const resultYaml = generateStarshipYaml(input); - // Normalize YAML to ignore comments and spacing differences - const normalizeYamlForTest = (yaml: string) => - yaml - .split("\n") - .map((line) => line.replace(/#.*$/, "").trim()) // Remove comments and trim - .filter((line) => line) - .join("\n"); - - expect(normalizeYamlForTest(resultYaml)).toEqual( - normalizeYamlForTest(expectedYaml), - ); - }); -}); diff --git a/src/starship/tools/starship-config-gen.ts b/src/starship/tools/starship-config-gen.ts index 13ea95f..84d38cf 100644 --- a/src/starship/tools/starship-config-gen.ts +++ b/src/starship/tools/starship-config-gen.ts @@ -1,1004 +1,76 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import YAML from "js-yaml"; -import { z } from "zod"; -import { writeFile } from "../../utils/file-system.js"; +import { readFileSync } from 'node:fs'; +import { dirname, join } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; -// --- Resource Schema --- -const resourceSchema = z - .object({ - cpu: z - .string() - .optional() - .describe("CPU resource allocation (e.g., '0.5', '1')"), - memory: z - .string() - .optional() - .describe("Memory resource allocation (e.g., '200M', '1Gi')"), - }) - .describe("Resource allocation for CPU and memory"); +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); -// --- Chain Ports Schema --- -const chainPortsSchema = z - .object({ - rest: z.number().int().optional().describe("REST port"), - rpc: z.number().int().optional().describe("RPC port"), - grpc: z.number().int().optional().describe("gRPC port"), - faucet: z.number().int().optional().describe("Faucet port"), - exposer: z.number().int().optional().describe("Exposer sidecar port"), - }) - .optional() - .describe("Port forwarding configuration for the chain's genesis node"); +// Define paths for documentation and type definitions +const DOCS_PATH = join(__dirname, '../data/starship-config-docs.md'); +const TYPES_PATH = join(__dirname, '../data/config-types.ts'); +const PROMPT_PATH = join(__dirname, '../prompts/generate-starship-config.md'); -// --- Chain Faucet Schema --- -const chainFaucetSchema = z - .object({ - enabled: z - .boolean() - .optional() - .default(true) - .describe("Whether to enable the faucet (default: true)"), - type: z - .enum(["cosmjs", "starship"]) - .optional() - .default("cosmjs") - .describe("Type of faucet to use (default: cosmjs)"), - image: z.string().optional().describe("Custom Docker image for the faucet"), - concurrency: z - .number() - .int() - .positive() - .optional() - .describe("Number of concurrent requests the faucet can handle"), - resources: resourceSchema.optional(), - }) - .optional() - .describe("Configuration for the chain's faucet service"); - -// --- Chain Build Schema --- -const chainBuildSchema = z - .object({ - enabled: z - .boolean() - .describe("Whether to enable building the chain binaries on the fly"), - source: z - .string() - .describe("Source to build from (tag, commit, or branch)"), - }) - .optional() - .describe("Configuration for building chain binaries during setup"); - -// --- Chain Upgrade Schema --- -const chainUpgradeSchema = z - .object({ - enabled: z.boolean().describe("Whether to enable upgrade preparation"), - type: z - .literal("build") - .describe("Type must be 'build' for building upgrade versions"), - genesis: z.string().describe("Current chain version (tag, branch, commit)"), - upgrades: z - .array( - z.object({ - name: z.string().describe("Upgrade proposal name (e.g., 'v14')"), - version: z - .string() - .describe("Upgrade chain version (tag, branch, commit)"), - }), - ) - .min(1) - .describe("List of upgrade versions to prepare"), - }) - .optional() - .describe( - "Configuration for preparing chain software upgrades using Cosmovisor", - ); - -// --- Chain Genesis Schema --- -const chainGenesisSchema = z - .object({ - app_state: z - .record(z.any()) - .optional() - .describe("Patches to apply to the 'app_state' section of genesis.json"), - // Add other top-level genesis fields if needed for patching - }) - .optional() - .describe("Patch configuration for the chain's genesis.json file"); - -// --- Chain Scripts Schema --- -const scriptEntrySchema = z.object({ - file: z.string().describe("Path to the script file, relative to config"), -}); -const chainScriptsSchema = z - .object({ - createGenesis: scriptEntrySchema.optional(), - updateGenesis: scriptEntrySchema.optional(), - updateConfig: scriptEntrySchema.optional(), - createValidator: scriptEntrySchema.optional(), - transferTokens: scriptEntrySchema.optional(), - buildChain: scriptEntrySchema.optional(), - }) - .optional() - .describe( - "Override default scripts used during chain setup (Requires using scripts/install.sh)", - ); - -// --- Chain CometMock Schema --- -const chainCometMockSchema = z - .object({ - enabled: z.boolean().describe("Whether to enable CometMock"), - image: z - .string() - .optional() - .describe("Custom Docker image for CometMock (e.g., version tag)"), - }) - .optional() - .describe("Configuration for running the chain with CometMock"); - -// --- Chain Env Var Schema --- -const chainEnvVarSchema = z - .object({ - name: z.string().describe("Name of the environment variable"), - value: z.string().describe("Value of the environment variable"), - }) - .describe("Environment variable definition"); - -// --- Chain ICS Schema --- -const chainIcsSchema = z - .object({ - enabled: z.boolean().describe("Whether to enable Interchain Security"), - provider: z - .string() - .describe("Chain ID of the ICS provider chain for this consumer"), - }) - .optional() - .describe( - "Interchain Security (ICS) configuration (for consumer chains only)", - ); - -// --- Chain Balance Schema --- -const chainBalanceSchema = z - .object({ - address: z.string().describe("Address to receive the balance"), - amount: z.string().describe("Amount and denom (e.g., '10000uosmo')"), - }) - .describe("Initial balance for a specific address"); - -// --- Chain Readiness Probe Schema --- -const chainReadinessProbeSchema = z - .object({ - exec: z - .object({ - command: z.array(z.string()).describe("Command array to execute"), - }) - .optional(), - // httpGet: ... // Add if needed - // tcpSocket: ... // Add if needed - initialDelaySeconds: z - .number() - .int() - .optional() - .describe("Delay before the first probe"), - periodSeconds: z - .number() - .int() - .optional() - .describe("How often to perform the probe"), - }) - .optional() - .describe("Custom readiness probe configuration for chain pods"); - -// --- Ethereum Specific Config (Optional within Chain) --- -const ethConfigSchema = z - .object({ - beacon: z - .object({ - enabled: z.boolean().optional().default(true), - image: z.string().optional(), - numValidator: z.number().int().positive().optional().default(1), - }) - .optional(), - validator: z - .object({ - enabled: z.boolean().optional().default(true), - image: z.string().optional(), - numValidator: z.number().int().positive().optional().default(1), - }) - .optional(), - prysmctl: z - .object({ - image: z.string().optional(), - }) - .optional(), - }) - .optional() - .describe( - "Ethereum-specific configuration (only applicable if name is 'ethereum')", - ); - -// --- Updated Chain Schema --- -const chainSchema = z.object({ - id: z.string().describe("Unique identifier for the chain (e.g., osmosis-1)"), - name: z - .string() - .describe( - "Type of chain (e.g., 'osmosis', 'cosmoshub') or 'custom' for full manual configuration", - ), - numValidators: z - .number() - .int() - .positive() - .describe("Number of validators for the chain (must be >= 1)"), - image: z - .string() - .optional() - .describe( - "Override default Docker image for the chain (e.g., 'ghcr.io/osmosis-labs/osmosis:v25.0.0')", - ), - home: z - .string() - .optional() - .describe("Home directory path (needed for name: 'custom')"), - binary: z - .string() - .optional() - .describe("Binary name (needed for name: 'custom')"), - prefix: z - .string() - .optional() - .describe("Address prefix (needed for name: 'custom')"), - denom: z - .string() - .optional() - .describe("Primary denomination (needed for name: 'custom')"), - coins: z - .string() - .optional() - .describe("Genesis coins (e.g., '1000uosmo,1000uion')"), - hdPath: z - .string() - .optional() - .describe("HD path (e.g., \"m/44'/118'/0'/0/0\")"), - coinType: z.number().int().optional().describe("Coin type (e.g., 118)"), - repo: z - .string() - .url() - .optional() - .describe( - "Git repository URL (needed for name: 'custom' or build/upgrade)", - ), - ports: chainPortsSchema, - resources: resourceSchema.optional(), - faucet: chainFaucetSchema, - build: chainBuildSchema, - upgrade: chainUpgradeSchema, - genesis: chainGenesisSchema, - scripts: chainScriptsSchema, - cometmock: chainCometMockSchema, - env: z - .array(chainEnvVarSchema) - .optional() - .describe("Custom environment variables for chain containers"), - ics: chainIcsSchema, - balances: z - .array(chainBalanceSchema) - .optional() - .describe("Set initial balances for specific addresses"), - readinessProbe: chainReadinessProbeSchema, - config: ethConfigSchema, // Add Ethereum config here -}); - -// --- Relayer Ports Schema (Hermes only) --- -const relayerPortsSchema = z - .object({ - rest: z.number().int().optional().describe("Hermes REST API port"), - exposer: z - .number() - .int() - .optional() - .describe("Relayer exposer service port"), - }) - .optional() - .describe("Port forwarding for the relayer (Hermes only)"); - -// --- Relayer Config Schema (Hermes only) --- -const relayerConfigSchema = z - .record(z.any()) - .optional() - .describe( - "Hermes relayer specific configuration overrides (refer to Hermes config.toml)", - ); - -// --- Relayer ICS Schema (Hermes only) --- -const relayerIcsSchema = z - .object({ - enabled: z - .boolean() - .describe("Whether to enable ICS setup for the relayer"), - consumer: z.string().describe("Chain ID of the ICS consumer chain"), - provider: z.string().describe("Chain ID of the ICS provider chain"), - }) - .optional() - .describe( - "Interchain Security (ICS) setup for the Hermes relayer connection", - ); - -// --- Updated Relayer Schema --- -const relayerSchema = z.object({ - name: z.string().describe("Name for the relayer instance (e.g., osmo-gaia)"), - type: z - .enum(["hermes", "ts-relayer", "go-relayer", "neutron-query-relayer"]) - .describe("Type of the relayer"), - image: z - .string() - .optional() - .describe("Override default Docker image for the relayer"), - replicas: z - .literal(1) - .optional() - .default(1) - .describe("Number of replicas (currently only 1 supported)"), - chains: z - .array(z.string()) - .min(2) - .describe("List of chain IDs to connect with this relayer"), - config: relayerConfigSchema, // Hermes specific - ports: relayerPortsSchema, // Hermes specific - ics: relayerIcsSchema, // Hermes specific -}); - -// --- Updated Explorer Schema --- -const explorerSchema = z - .object({ - enabled: z.boolean().describe("Whether to enable the explorer"), - type: z - .literal("ping-pub") - .optional() - .default("ping-pub") - .describe("Type of explorer (currently only 'ping-pub')"), - ports: z // Simplified ports object for explorer - .object({ - rest: z - .number() - .int() - .optional() - .describe("Local port for explorer UI"), - }) - .optional(), - resources: resourceSchema.optional(), - image: z - .string() - .optional() - .describe("Override default Docker image for the explorer"), - }) - .optional(); - -// --- Updated Registry Schema --- -const registrySchema = z - .object({ - enabled: z.boolean().describe("Whether to enable the registry service"), - localhost: z - .boolean() - .optional() - .default(true) - .describe( - "Set API endpoints to localhost using chain ports (default: true)", - ), - ports: z // Simplified ports object for registry - .object({ - rest: z - .number() - .int() - .optional() - .describe("Local port for registry API"), - }) - .optional(), - resources: resourceSchema.optional(), - image: z - .string() - .optional() - .describe("Override default Docker image for the registry service"), - }) - .optional(); - -// --- Define final input schema for GENERATION --- -const starshipConfigInputSchema = z.object({ - configFilePath: z - .string() - .describe( - "The absolute path to the Starship configuration file, default to '/starship/config.yaml'", - ), - configName: z - .string() - .optional() - .default("starship") - .describe("Top-level configuration name"), - configVersion: z - .string() - .optional() - .default("1.6.0") - .describe("Top-level configuration version"), - chains: z.array(chainSchema).min(1).describe("List of chain configurations"), - relayers: z - .array(relayerSchema) - .optional() - .describe("List of relayer configurations"), - explorer: explorerSchema.describe("Explorer configuration"), - registry: registrySchema.describe("Registry service configuration"), - // Add ingress schema if needed -}); - -export type StarshipConfigInput = z.infer; - -// --- Define input schema for VERIFICATION --- -const verifyStarshipConfigInputSchema = z.object({ - configFilePath: z - .string() - .describe( - "The absolute path to the Starship configuration file, default to '/starship/config.yaml'", - ), - yamlContent: z - .string() - .describe("The Starship configuration content in YAML format as a string."), -}); - -export type VerifyStarshipConfigInput = z.infer< - typeof verifyStarshipConfigInputSchema ->; - -// Consider creating more specific types if needed, but increases complexity -interface StarshipConfig { - name: string; - version: string; - chains: ChainOutput[]; // Use specific output type - relayers?: RelayerOutput[]; // Use specific output type - explorer?: ExplorerOutput; // Use specific output type - registry?: RegistryOutput; // Use specific output type -} - -// --- Output Type Definitions --- -interface PortConfig { - rest?: number; - rpc?: number; - grpc?: number; - faucet?: number; - exposer?: number; -} - -interface ResourceConfig { - cpu?: string; - memory?: string; -} - -interface FaucetConfig { - enabled?: boolean; - type?: "cosmjs" | "starship"; - image?: string; - concurrency?: number; - resources?: ResourceConfig; -} - -interface BuildConfig { - enabled: boolean; - source: string; -} - -interface UpgradeEntry { - name: string; - version: string; -} - -interface UpgradeConfig { - enabled: boolean; - type: "build"; - genesis: string; - upgrades: UpgradeEntry[]; -} - -interface GenesisConfig { - app_state?: Record; // Keep this flexible -} - -interface ScriptEntry { - file: string; -} -interface ScriptsConfig { - createGenesis?: ScriptEntry; - updateGenesis?: ScriptEntry; - updateConfig?: ScriptEntry; - createValidator?: ScriptEntry; - transferTokens?: ScriptEntry; - buildChain?: ScriptEntry; -} - -interface CometMockConfig { - enabled: boolean; - image?: string; -} - -interface EnvVar { - name: string; - value: string; -} - -interface IcsConfig { - enabled: boolean; - provider: string; -} - -interface BalanceEntry { - address: string; - amount: string; -} - -interface ReadinessProbeExec { - command: string[]; -} -interface ReadinessProbeConfig { - exec?: ReadinessProbeExec; - initialDelaySeconds?: number; - periodSeconds?: number; -} - -interface EthSubConfig { - enabled?: boolean; - image?: string; - numValidator?: number; -} - -interface EthConfigOutput { - beacon?: EthSubConfig; - validator?: EthSubConfig; - prysmctl?: { image?: string }; -} - -interface ChainOutput { - id: string; - name: string; - numValidators: number; - image?: string; - home?: string; - binary?: string; - prefix?: string; - denom?: string; - coins?: string; - hdPath?: string; - coinType?: number; - repo?: string; - ports?: PortConfig; - resources?: ResourceConfig; - faucet?: FaucetConfig; - build?: BuildConfig; - upgrade?: UpgradeConfig; - genesis?: GenesisConfig; - scripts?: ScriptsConfig; - cometmock?: CometMockConfig; - env?: EnvVar[]; - ics?: IcsConfig; - balances?: BalanceEntry[]; - readinessProbe?: ReadinessProbeConfig; - config?: EthConfigOutput; -} - -interface RelayerPortsConfig { - rest?: number; - exposer?: number; -} - -interface RelayerHermesConfig { - config?: Record; // Keep flexible - ports?: RelayerPortsConfig; - ics?: { enabled: boolean; consumer: string; provider: string }; -} - -interface RelayerOutput { - name: string; - type: "hermes" | "ts-relayer" | "go-relayer" | "neutron-query-relayer"; - image?: string; - replicas?: number; - chains: string[]; - config?: Record; // Hermes specific - ports?: RelayerPortsConfig; // Hermes specific, optional - ics?: { enabled: boolean; consumer: string; provider: string }; // Hermes specific -} - -interface ExplorerPortsConfig { - rest?: number; -} -interface ExplorerOutput { - enabled: boolean; - type?: "ping-pub"; - ports?: ExplorerPortsConfig; // Optional now - resources?: ResourceConfig; - image?: string; -} - -interface RegistryPortsConfig { - rest?: number; -} -interface RegistryOutput { - enabled: boolean; - localhost?: boolean; - ports?: RegistryPortsConfig; // Optional now - resources?: ResourceConfig; - image?: string; -} +const fallbackInstructions = ` + Please refer to the official documentation at: https://docs.hyperweb.io/starship/config +`; /** - * Generates the Starship configuration object based on input, automatically resolving port conflicts. - * @param input The validated configuration input. - * @returns The configuration object ready for YAML conversion. + * Reads and returns the content of a file + * @param path - Path to the file + * @returns Content of the file as string */ -function buildStarshipConfigObject(input: StarshipConfigInput): StarshipConfig { - const assignedPorts = new Set(); - const MAX_PORT_INCREMENT_ATTEMPTS = 100; - - /** - * Assigns a port, incrementing if the requested port is already taken. - * @param requestedPort The initially desired port number. - * @param assignedPorts The set of already assigned ports. - * @returns The assigned port number (either the original or an incremented one). - * @throws Error if an available port cannot be found within the attempt limit. - */ - function assignPort( - requestedPort: number | undefined, - assignedPorts: Set, - ): number | undefined { - if (requestedPort === undefined) { - return undefined; - } - - let currentPort = requestedPort; - let attempts = 0; - while (assignedPorts.has(currentPort)) { - if (attempts >= MAX_PORT_INCREMENT_ATTEMPTS) { - throw new Error( - `Could not find an available port for requested port ${requestedPort} after ${MAX_PORT_INCREMENT_ATTEMPTS} attempts.`, - ); - } - currentPort++; - attempts++; - } - assignedPorts.add(currentPort); - return currentPort; - } - - // Map chains, resolving port conflicts - const chains: ChainOutput[] = input.chains.map((chainInput) => { - const chainOutput: Partial = {}; // Use Partial for building - // Copy basic fields - chainOutput.id = chainInput.id; - chainOutput.name = chainInput.name; - chainOutput.numValidators = chainInput.numValidators; - if (chainInput.image) chainOutput.image = chainInput.image; - if (chainInput.home) chainOutput.home = chainInput.home; - if (chainInput.binary) chainOutput.binary = chainInput.binary; - if (chainInput.prefix) chainOutput.prefix = chainInput.prefix; - if (chainInput.denom) chainOutput.denom = chainInput.denom; - if (chainInput.coins) chainOutput.coins = chainInput.coins; - if (chainInput.hdPath) chainOutput.hdPath = chainInput.hdPath; - if (chainInput.coinType) chainOutput.coinType = chainInput.coinType; - if (chainInput.repo) chainOutput.repo = chainInput.repo; - - // Assign ports, resolving conflicts - const assignedChainPorts: Partial = {}; - if (chainInput.ports) { - if (chainInput.ports.rest !== undefined) { - assignedChainPorts.rest = assignPort( - chainInput.ports.rest, - assignedPorts, - ); - } - if (chainInput.ports.rpc !== undefined) { - assignedChainPorts.rpc = assignPort( - chainInput.ports.rpc, - assignedPorts, - ); - } - if (chainInput.ports.grpc !== undefined) { - assignedChainPorts.grpc = assignPort( - chainInput.ports.grpc, - assignedPorts, - ); - } - if (chainInput.ports.faucet !== undefined) { - assignedChainPorts.faucet = assignPort( - chainInput.ports.faucet, - assignedPorts, - ); - } - if (chainInput.ports.exposer !== undefined) { - assignedChainPorts.exposer = assignPort( - chainInput.ports.exposer, - assignedPorts, - ); - } - } - // Add ports object only if it contains assigned ports - if (Object.keys(assignedChainPorts).length > 0) { - chainOutput.ports = assignedChainPorts as PortConfig; - } - - // Copy other complex fields - if (chainInput.resources) chainOutput.resources = chainInput.resources; - if (chainInput.faucet) chainOutput.faucet = chainInput.faucet; - if (chainInput.build) chainOutput.build = chainInput.build; - if (chainInput.upgrade) chainOutput.upgrade = chainInput.upgrade; - if (chainInput.genesis) - chainOutput.genesis = chainInput.genesis as GenesisConfig; // Cast needed - if (chainInput.scripts) chainOutput.scripts = chainInput.scripts; - if (chainInput.cometmock) chainOutput.cometmock = chainInput.cometmock; - if (chainInput.env && chainInput.env.length > 0) - chainOutput.env = chainInput.env; - if (chainInput.ics) chainOutput.ics = chainInput.ics; - if (chainInput.balances && chainInput.balances.length > 0) - chainOutput.balances = chainInput.balances; - if (chainInput.readinessProbe) - chainOutput.readinessProbe = chainInput.readinessProbe; - if (chainInput.name === "ethereum" && chainInput.config) - chainOutput.config = chainInput.config; - - return chainOutput as ChainOutput; // Cast to final type - }); - - // Map relayers, resolving port conflicts - const relayers: RelayerOutput[] | undefined = input.relayers - ? input.relayers.map((relayerInput) => { - const relayerOutput: Partial = {}; // Use Partial - relayerOutput.name = relayerInput.name; - relayerOutput.type = relayerInput.type; - if (relayerInput.image) relayerOutput.image = relayerInput.image; - relayerOutput.replicas = relayerInput.replicas; - relayerOutput.chains = relayerInput.chains; - - // Hermes specific fields with port resolution - if (relayerInput.type === "hermes") { - if (relayerInput.config) - relayerOutput.config = relayerInput.config as Record< - string, - unknown - >; - const assignedRelayerPorts: Partial = {}; - if (relayerInput.ports) { - if (relayerInput.ports.rest !== undefined) { - assignedRelayerPorts.rest = assignPort( - relayerInput.ports.rest, - assignedPorts, - ); - } - if (relayerInput.ports.exposer !== undefined) { - assignedRelayerPorts.exposer = assignPort( - relayerInput.ports.exposer, - assignedPorts, - ); - } - } - // Add ports object only if it contains assigned ports - if (Object.keys(assignedRelayerPorts).length > 0) { - relayerOutput.ports = assignedRelayerPorts as RelayerPortsConfig; - } - if (relayerInput.ics) relayerOutput.ics = relayerInput.ics; - } - return relayerOutput as RelayerOutput; // Cast to final type - }) - : undefined; - - // Build explorer config if enabled, resolving port conflicts - let explorer: ExplorerOutput | undefined = undefined; - if (input.explorer?.enabled) { - const explorerOutput: Partial = { enabled: true }; // Use Partial - if (input.explorer.type) explorerOutput.type = input.explorer.type; - const assignedExplorerPorts: Partial = {}; - if (input.explorer.ports) { - if (input.explorer.ports.rest !== undefined) { - assignedExplorerPorts.rest = assignPort( - input.explorer.ports.rest, - assignedPorts, - ); - } - } - // Add ports object only if it contains assigned ports - if (Object.keys(assignedExplorerPorts).length > 0) { - explorerOutput.ports = assignedExplorerPorts as ExplorerPortsConfig; - } - - if (input.explorer.resources) - explorerOutput.resources = input.explorer.resources; - if (input.explorer.image) explorerOutput.image = input.explorer.image; - explorer = explorerOutput as ExplorerOutput; // Cast to final type - } - - // Build registry config if enabled, resolving port conflicts - let registry: RegistryOutput | undefined = undefined; - if (input.registry?.enabled) { - const registryOutput: Partial = { enabled: true }; // Use Partial - if (input.registry.localhost !== undefined) - registryOutput.localhost = input.registry.localhost; - const assignedRegistryPorts: Partial = {}; - if (input.registry.ports) { - if (input.registry.ports.rest !== undefined) { - assignedRegistryPorts.rest = assignPort( - input.registry.ports.rest, - assignedPorts, - ); - } - } - // Add ports object only if it contains assigned ports - if (Object.keys(assignedRegistryPorts).length > 0) { - registryOutput.ports = assignedRegistryPorts as RegistryPortsConfig; - } - - if (input.registry.resources) - registryOutput.resources = input.registry.resources; - if (input.registry.image) registryOutput.image = input.registry.image; - registry = registryOutput as RegistryOutput; // Cast to final type - } - - // Construct the final configuration object - const config: StarshipConfig = { - name: input.configName, - version: input.configVersion, - chains: chains, - }; - - // Add optional top-level fields if they exist and are not empty - if (relayers && relayers.length > 0) { - config.relayers = relayers; - } - if (explorer) { - config.explorer = explorer; +function readFileContent(path: string): string { + try { + return readFileSync(path, 'utf-8'); + } catch (error) { + console.error(`Error reading file ${path}:`, error); + return ''; } - if (registry) { - config.registry = registry; - } - - return config; } /** - * Takes validated Starship configuration input and returns a YAML string. - * @param input Validated Starship configuration input. - * @returns A string containing the Starship configuration in YAML format. - */ -export function generateStarshipYaml(input: StarshipConfigInput): string { - const configObject = buildStarshipConfigObject(input); - // Convert the JSON object to YAML format - // skipInvalid: true prevents YAML.dump from throwing on undefined/function values - const yamlConfig = YAML.dump(configObject, { skipInvalid: true }); - return yamlConfig; -} - -/** - * Registers the Starship config generation and verification tools with the MCP server. - * @param server The McpServer instance to register the tools with. + * Registers the Starship config generation tool with the MCP server. + * @param server The McpServer instance to register the tool with. */ export function registerStarshipConfigGenTool(server: McpServer): void { - // --- Generate Tool --- - server.tool( - "generateStarshipConfig", - "Generates a Starship configuration file in YAML format based on detailed input options then creates the file starship/config.yaml in the workspace root.", - starshipConfigInputSchema.shape, - async (input: StarshipConfigInput) => { - try { - const yamlConfig = generateStarshipYaml(input); - await writeFile(input.configFilePath, yamlConfig); - - return { - content: [ - { - type: "text", - text: `Starship configuration generated successfully:\n\n\`\`\`yaml\n${yamlConfig}\n\`\`\``, - }, - ], - }; - } catch (err: unknown) { - let errorMessage = `Error generating Starship configuration: ${String(err)}`; - if (err instanceof z.ZodError) { - errorMessage = `Input validation error during generation: ${err.errors - .map((e) => `${e.path.join(".")} - ${e.message}`) - .join("; ")}`; - console.error("Zod Validation Error (Generate):", err.flatten()); - } else if (err instanceof Error) { - errorMessage = `Error generating Starship configuration: ${err.message}`; - console.error("Generation Error:", err); - } else { - console.error("Unknown Generation Error:", err); - } - // Use MCP error format - return { - isError: true, - content: [ - { - type: "text", - text: errorMessage, - }, - ], - }; - } - }, - ); - - // --- Verify Tool --- server.tool( - "verifyStarshipConfig", - "Parses and validates a Starship configuration YAML string against the known schema.", - verifyStarshipConfigInputSchema.shape, // Use the verification schema shape - async (input: VerifyStarshipConfigInput) => { - if (!input.yamlContent) { - return { - content: [ - { - type: "text", - text: "Starship has not been initialized yet. Skipping configuration verification.", - }, - ], - }; - } - + 'generateStarshipConfig', + 'This tool helps generate Starship configuration files by providing comprehensive documentation and type definitions.', + async () => { try { - // 1. Parse the YAML string - // Use load instead of safeLoad as safeLoad is not exposed in type defs - const parsedConfig = YAML.load(input.yamlContent); - - // Basic check if parsing resulted in something usable - if ( - typeof parsedConfig !== "object" || - parsedConfig === null || - Array.isArray(parsedConfig) - ) { - throw new Error( - "Invalid YAML structure: Expected a top-level object.", - ); - } + // Read the base prompt template + let promptText = readFileContent(PROMPT_PATH); - // 2. Validate the parsed object against the Zod schema - const validationResult = - starshipConfigInputSchema.safeParse(parsedConfig); + // Read documentation and type definitions + const docsContent = readFileContent(DOCS_PATH); + const typesContent = readFileContent(TYPES_PATH); - if (validationResult.success) { - // Valid configuration - return { - content: [ - { - type: "text", - text: "Starship configuration is valid according to the schema.", - }, - ], - }; - } + // Replace placeholders in the prompt template + promptText = promptText + .replace('{{STARSHIP_CONFIG_DOCS}}', docsContent) + .replace('{{STARSHIP_CONFIG_TYPES}}', typesContent); - // Invalid configuration according to schema - const errorDetails = validationResult.error.errors - .map((e) => `- ${e.path.join(".") || "root"}: ${e.message}`) - .join("\n"); return { - isError: true, // Indicate it's a validation error, not a tool crash content: [ { - type: "text", - text: `Starship configuration is invalid:\n${errorDetails}`, + type: 'text', + text: promptText, }, ], }; - } catch (err: unknown) { - // Handle YAML parsing errors or other unexpected errors - let errorMessage = "Failed to verify Starship configuration."; - if (err instanceof Error) { - errorMessage = `Error verifying Starship configuration: ${err.message}`; - } else { - errorMessage = `An unknown error occurred during verification: ${String(err)}`; - } - console.error("Verification Error:", err); - + } catch (error) { + console.error('Error generating Starship configuration guide:', error); return { isError: true, content: [ { - type: "text", - text: errorMessage, + type: 'text', + text: fallbackInstructions, }, ], }; } - }, + } ); } diff --git a/src/starship/tools/starship-config-schema.ts b/src/starship/tools/starship-config-schema.ts new file mode 100644 index 0000000..2b2c553 --- /dev/null +++ b/src/starship/tools/starship-config-schema.ts @@ -0,0 +1,322 @@ +import { z } from 'zod'; + +// --- Resource Schema --- +const resourceSchema = z + .object({ + cpu: z.string().optional().describe("CPU resource allocation (e.g., '0.5', '1')"), + memory: z.string().optional().describe("Memory resource allocation (e.g., '200M', '1Gi')"), + }) + .describe('Resource allocation for CPU and memory'); + +// --- Chain Ports Schema --- +const chainPortsSchema = z + .object({ + rest: z.number().int().optional().describe('REST port'), + rpc: z.number().int().optional().describe('RPC port'), + grpc: z.number().int().optional().describe('gRPC port'), + faucet: z.number().int().optional().describe('Faucet port'), + exposer: z.number().int().optional().describe('Exposer sidecar port'), + }) + .optional() + .describe("Port forwarding configuration for the chain's genesis node"); + +// --- Chain Faucet Schema --- +const chainFaucetSchema = z + .object({ + enabled: z + .boolean() + .optional() + .default(true) + .describe('Whether to enable the faucet (default: true)'), + type: z + .enum(['cosmjs', 'starship']) + .optional() + .default('cosmjs') + .describe('Type of faucet to use (default: cosmjs)'), + image: z.string().optional().describe('Custom Docker image for the faucet'), + concurrency: z + .number() + .int() + .positive() + .optional() + .describe('Number of concurrent requests the faucet can handle'), + resources: resourceSchema.optional(), + }) + .optional() + .describe("Configuration for the chain's faucet service"); + +// --- Chain Build Schema --- +const chainBuildSchema = z + .object({ + enabled: z.boolean().describe('Whether to enable building the chain binaries on the fly'), + source: z.string().describe('Source to build from (tag, commit, or branch)'), + }) + .optional() + .describe('Configuration for building chain binaries during setup'); + +// --- Chain Upgrade Schema --- +const chainUpgradeSchema = z + .object({ + enabled: z.boolean().describe('Whether to enable upgrade preparation'), + type: z.literal('build').describe("Type must be 'build' for building upgrade versions"), + genesis: z.string().describe('Current chain version (tag, branch, commit)'), + upgrades: z + .array( + z.object({ + name: z.string().describe("Upgrade proposal name (e.g., 'v14')"), + version: z.string().describe('Upgrade chain version (tag, branch, commit)'), + }) + ) + .min(1) + .describe('List of upgrade versions to prepare'), + }) + .optional() + .describe('Configuration for preparing chain software upgrades using Cosmovisor'); + +// --- Chain Genesis Schema --- +const chainGenesisSchema = z + .object({ + app_state: z + .record(z.any()) + .optional() + .describe("Patches to apply to the 'app_state' section of genesis.json"), + // Add other top-level genesis fields if needed for patching + }) + .optional() + .describe("Patch configuration for the chain's genesis.json file"); + +// --- Chain Scripts Schema --- +const scriptEntrySchema = z.object({ + file: z.string().describe('Path to the script file, relative to config'), +}); +const chainScriptsSchema = z + .object({ + createGenesis: scriptEntrySchema.optional(), + updateGenesis: scriptEntrySchema.optional(), + updateConfig: scriptEntrySchema.optional(), + createValidator: scriptEntrySchema.optional(), + transferTokens: scriptEntrySchema.optional(), + buildChain: scriptEntrySchema.optional(), + }) + .optional() + .describe('Override default scripts used during chain setup (Requires using scripts/install.sh)'); + +// --- Chain CometMock Schema --- +const chainCometMockSchema = z + .object({ + enabled: z.boolean().describe('Whether to enable CometMock'), + image: z.string().optional().describe('Custom Docker image for CometMock (e.g., version tag)'), + }) + .optional() + .describe('Configuration for running the chain with CometMock'); + +// --- Chain Env Var Schema --- +const chainEnvVarSchema = z + .object({ + name: z.string().describe('Name of the environment variable'), + value: z.string().describe('Value of the environment variable'), + }) + .describe('Environment variable definition'); + +// --- Chain ICS Schema --- +const chainIcsSchema = z + .object({ + enabled: z.boolean().describe('Whether to enable Interchain Security'), + provider: z.string().describe('Chain ID of the ICS provider chain for this consumer'), + }) + .optional() + .describe('Interchain Security (ICS) configuration (for consumer chains only)'); + +// --- Chain Balance Schema --- +const chainBalanceSchema = z + .object({ + address: z.string().describe('Address to receive the balance'), + amount: z.string().describe("Amount and denom (e.g., '10000uosmo')"), + }) + .describe('Initial balance for a specific address'); + +// --- Chain Readiness Probe Schema --- +const chainReadinessProbeSchema = z + .object({ + exec: z + .object({ + command: z.array(z.string()).describe('Command array to execute'), + }) + .optional(), + // httpGet: ... // Add if needed + // tcpSocket: ... // Add if needed + initialDelaySeconds: z.number().int().optional().describe('Delay before the first probe'), + periodSeconds: z.number().int().optional().describe('How often to perform the probe'), + }) + .optional() + .describe('Custom readiness probe configuration for chain pods'); + +// --- Ethereum Specific Config (Optional within Chain) --- +const ethConfigSchema = z + .object({ + beacon: z + .object({ + enabled: z.boolean().optional().default(true), + image: z.string().optional(), + numValidator: z.number().int().positive().optional().default(1), + }) + .optional(), + validator: z + .object({ + enabled: z.boolean().optional().default(true), + image: z.string().optional(), + numValidator: z.number().int().positive().optional().default(1), + }) + .optional(), + prysmctl: z + .object({ + image: z.string().optional(), + }) + .optional(), + }) + .optional() + .describe("Ethereum-specific configuration (only applicable if name is 'ethereum')"); + +// --- Updated Chain Schema --- +const chainSchema = z.object({ + id: z.string().describe('Unique identifier for the chain (e.g., osmosis-1)'), + name: z + .string() + .describe( + "Type of chain (e.g., 'osmosis', 'cosmoshub') or 'custom' for full manual configuration" + ), + numValidators: z + .number() + .int() + .positive() + .describe('Number of validators for the chain (must be >= 1)'), + image: z + .string() + .optional() + .describe( + "Override default Docker image for the chain (e.g., 'ghcr.io/osmosis-labs/osmosis:v25.0.0')" + ), + home: z.string().optional().describe("Home directory path (needed for name: 'custom')"), + binary: z.string().optional().describe("Binary name (needed for name: 'custom')"), + prefix: z.string().optional().describe("Address prefix (needed for name: 'custom')"), + denom: z.string().optional().describe("Primary denomination (needed for name: 'custom')"), + coins: z.string().optional().describe("Genesis coins (e.g., '1000uosmo,1000uion')"), + hdPath: z.string().optional().describe("HD path (e.g., \"m/44'/118'/0'/0/0\")"), + coinType: z.number().int().optional().describe('Coin type (e.g., 118)'), + repo: z + .string() + .url() + .optional() + .describe("Git repository URL (needed for name: 'custom' or build/upgrade)"), + ports: chainPortsSchema, + resources: resourceSchema.optional(), + faucet: chainFaucetSchema, + build: chainBuildSchema, + upgrade: chainUpgradeSchema, + genesis: chainGenesisSchema, + scripts: chainScriptsSchema, + cometmock: chainCometMockSchema, + env: z + .array(chainEnvVarSchema) + .optional() + .describe('Custom environment variables for chain containers'), + ics: chainIcsSchema, + balances: z + .array(chainBalanceSchema) + .optional() + .describe('Set initial balances for specific addresses'), + readinessProbe: chainReadinessProbeSchema, + config: ethConfigSchema, // Add Ethereum config here +}); + +// --- Relayer Ports Schema (Hermes only) --- +const relayerPortsSchema = z + .object({ + rest: z.number().int().optional().describe('Hermes REST API port'), + exposer: z.number().int().optional().describe('Relayer exposer service port'), + }) + .optional() + .describe('Port forwarding for the relayer (Hermes only)'); + +// --- Relayer Config Schema (Hermes only) --- +const relayerConfigSchema = z + .record(z.any()) + .optional() + .describe('Hermes relayer specific configuration overrides (refer to Hermes config.toml)'); + +// --- Relayer ICS Schema (Hermes only) --- +const relayerIcsSchema = z + .object({ + enabled: z.boolean().describe('Whether to enable ICS setup for the relayer'), + consumer: z.string().describe('Chain ID of the ICS consumer chain'), + provider: z.string().describe('Chain ID of the ICS provider chain'), + }) + .optional() + .describe('Interchain Security (ICS) setup for the Hermes relayer connection'); + +// --- Updated Relayer Schema --- +const relayerSchema = z.object({ + name: z.string().describe('Name for the relayer instance (e.g., osmo-gaia)'), + type: z + .enum(['hermes', 'ts-relayer', 'go-relayer', 'neutron-query-relayer']) + .describe('Type of the relayer'), + image: z.string().optional().describe('Override default Docker image for the relayer'), + replicas: z + .literal(1) + .optional() + .default(1) + .describe('Number of replicas (currently only 1 supported)'), + chains: z.array(z.string()).min(2).describe('List of chain IDs to connect with this relayer'), + config: relayerConfigSchema, // Hermes specific + ports: relayerPortsSchema, // Hermes specific + ics: relayerIcsSchema, // Hermes specific +}); + +// --- Updated Explorer Schema --- +const explorerSchema = z + .object({ + enabled: z.boolean().describe('Whether to enable the explorer'), + type: z + .literal('ping-pub') + .optional() + .default('ping-pub') + .describe("Type of explorer (currently only 'ping-pub')"), + ports: z // Simplified ports object for explorer + .object({ + rest: z.number().int().optional().describe('Local port for explorer UI'), + }) + .optional(), + resources: resourceSchema.optional(), + image: z.string().optional().describe('Override default Docker image for the explorer'), + }) + .optional(); + +// --- Updated Registry Schema --- +const registrySchema = z + .object({ + enabled: z.boolean().describe('Whether to enable the registry service'), + localhost: z + .boolean() + .optional() + .default(true) + .describe('Set API endpoints to localhost using chain ports (default: true)'), + ports: z // Simplified ports object for registry + .object({ + rest: z.number().int().optional().describe('Local port for registry API'), + }) + .optional(), + resources: resourceSchema.optional(), + image: z.string().optional().describe('Override default Docker image for the registry service'), + }) + .optional(); + +// --- Define final input schema for GENERATION --- +export const starshipConfigSchema = z.object({ + name: z.string().optional().default('starship').describe('Top-level configuration name'), + version: z.string().optional().default('1.6.0').describe('Top-level configuration version'), + chains: z.array(chainSchema).min(1).describe('List of chain configurations'), + relayers: z.array(relayerSchema).optional().describe('List of relayer configurations'), + explorer: explorerSchema.describe('Explorer configuration'), + registry: registrySchema.describe('Registry service configuration'), + // Add ingress schema if needed +}); diff --git a/src/starship/tools/starship-config-verify.ts b/src/starship/tools/starship-config-verify.ts new file mode 100644 index 0000000..f2fda2a --- /dev/null +++ b/src/starship/tools/starship-config-verify.ts @@ -0,0 +1,95 @@ +import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; +import YAML from 'js-yaml'; +import { z } from 'zod'; +import { starshipConfigSchema } from './starship-config-schema.js'; + +/** + * Registers the Starship verification tool with the MCP server. + * @param server The McpServer instance to register the tool with. + */ +export function registerStarshipConfigVerifyTool(server: McpServer): void { + server.tool( + 'verifyStarshipConfig', + 'Parses and validates a Starship configuration YAML string against the known schema.', + { + yamlContent: z + .string() + .describe('The Starship configuration content in YAML format as a string.'), + }, + async (input) => { + if (!input.yamlContent) { + return { + content: [ + { + type: 'text', + text: 'Starship has not been initialized yet. Skipping configuration verification.', + }, + ], + }; + } + + try { + // 1. Parse the YAML string + // Use load instead of safeLoad as safeLoad is not exposed in type defs + const parsedConfig = YAML.load(input.yamlContent); + + // Basic check if parsing resulted in something usable + if ( + typeof parsedConfig !== 'object' || + parsedConfig === null || + Array.isArray(parsedConfig) + ) { + throw new Error('Invalid YAML structure: Expected a top-level object.'); + } + + // 2. Validate the parsed object against the Zod schema + const validationResult = starshipConfigSchema.safeParse(parsedConfig); + + if (validationResult.success) { + // Valid configuration + return { + content: [ + { + type: 'text', + text: 'Starship configuration is valid according to the schema.', + }, + ], + }; + } + + // Invalid configuration according to schema + const errorDetails = validationResult.error.errors + .map((e) => `- ${e.path.join('.') || 'root'}: ${e.message}`) + .join('\n'); + return { + isError: true, // Indicate it's a validation error, not a tool crash + content: [ + { + type: 'text', + text: `Starship configuration is invalid:\n${errorDetails}`, + }, + ], + }; + } catch (err: unknown) { + // Handle YAML parsing errors or other unexpected errors + let errorMessage = 'Failed to verify Starship configuration.'; + if (err instanceof Error) { + errorMessage = `Error verifying Starship configuration: ${err.message}`; + } else { + errorMessage = `An unknown error occurred during verification: ${String(err)}`; + } + console.error('Verification Error:', err); + + return { + isError: true, + content: [ + { + type: 'text', + text: errorMessage, + }, + ], + }; + } + } + ); +}