diff --git a/README.md b/README.md index 4e4df5e..52b6547 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ UTCP offers a unified framework for integrating disparate tools and services, ma * **Automatic Plugin Registration**: The official plugins are automatically discovered and registered when you import the core client—no manual setup required. For other plugins, you will need to register them manually. * **Scalability**: Designed to handle a large number of tools and providers without compromising performance. * **Extensibility**: A pluggable architecture allows developers to easily add new communication protocols, tool storage mechanisms, and search strategies without modifying the core library. -* **Interoperability**: With a growing ecosystem of protocol plugins—including HTTP, MCP, Text, and CLI—UTCP can integrate with almost any existing service or infrastructure. +* **Interoperability**: With a growing ecosystem of protocol plugins—including HTTP, MCP, Text, File, and CLI—UTCP can integrate with almost any existing service or infrastructure. * **Type Safety**: Built on well-defined TypeScript interfaces and runtime validation powered by Zod, making it robust and developer-friendly. * **Secure Variable Management**: Namespace-isolated variables prevent leakage between manuals, with support for environment variables and .env files. @@ -28,10 +28,13 @@ Install UTCP packages from npm: ```bash # Install core SDK and desired protocol plugins -npm install @utcp/sdk @utcp/http @utcp/mcp @utcp/text +npm install @utcp/sdk @utcp/http @utcp/mcp @utcp/text @utcp/file + +# Optional: Add dotenv variable loader for Node.js +npm install @utcp/dotenv-loader # Or using bun -bun add @utcp/sdk @utcp/http @utcp/mcp @utcp/text +bun add @utcp/sdk @utcp/http @utcp/mcp @utcp/text @utcp/file ``` ### For Development @@ -98,13 +101,13 @@ main().catch(console.error); import { UtcpClient } from '@utcp/sdk'; import { HttpCallTemplateSerializer } from '@utcp/http'; import { McpCallTemplateSerializer } from '@utcp/mcp'; -import { TextCallTemplateSerializer } from '@utcp/text'; +import { FileCallTemplateSerializer } from '@utcp/file'; async function main() { // Create serializers for each protocol const httpSerializer = new HttpCallTemplateSerializer(); const mcpSerializer = new McpCallTemplateSerializer(); - const textSerializer = new TextCallTemplateSerializer(); + const fileSerializer = new FileCallTemplateSerializer(); // Validate and create call templates const httpTemplate = httpSerializer.validateDict({ @@ -132,9 +135,9 @@ async function main() { } }); - const textTemplate = textSerializer.validateDict({ + const fileTemplate = fileSerializer.validateDict({ name: 'local_tools', - call_template_type: 'text', + call_template_type: 'file', file_path: './config/tools.json' }); @@ -152,7 +155,7 @@ async function main() { manual_call_templates: [ httpTemplate, // HTTP API mcpTemplate, // MCP Server - textTemplate // Local file-based tools + fileTemplate // Local file-based tools ] }); @@ -383,15 +386,34 @@ const mcpTemplate = serializer.validateDict({ ### Text Protocol -Load tools from local JSON/YAML files or OpenAPI specs: +Handle direct text/string content (browser-compatible): ```typescript import { TextCallTemplateSerializer } from '@utcp/text'; const serializer = new TextCallTemplateSerializer(); const textTemplate = serializer.validateDict({ - name: 'local_tools', + name: 'inline_tools', call_template_type: 'text', + content: JSON.stringify({ + tools: [ + // UTCP manual or OpenAPI spec as string + ] + }) +}); +``` + +### File Protocol + +Load tools from local JSON/YAML files or OpenAPI specs (Node.js only): + +```typescript +import { FileCallTemplateSerializer } from '@utcp/file'; + +const serializer = new FileCallTemplateSerializer(); +const fileTemplate = serializer.validateDict({ + name: 'local_tools', + call_template_type: 'file', file_path: './config/tools.json' // Supports: .json, .yaml, .yml, OpenAPI specs }); @@ -425,7 +447,10 @@ typescript-utcp/ │ ├── core/ # Core SDK with UtcpClient and interfaces │ ├── http/ # HTTP protocol plugin │ ├── mcp/ # MCP protocol plugin -│ ├── text/ # Text/file protocol plugin +│ ├── text/ # Text/string content protocol plugin (browser-compatible) +│ ├── file/ # File system protocol plugin (Node.js only) +│ ├── dotenv-loader/ # DotEnv variable loader plugin (Node.js only) +│ ├── direct-call/ # Direct call protocol plugin │ └── cli/ # CLI protocol plugin ├── tests/ # End-to-end integration tests └── README.md @@ -435,7 +460,10 @@ Each package is independently published to npm: - `@utcp/sdk` - Core SDK library (required) - `@utcp/http` - HTTP protocol support - `@utcp/mcp` - MCP protocol support -- `@utcp/text` - File-based tools +- `@utcp/text` - Direct text/string content (browser-compatible) +- `@utcp/file` - File system operations (Node.js only) +- `@utcp/dotenv-loader` - DotEnv variable loader (Node.js only) +- `@utcp/direct-call` - Direct function call protocol - `@utcp/cli` - Command-line tools ## Development & Testing diff --git a/bun.lock b/bun.lock index 857e8e8..273f7fa 100644 --- a/bun.lock +++ b/bun.lock @@ -17,7 +17,7 @@ }, "packages/cli": { "name": "@utcp/cli", - "version": "1.0.4", + "version": "1.0.6", "dependencies": { "@utcp/sdk": "^1.0.4", }, @@ -29,7 +29,7 @@ }, "packages/core": { "name": "@utcp/sdk", - "version": "1.0.5", + "version": "1.0.8", "dependencies": { "dotenv": "^17.2.1", "zod": "^3.23.8", @@ -41,7 +41,7 @@ }, "packages/direct-call": { "name": "@utcp/direct-call", - "version": "1.0.0", + "version": "1.0.1", "dependencies": { "@utcp/sdk": "^1.0.4", }, @@ -51,11 +51,40 @@ "typescript": "^5.0.0", }, }, + "packages/dotenv-loader": { + "name": "@utcp/dotenv-loader", + "version": "1.0.0", + "dependencies": { + "dotenv": "^17.2.1", + "zod": "^3.23.8", + }, + "devDependencies": { + "bun-types": "latest", + "typescript": "^5.0.0", + }, + "peerDependencies": { + "@utcp/sdk": "^1.0.8", + }, + }, + "packages/file": { + "name": "@utcp/file", + "version": "1.0.0", + "dependencies": { + "@utcp/http": "^1.0.7", + "@utcp/sdk": "^1.0.8", + "js-yaml": "^4.1.0", + }, + "devDependencies": { + "@types/bun": "latest", + "bun-types": "latest", + "typescript": "^5.0.0", + }, + }, "packages/http": { "name": "@utcp/http", - "version": "1.0.4", + "version": "1.0.8", "dependencies": { - "@utcp/sdk": "^1.0.4", + "@utcp/sdk": "^1.0.6", "axios": "^1.11.0", "js-yaml": "^4.1.0", }, @@ -67,7 +96,7 @@ }, "packages/mcp": { "name": "@utcp/mcp", - "version": "1.0.4", + "version": "1.0.6", "dependencies": { "@modelcontextprotocol/sdk": "^1.17.4", "@utcp/sdk": "^1.0.4", @@ -82,12 +111,13 @@ }, "packages/text": { "name": "@utcp/text", - "version": "1.0.4", + "version": "1.0.11", "dependencies": { - "@utcp/sdk": "^1.0.4", + "@utcp/http": "^1.0.8", + "@utcp/sdk": "^1.0.8", + "js-yaml": "^4.1.0", }, "devDependencies": { - "@types/bun": "latest", "bun-types": "latest", "typescript": "^5.0.0", }, @@ -229,6 +259,10 @@ "@utcp/direct-call": ["@utcp/direct-call@workspace:packages/direct-call"], + "@utcp/dotenv-loader": ["@utcp/dotenv-loader@workspace:packages/dotenv-loader"], + + "@utcp/file": ["@utcp/file@workspace:packages/file"], + "@utcp/http": ["@utcp/http@workspace:packages/http"], "@utcp/mcp": ["@utcp/mcp@workspace:packages/mcp"], @@ -591,13 +625,17 @@ "@utcp/direct-call/bun-types": ["bun-types@1.3.0", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-u8X0thhx+yJ0KmkxuEo9HAtdfgCBaM/aI9K90VQcQioAmkVp3SG3FkwWGibUFz3WdXAdcsqOcbU40lK7tbHdkQ=="], - "@utcp/http/bun-types": ["bun-types@1.3.0", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-u8X0thhx+yJ0KmkxuEo9HAtdfgCBaM/aI9K90VQcQioAmkVp3SG3FkwWGibUFz3WdXAdcsqOcbU40lK7tbHdkQ=="], + "@utcp/dotenv-loader/bun-types": ["bun-types@1.3.1", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-NMrcy7smratanWJ2mMXdpatalovtxVggkj11bScuWuiOoXTiKIu2eVS1/7qbyI/4yHedtsn175n4Sm4JcdHLXw=="], + + "@utcp/file/bun-types": ["bun-types@1.3.1", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-NMrcy7smratanWJ2mMXdpatalovtxVggkj11bScuWuiOoXTiKIu2eVS1/7qbyI/4yHedtsn175n4Sm4JcdHLXw=="], + + "@utcp/http/bun-types": ["bun-types@1.3.1", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-NMrcy7smratanWJ2mMXdpatalovtxVggkj11bScuWuiOoXTiKIu2eVS1/7qbyI/4yHedtsn175n4Sm4JcdHLXw=="], "@utcp/mcp/bun-types": ["bun-types@1.3.0", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-u8X0thhx+yJ0KmkxuEo9HAtdfgCBaM/aI9K90VQcQioAmkVp3SG3FkwWGibUFz3WdXAdcsqOcbU40lK7tbHdkQ=="], - "@utcp/sdk/bun-types": ["bun-types@1.3.0", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-u8X0thhx+yJ0KmkxuEo9HAtdfgCBaM/aI9K90VQcQioAmkVp3SG3FkwWGibUFz3WdXAdcsqOcbU40lK7tbHdkQ=="], + "@utcp/sdk/bun-types": ["bun-types@1.3.1", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-NMrcy7smratanWJ2mMXdpatalovtxVggkj11bScuWuiOoXTiKIu2eVS1/7qbyI/4yHedtsn175n4Sm4JcdHLXw=="], - "@utcp/text/bun-types": ["bun-types@1.3.0", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-u8X0thhx+yJ0KmkxuEo9HAtdfgCBaM/aI9K90VQcQioAmkVp3SG3FkwWGibUFz3WdXAdcsqOcbU40lK7tbHdkQ=="], + "@utcp/text/bun-types": ["bun-types@1.3.1", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-NMrcy7smratanWJ2mMXdpatalovtxVggkj11bScuWuiOoXTiKIu2eVS1/7qbyI/4yHedtsn175n4Sm4JcdHLXw=="], "accepts/mime-types": ["mime-types@3.0.1", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA=="], diff --git a/package-lock.json b/package-lock.json index 79d8f3e..48cb8a8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "@types/js-yaml": "^4.0.9", "bun-types": "^1.2.22", "nock": "^14.0.10", + "tsup": "^8.5.0", "zod": "^3.25.76" }, "peerDependencies": { @@ -19,6 +20,505 @@ "zod": "^3.23.8" } }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.11.tgz", + "integrity": "sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.11.tgz", + "integrity": "sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.11.tgz", + "integrity": "sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.11.tgz", + "integrity": "sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.11.tgz", + "integrity": "sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.11.tgz", + "integrity": "sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.11.tgz", + "integrity": "sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.11.tgz", + "integrity": "sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.11.tgz", + "integrity": "sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.11.tgz", + "integrity": "sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.11.tgz", + "integrity": "sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.11.tgz", + "integrity": "sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.11.tgz", + "integrity": "sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.11.tgz", + "integrity": "sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.11.tgz", + "integrity": "sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.11.tgz", + "integrity": "sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.11.tgz", + "integrity": "sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.11.tgz", + "integrity": "sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.11.tgz", + "integrity": "sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.11.tgz", + "integrity": "sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.11.tgz", + "integrity": "sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.11.tgz", + "integrity": "sha512-rOREuNIQgaiR+9QuNkbkxubbp8MSO9rONmwP5nKncnWJ9v5jQ4JxFnLu4zDSRPf3x4u+2VN4pM4RdyIzDty/wQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.11.tgz", + "integrity": "sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.11.tgz", + "integrity": "sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.11.tgz", + "integrity": "sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.11.tgz", + "integrity": "sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, "node_modules/@modelcontextprotocol/sdk": { "version": "1.17.4", "license": "MIT", @@ -75,6 +575,325 @@ "dev": true, "license": "MIT" }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.5.tgz", + "integrity": "sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.5.tgz", + "integrity": "sha512-mQGfsIEFcu21mvqkEKKu2dYmtuSZOBMmAl5CFlPGLY94Vlcm+zWApK7F/eocsNzp8tKmbeBP8yXyAbx0XHsFNA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.5.tgz", + "integrity": "sha512-takF3CR71mCAGA+v794QUZ0b6ZSrgJkArC+gUiG6LB6TQty9T0Mqh3m2ImRBOxS2IeYBo4lKWIieSvnEk2OQWA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.5.tgz", + "integrity": "sha512-W901Pla8Ya95WpxDn//VF9K9u2JbocwV/v75TE0YIHNTbhqUTv9w4VuQ9MaWlNOkkEfFwkdNhXgcLqPSmHy0fA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.5.tgz", + "integrity": "sha512-QofO7i7JycsYOWxe0GFqhLmF6l1TqBswJMvICnRUjqCx8b47MTo46W8AoeQwiokAx3zVryVnxtBMcGcnX12LvA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.5.tgz", + "integrity": "sha512-jr21b/99ew8ujZubPo9skbrItHEIE50WdV86cdSoRkKtmWa+DDr6fu2c/xyRT0F/WazZpam6kk7IHBerSL7LDQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.5.tgz", + "integrity": "sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.5.tgz", + "integrity": "sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.5.tgz", + "integrity": "sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.5.tgz", + "integrity": "sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.5.tgz", + "integrity": "sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.5.tgz", + "integrity": "sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.5.tgz", + "integrity": "sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.5.tgz", + "integrity": "sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.5.tgz", + "integrity": "sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.5.tgz", + "integrity": "sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.5.tgz", + "integrity": "sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.5.tgz", + "integrity": "sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.5.tgz", + "integrity": "sha512-w0cDWVR6MlTstla1cIfOGyl8+qb93FlAVutcor14Gf5Md5ap5ySfQ7R9S/NjNaMLSFdUnKGEasmVnu3lCMqB7w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.5.tgz", + "integrity": "sha512-Aufdpzp7DpOTULJCuvzqcItSGDH73pF3ko/f+ckJhxQyHtp67rHw3HMNxoIdDMUITJESNE6a8uh4Lo4SLouOUg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.5.tgz", + "integrity": "sha512-UGBUGPFp1vkj6p8wCRraqNhqwX/4kNQPS57BCFc8wYh0g94iVIW33wJtQAx3G7vrjjNtRaxiMUylM0ktp/TRSQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.5.tgz", + "integrity": "sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@types/bun": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@types/bun/-/bun-1.3.0.tgz", @@ -85,6 +904,13 @@ "bun-types": "1.3.0" } }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/js-yaml": { "version": "4.0.9", "dev": true, @@ -115,6 +941,14 @@ "resolved": "packages/direct-call", "link": true }, + "node_modules/@utcp/dotenv-loader": { + "resolved": "packages/dotenv-loader", + "link": true + }, + "node_modules/@utcp/file": { + "resolved": "packages/file", + "link": true + }, "node_modules/@utcp/http": { "resolved": "packages/http", "link": true @@ -159,6 +993,19 @@ "node": ">= 0.6" } }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/ajv": { "version": "6.12.6", "license": "MIT", @@ -173,6 +1020,39 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, + "license": "MIT" + }, "node_modules/argparse": { "version": "2.0.1", "license": "Python-2.0" @@ -182,7 +1062,9 @@ "license": "MIT" }, "node_modules/axios": { - "version": "1.11.0", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.12.2.tgz", + "integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==", "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", @@ -190,6 +1072,13 @@ "proxy-from-env": "^1.1.0" } }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, "node_modules/body-parser": { "version": "2.2.0", "license": "MIT", @@ -208,6 +1097,16 @@ "node": ">=18" } }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, "node_modules/bun-types": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/bun-types/-/bun-types-1.3.0.tgz", @@ -221,11 +1120,37 @@ "@types/react": "^19" } }, + "node_modules/bundle-require": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/bundle-require/-/bundle-require-5.1.0.tgz", + "integrity": "sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "load-tsconfig": "^0.2.3" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "peerDependencies": { + "esbuild": ">=0.18" + } + }, "node_modules/bytes": { "version": "3.1.2", "license": "MIT", "engines": { - "node": ">= 0.8" + "node": ">= 0.8" + } + }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" } }, "node_modules/call-bind-apply-helpers": { @@ -253,6 +1178,42 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, "node_modules/combined-stream": { "version": "1.0.8", "license": "MIT", @@ -263,6 +1224,33 @@ "node": ">= 0.8" } }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, "node_modules/content-disposition": { "version": "1.0.0", "license": "MIT", @@ -374,10 +1362,24 @@ "node": ">= 0.4" } }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, "node_modules/ee-first": { "version": "1.1.1", "license": "MIT" }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, "node_modules/encodeurl": { "version": "2.0.0", "license": "MIT", @@ -422,6 +1424,48 @@ "node": ">= 0.4" } }, + "node_modules/esbuild": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.11.tgz", + "integrity": "sha512-KohQwyzrKTQmhXDW1PjCv3Tyspn9n5GcY2RTDqeORIdIJY8yKIF7sTSopFmn/wpMPW4rdPXI0UE5LJLuq3bx0Q==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.11", + "@esbuild/android-arm": "0.25.11", + "@esbuild/android-arm64": "0.25.11", + "@esbuild/android-x64": "0.25.11", + "@esbuild/darwin-arm64": "0.25.11", + "@esbuild/darwin-x64": "0.25.11", + "@esbuild/freebsd-arm64": "0.25.11", + "@esbuild/freebsd-x64": "0.25.11", + "@esbuild/linux-arm": "0.25.11", + "@esbuild/linux-arm64": "0.25.11", + "@esbuild/linux-ia32": "0.25.11", + "@esbuild/linux-loong64": "0.25.11", + "@esbuild/linux-mips64el": "0.25.11", + "@esbuild/linux-ppc64": "0.25.11", + "@esbuild/linux-riscv64": "0.25.11", + "@esbuild/linux-s390x": "0.25.11", + "@esbuild/linux-x64": "0.25.11", + "@esbuild/netbsd-arm64": "0.25.11", + "@esbuild/netbsd-x64": "0.25.11", + "@esbuild/openbsd-arm64": "0.25.11", + "@esbuild/openbsd-x64": "0.25.11", + "@esbuild/openharmony-arm64": "0.25.11", + "@esbuild/sunos-x64": "0.25.11", + "@esbuild/win32-arm64": "0.25.11", + "@esbuild/win32-ia32": "0.25.11", + "@esbuild/win32-x64": "0.25.11" + } + }, "node_modules/escape-html": { "version": "1.0.3", "license": "MIT" @@ -528,6 +1572,24 @@ "version": "2.1.0", "license": "MIT" }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, "node_modules/finalhandler": { "version": "2.1.0", "license": "MIT", @@ -543,6 +1605,18 @@ "node": ">= 0.8" } }, + "node_modules/fix-dts-default-cjs-exports": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fix-dts-default-cjs-exports/-/fix-dts-default-cjs-exports-1.0.1.tgz", + "integrity": "sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "magic-string": "^0.30.17", + "mlly": "^1.7.4", + "rollup": "^4.34.8" + } + }, "node_modules/follow-redirects": { "version": "1.15.11", "funding": [ @@ -561,6 +1635,23 @@ } } }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/form-data": { "version": "4.0.4", "license": "MIT", @@ -589,6 +1680,21 @@ "node": ">= 0.8" } }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { "version": "1.1.2", "license": "MIT", @@ -629,6 +1735,27 @@ "node": ">= 0.4" } }, + "node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/gopd": { "version": "1.2.0", "license": "MIT", @@ -707,6 +1834,16 @@ "node": ">= 0.10" } }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/is-node-process": { "version": "1.2.0", "dev": true, @@ -720,6 +1857,32 @@ "version": "2.0.0", "license": "ISC" }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/joycon": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", + "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/js-yaml": { "version": "4.1.0", "license": "MIT", @@ -739,6 +1902,60 @@ "dev": true, "license": "ISC" }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/load-tsconfig": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/load-tsconfig/-/load-tsconfig-0.2.5.tgz", + "integrity": "sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/magic-string": { + "version": "0.30.19", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.19.tgz", + "integrity": "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "license": "MIT", @@ -780,10 +1997,61 @@ "node": ">= 0.6" } }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mlly": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.0.tgz", + "integrity": "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.15.0", + "pathe": "^2.0.3", + "pkg-types": "^1.3.1", + "ufo": "^1.6.1" + } + }, "node_modules/ms": { "version": "2.1.3", "license": "MIT" }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, "node_modules/negotiator": { "version": "1.0.0", "license": "MIT", @@ -843,6 +2111,13 @@ "dev": true, "license": "MIT" }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, "node_modules/parseurl": { "version": "1.3.3", "license": "MIT", @@ -857,6 +2132,23 @@ "node": ">=8" } }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/path-to-regexp": { "version": "8.2.0", "license": "MIT", @@ -864,6 +2156,43 @@ "node": ">=16" } }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/pkce-challenge": { "version": "5.0.0", "license": "MIT", @@ -871,6 +2200,61 @@ "node": ">=16.20.0" } }, + "node_modules/pkg-types": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" + } + }, + "node_modules/postcss-load-config": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", + "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.1.1" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "jiti": ">=1.21.0", + "postcss": ">=8.0.9", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, "node_modules/propagate": { "version": "2.0.1", "dev": true, @@ -918,20 +2302,86 @@ "version": "1.2.1", "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.0", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.6.3", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" } }, - "node_modules/raw-body": { - "version": "3.0.0", + "node_modules/rollup": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.5.tgz", + "integrity": "sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw==", + "dev": true, "license": "MIT", "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.6.3", - "unpipe": "1.0.0" + "@types/estree": "1.0.8" }, - "engines": { - "node": ">= 0.8" + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.52.5", + "@rollup/rollup-android-arm64": "4.52.5", + "@rollup/rollup-darwin-arm64": "4.52.5", + "@rollup/rollup-darwin-x64": "4.52.5", + "@rollup/rollup-freebsd-arm64": "4.52.5", + "@rollup/rollup-freebsd-x64": "4.52.5", + "@rollup/rollup-linux-arm-gnueabihf": "4.52.5", + "@rollup/rollup-linux-arm-musleabihf": "4.52.5", + "@rollup/rollup-linux-arm64-gnu": "4.52.5", + "@rollup/rollup-linux-arm64-musl": "4.52.5", + "@rollup/rollup-linux-loong64-gnu": "4.52.5", + "@rollup/rollup-linux-ppc64-gnu": "4.52.5", + "@rollup/rollup-linux-riscv64-gnu": "4.52.5", + "@rollup/rollup-linux-riscv64-musl": "4.52.5", + "@rollup/rollup-linux-s390x-gnu": "4.52.5", + "@rollup/rollup-linux-x64-gnu": "4.52.5", + "@rollup/rollup-linux-x64-musl": "4.52.5", + "@rollup/rollup-openharmony-arm64": "4.52.5", + "@rollup/rollup-win32-arm64-msvc": "4.52.5", + "@rollup/rollup-win32-ia32-msvc": "4.52.5", + "@rollup/rollup-win32-x64-gnu": "4.52.5", + "@rollup/rollup-win32-x64-msvc": "4.52.5", + "fsevents": "~2.3.2" } }, "node_modules/router": { @@ -1105,6 +2555,33 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/source-map": { + "version": "0.8.0-beta.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz", + "integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==", + "deprecated": "The work that was done in this beta branch won't be included in future versions", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "whatwg-url": "^7.0.0" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/statuses": { "version": "2.0.1", "license": "MIT", @@ -1117,6 +2594,180 @@ "dev": true, "license": "MIT" }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/sucrase": { + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", + "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "^10.3.10", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, "node_modules/toidentifier": { "version": "1.0.1", "license": "MIT", @@ -1124,6 +2775,86 @@ "node": ">=0.6" } }, + "node_modules/tr46": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", + "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "license": "MIT", + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/tsup": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/tsup/-/tsup-8.5.0.tgz", + "integrity": "sha512-VmBp77lWNQq6PfuMqCHD3xWl22vEoWsKajkF8t+yMBawlUS8JzEI+vOVMeuNZIuMML8qXRizFKi9oD5glKQVcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bundle-require": "^5.1.0", + "cac": "^6.7.14", + "chokidar": "^4.0.3", + "consola": "^3.4.0", + "debug": "^4.4.0", + "esbuild": "^0.25.0", + "fix-dts-default-cjs-exports": "^1.0.0", + "joycon": "^3.1.1", + "picocolors": "^1.1.1", + "postcss-load-config": "^6.0.1", + "resolve-from": "^5.0.0", + "rollup": "^4.34.8", + "source-map": "0.8.0-beta.0", + "sucrase": "^3.35.0", + "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.11", + "tree-kill": "^1.2.2" + }, + "bin": { + "tsup": "dist/cli-default.js", + "tsup-node": "dist/cli-node.js" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@microsoft/api-extractor": "^7.36.0", + "@swc/core": "^1", + "postcss": "^8.4.12", + "typescript": ">=4.5.0" + }, + "peerDependenciesMeta": { + "@microsoft/api-extractor": { + "optional": true + }, + "@swc/core": { + "optional": true + }, + "postcss": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, "node_modules/type-is": { "version": "2.0.1", "license": "MIT", @@ -1164,6 +2895,13 @@ "node": ">=14.17" } }, + "node_modules/ufo": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.1.tgz", + "integrity": "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==", + "dev": true, + "license": "MIT" + }, "node_modules/undici-types": { "version": "7.10.0", "dev": true, @@ -1190,6 +2928,25 @@ "node": ">= 0.8" } }, + "node_modules/webidl-conversions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", + "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + }, "node_modules/which": { "version": "2.0.2", "license": "ISC", @@ -1203,6 +2960,104 @@ "node": ">= 8" } }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/wrappy": { "version": "1.0.2", "license": "ISC" @@ -1223,10 +3078,10 @@ }, "packages/cli": { "name": "@utcp/cli", - "version": "1.0.4", + "version": "1.0.11", "license": "MPL-2.0", "dependencies": { - "@utcp/sdk": "^1.0.4" + "@utcp/sdk": "^1.0.11" }, "devDependencies": { "@types/bun": "latest", @@ -1236,7 +3091,7 @@ }, "packages/core": { "name": "@utcp/sdk", - "version": "1.0.4", + "version": "1.0.14", "license": "MPL-2.0", "dependencies": { "dotenv": "^17.2.1", @@ -1249,10 +3104,41 @@ }, "packages/direct-call": { "name": "@utcp/direct-call", + "version": "1.0.11", + "license": "MPL-2.0", + "dependencies": { + "@utcp/sdk": "^1.0.11" + }, + "devDependencies": { + "@types/bun": "latest", + "bun-types": "latest", + "typescript": "^5.0.0" + } + }, + "packages/dotenv-loader": { + "name": "@utcp/dotenv-loader", + "version": "1.0.0", + "license": "MPL-2.0", + "dependencies": { + "dotenv": "^17.2.1", + "zod": "^3.23.8" + }, + "devDependencies": { + "bun-types": "latest", + "typescript": "^5.0.0" + }, + "peerDependencies": { + "@utcp/sdk": "^1.0.8" + } + }, + "packages/file": { + "name": "@utcp/file", "version": "1.0.0", "license": "MPL-2.0", "dependencies": { - "@utcp/sdk": "^1.0.4" + "@utcp/http": "^1.0.7", + "@utcp/sdk": "^1.0.8", + "js-yaml": "^4.1.0" }, "devDependencies": { "@types/bun": "latest", @@ -1262,10 +3148,10 @@ }, "packages/http": { "name": "@utcp/http", - "version": "1.0.4", + "version": "1.0.12", "license": "MPL-2.0", "dependencies": { - "@utcp/sdk": "^1.0.4", + "@utcp/sdk": "^1.0.14", "axios": "^1.11.0", "js-yaml": "^4.1.0" }, @@ -1277,11 +3163,11 @@ }, "packages/mcp": { "name": "@utcp/mcp", - "version": "1.0.4", + "version": "1.0.11", "license": "MPL-2.0", "dependencies": { "@modelcontextprotocol/sdk": "^1.17.4", - "@utcp/sdk": "^1.0.4", + "@utcp/sdk": "^1.0.11", "axios": "^1.11.0" }, "devDependencies": { @@ -1293,13 +3179,14 @@ }, "packages/text": { "name": "@utcp/text", - "version": "1.0.4", + "version": "1.0.11", "license": "MPL-2.0", "dependencies": { - "@utcp/sdk": "^1.0.4" + "@utcp/http": "^1.0.11", + "@utcp/sdk": "^1.0.11", + "js-yaml": "^4.1.0" }, "devDependencies": { - "@types/bun": "latest", "bun-types": "latest", "typescript": "^5.0.0" } diff --git a/package.json b/package.json index c3829c2..b682b5f 100644 --- a/package.json +++ b/package.json @@ -18,13 +18,15 @@ }, "scripts": { "clean": "rm -rf packages/*/dist || rmdir /s /q packages\\*\\dist || true", - "build": "bun run build:core && bun run build:http && bun run build:mcp && bun run build:text && bun run build:cli && bun run build:direct-call", + "build": "bun run build:core && bun run build:http && bun run build:mcp && bun run build:text && bun run build:file && bun run build:cli && bun run build:direct-call && bun run build:dotenv-loader", "build:core": "cd packages/core && bun run build", "build:http": "cd packages/http && bun run build", "build:mcp": "cd packages/mcp && bun run build", "build:text": "cd packages/text && bun run build", + "build:file": "cd packages/file && bun run build", "build:cli": "cd packages/cli && bun run build", "build:direct-call": "cd packages/direct-call && bun run build", + "build:dotenv-loader": "cd packages/dotenv-loader && bun run build", "rebuild": "bun run clean && bun run build", "test": "bun test --config tsconfig.test.json", "version:patch": "node scripts/update-versions.js patch", @@ -33,11 +35,13 @@ "version:set": "node scripts/update-versions.js set", "publish:core": "cd packages/core && npm publish", "publish:text": "cd packages/text && npm publish", + "publish:file": "cd packages/file && npm publish", "publish:http": "cd packages/http && npm publish", "publish:mcp": "cd packages/mcp && npm publish", "publish:cli": "cd packages/cli && npm publish", "publish:direct-call": "cd packages/direct-call && npm publish", - "publish:all": "bun run build && bun run publish:core && bun run publish:text && bun run publish:http && bun run publish:mcp && bun run publish:cli && bun run publish:direct-call" + "publish:dotenv-loader": "cd packages/dotenv-loader && npm publish", + "publish:all": "bun run build && bun run publish:core && bun run publish:text && bun run publish:file && bun run publish:http && bun run publish:mcp && bun run publish:cli && bun run publish:direct-call && bun run publish:dotenv-loader" }, "workspaces": [ "packages/*" diff --git a/packages/cli/package.json b/packages/cli/package.json index 0835946..2bf8d83 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@utcp/cli", - "version": "1.0.6", + "version": "1.0.12", "description": "CLI utilities for UTCP", "main": "dist/index.cjs", "module": "dist/index.js", @@ -43,7 +43,7 @@ } }, "dependencies": { - "@utcp/sdk": "^1.0.6" + "@utcp/sdk": "^1.0.15" }, "devDependencies": { "bun-types": "latest", diff --git a/packages/core/README.md b/packages/core/README.md index a9113e8..8979c13 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -11,7 +11,7 @@ The `@utcp/sdk` package provides the fundamental components and interfaces for t ### Core Components * **UtcpClient**: The main client for interacting with the UTCP ecosystem - * Automatic plugin registration for official protocols (HTTP, MCP, Text, CLI) + * Plugin-based protocol support (HTTP, MCP, Text, File, CLI, etc.) * Manual and tool registration/deregistration * Tool search with name, tag, and description matching * Tool execution across multiple protocols @@ -47,14 +47,15 @@ bun add @utcp/sdk ### Basic Usage -Plugins are **automatically registered** when you import the UtcpClient: +**Important:** Plugins must be explicitly imported to register their protocols: ```typescript import { UtcpClient } from '@utcp/sdk'; +import '@utcp/http'; // Auto-registers HTTP protocol import { HttpCallTemplateSerializer } from '@utcp/http'; async function main() { - // Create client - official plugins auto-register + // Create client const serializer = new HttpCallTemplateSerializer(); const apiTemplate = serializer.validateDict({ name: 'api_manual', @@ -88,6 +89,7 @@ async function main() { ```typescript import { UtcpClient } from '@utcp/sdk'; +import '@utcp/dotenv-loader'; // Required for .env file support const client = await UtcpClient.create(process.cwd(), { load_variables_from: [ @@ -239,11 +241,47 @@ All lookups use the namespaced key: `{namespace}_VARIABLE_NAME` ## Plugin System -### Automatic Registration +### Available Plugins -Official plugins (HTTP, MCP, Text, CLI) are automatically registered when you import `UtcpClient`. The plugin loader in `@utcp/sdk` tries to discover and register available plugins. +UTCP provides several optional protocol plugins: -### Manual Registration +**Browser-Compatible:** +- `@utcp/http` - HTTP/HTTPS requests with full authentication support +- `@utcp/text` - Direct text/string content (inline UTCP manuals or OpenAPI specs) +- `@utcp/direct-call` - Direct function calls + +**Node.js Only:** +- `@utcp/file` - File system access for loading manuals from local files +- `@utcp/mcp` - Model Context Protocol support +- `@utcp/cli` - CLI command execution +- `@utcp/dotenv-loader` - Load variables from .env files + +### Explicit Plugin Import + +Each plugin must be **explicitly imported** to register its protocol: + +```typescript +// Browser application - only import browser-compatible plugins +import { UtcpClient } from '@utcp/sdk'; +import '@utcp/http'; +import '@utcp/text'; +import '@utcp/direct-call'; + +// Node.js application - can use all plugins +import { UtcpClient } from '@utcp/sdk'; +import '@utcp/http'; +import '@utcp/mcp'; +import '@utcp/file'; +import '@utcp/dotenv-loader'; +``` + +This explicit import approach ensures: +- ✅ Browser bundles only include what you use +- ✅ No Node.js dependencies in browser builds +- ✅ Perfect tree-shaking for minimal bundle sizes +- ✅ Clear, explicit dependencies + +### Custom Plugin Registration For custom or third-party plugins: diff --git a/packages/core/package.json b/packages/core/package.json index 7def058..41b0fa8 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@utcp/sdk", - "version": "1.0.6", + "version": "1.0.15", "description": "Universal Tool Calling Protocol SDK", "main": "dist/index.cjs", "module": "dist/index.js", diff --git a/packages/core/src/client/utcp_client.ts b/packages/core/src/client/utcp_client.ts index 7deeca1..60610e6 100644 --- a/packages/core/src/client/utcp_client.ts +++ b/packages/core/src/client/utcp_client.ts @@ -1,7 +1,4 @@ // packages/core/src/client/utcp_client.ts -import { promises as fs } from 'fs'; -import * as path from 'path'; -import { parse as parseDotEnv } from 'dotenv'; import { CallTemplate, CallTemplateSchema } from '../data/call_template'; import { Tool } from '../data/tool'; import { UtcpManualSchema } from '../data/utcp_manual'; @@ -14,6 +11,7 @@ import { ToolPostProcessor } from '../interfaces/tool_post_processor'; import { UtcpClientConfig, UtcpClientConfigSchema, + UtcpClientConfigSerializer, } from './utcp_client_config'; import { DefaultVariableSubstitutor } from '../implementations/default_variable_substitutor'; import { ensureCorePluginsInitialized } from '../plugins/plugin_loader'; @@ -60,16 +58,14 @@ export class UtcpClient implements IUtcpClient { */ public static async create( root_dir: string = process.cwd(), - config: Partial | string = {} + config: UtcpClientConfig | null = null ): Promise { // Ensure core plugins are initialized before parsing config ensureCorePluginsInitialized(); let loadedConfig: Partial; - if (typeof config === 'string') { - const configPath = path.resolve(root_dir, config); - const configFileContent = await fs.readFile(configPath, 'utf-8'); - loadedConfig = JSON.parse(configFileContent); + if (config === null) { + loadedConfig = new UtcpClientConfigSerializer().validateDict({}); } else { loadedConfig = config; } @@ -347,27 +343,6 @@ export class UtcpClient implements IUtcpClient { return result.data as T; } - /** - * Loads variables from sources defined in the client configuration. - */ - private async loadVariables(): Promise { - for (const varLoader of this.config.load_variables_from || []) { - const parsedLoader = varLoader; - if (parsedLoader.variable_loader_type === 'dotenv') { - try { - const envFilePath = path.resolve(this.root_dir || process.cwd(), parsedLoader.env_file_path); - const envContent = await fs.readFile(envFilePath, 'utf-8'); - const envVars = parseDotEnv(envContent); - // Merge loaded variables, giving precedence to existing config.variables - this.config.variables = { ...envVars, ...this.config.variables }; - console.log(`Loaded variables from .env file: ${envFilePath}`); - } catch (e: any) { - console.warn(`Could not load .env file from '${parsedLoader.env_file_path}': ${e.message}`); - } - } - } - } - /** * Closes the UTCP client and releases any resources held by its communication protocols. */ diff --git a/packages/core/src/implementations/default_variable_substitutor.ts b/packages/core/src/implementations/default_variable_substitutor.ts index ddc2a41..8775ce9 100644 --- a/packages/core/src/implementations/default_variable_substitutor.ts +++ b/packages/core/src/implementations/default_variable_substitutor.ts @@ -36,7 +36,7 @@ export class DefaultVariableSubstitutor implements VariableSubstitutor { // --- Search Hierarchy --- // 1. Check config.variables (highest precedence) - if (config.variables && config.variables[effectiveKey]) { + if (config.variables && effectiveKey in config.variables) { return config.variables[effectiveKey]; } @@ -77,6 +77,11 @@ export class DefaultVariableSubstitutor implements VariableSubstitutor { throw new Error(`Variable namespace '${namespace}' contains invalid characters. Only alphanumeric characters and underscores are allowed.`); } + const objString = JSON.stringify(obj); + if (objString && objString.includes('$ref')) { + return obj; + } + if (typeof obj === 'string') { let currentString: string = obj; const regex = /\$\{([a-zA-Z0-9_]+)\}|\$([a-zA-Z0-9_]+)/g; @@ -147,8 +152,9 @@ export class DefaultVariableSubstitutor implements VariableSubstitutor { const varNameInTemplate = match[1] || match[2]; // Apply Python SDK's double underscore namespacing: + // Replaces underscores in namespace with double underscores, then adds single underscore before variable const effectiveNamespace = namespace ? namespace.replace(/_/g, '__') : undefined; - const prefixedVarName = effectiveNamespace ? `${effectiveNamespace}__${varNameInTemplate}` : varNameInTemplate; + const prefixedVarName = effectiveNamespace ? `${effectiveNamespace}_${varNameInTemplate}` : varNameInTemplate; variables.push(prefixedVarName); } diff --git a/packages/core/src/implementations/in_mem_concurrent_tool_repository.ts b/packages/core/src/implementations/in_mem_concurrent_tool_repository.ts index 6d351fa..7a6b1d6 100644 --- a/packages/core/src/implementations/in_mem_concurrent_tool_repository.ts +++ b/packages/core/src/implementations/in_mem_concurrent_tool_repository.ts @@ -183,7 +183,9 @@ export class InMemConcurrentToolRepository implements ConcurrentToolRepository { export class InMemConcurrentToolRepositorySerializer extends Serializer { toDict(obj: InMemConcurrentToolRepository): { [key: string]: any } { - return obj.toDict(); + return { + tool_repository_type: obj.tool_repository_type + } } validateDict(data: { [key: string]: any }): InMemConcurrentToolRepository { diff --git a/packages/core/src/implementations/post_processors/filter_dict_post_processor.ts b/packages/core/src/implementations/post_processors/filter_dict_post_processor.ts index f3fa75e..1539036 100644 --- a/packages/core/src/implementations/post_processors/filter_dict_post_processor.ts +++ b/packages/core/src/implementations/post_processors/filter_dict_post_processor.ts @@ -186,7 +186,16 @@ type FilterDictPostProcessorConfig = z.infer { toDict(obj: FilterDictPostProcessor): { [key: string]: any } { - return obj.toDict(); + const filterDictConfig = obj.toDict() + return { + tool_post_processor_type: filterDictConfig.tool_post_processor_type, + exclude_keys: filterDictConfig.exclude_keys, + only_include_keys: filterDictConfig.only_include_keys, + exclude_tools: filterDictConfig.exclude_tools, + only_include_tools: filterDictConfig.only_include_tools, + exclude_manuals: filterDictConfig.exclude_manuals, + only_include_manuals: filterDictConfig.only_include_manuals, + }; } validateDict(data: { [key: string]: any }): FilterDictPostProcessor { diff --git a/packages/core/src/implementations/post_processors/limit_strings_post_processor.ts b/packages/core/src/implementations/post_processors/limit_strings_post_processor.ts index d8138b1..a4d9f31 100644 --- a/packages/core/src/implementations/post_processors/limit_strings_post_processor.ts +++ b/packages/core/src/implementations/post_processors/limit_strings_post_processor.ts @@ -124,7 +124,15 @@ type LimitStringsPostProcessorConfig = z.infer { toDict(obj: LimitStringsPostProcessor): { [key: string]: any } { - return obj.toDict(); + const limitStringsConfig = obj.toDict() + return { + tool_post_processor_type: limitStringsConfig.tool_post_processor_type, + limit: limitStringsConfig.limit, + exclude_tools: limitStringsConfig.exclude_tools, + only_include_tools: limitStringsConfig.only_include_tools, + exclude_manuals: limitStringsConfig.exclude_manuals, + only_include_manuals: limitStringsConfig.only_include_manuals, + }; } validateDict(data: { [key: string]: any }): LimitStringsPostProcessor { diff --git a/packages/core/src/implementations/tag_search_strategy.ts b/packages/core/src/implementations/tag_search_strategy.ts index c026af1..6d79325 100644 --- a/packages/core/src/implementations/tag_search_strategy.ts +++ b/packages/core/src/implementations/tag_search_strategy.ts @@ -11,8 +11,8 @@ import { Serializer } from '../interfaces/serializer'; */ export class TagSearchStrategy implements ToolSearchStrategy { public readonly tool_search_strategy_type: 'tag_and_description_word_match' = 'tag_and_description_word_match'; - private readonly descriptionWeight: number; - private readonly tagWeight: number; + public readonly descriptionWeight: number; + public readonly tagWeight: number; private readonly _config: TagSearchStrategyConfig; /** @@ -92,23 +92,52 @@ export class TagSearchStrategy implements ToolSearchStrategy { } const tagWords = new Set(tagLower.match(/\w+/g) || []); + + // Exact word matches for (const word of tagWords) { if (queryWords.has(word)) { score += this.tagWeight * 0.5; } } + + // Partial/substring word matches (e.g., "author" matches "authors") + for (const queryWord of queryWords) { + if (queryWord.length > 2) { + for (const tagWord of tagWords) { + if (tagWord.length > 2 && (tagWord.includes(queryWord) || queryWord.includes(tagWord))) { + score += this.tagWeight * 0.3; // Lower weight for partial matches in tags + break; // Only count once per query word + } + } + } + } } } if (tool.description) { + const descriptionLower = tool.description.toLowerCase(); const descriptionWords = new Set( - tool.description.toLowerCase().match(/\w+/g) || [] + descriptionLower.match(/\w+/g) || [] ); + + // Exact word matches for (const word of descriptionWords) { if (queryWords.has(word) && word.length > 2) { score += this.descriptionWeight; } } + + // Partial/substring word matches (e.g., "author" matches "authors") + for (const queryWord of queryWords) { + if (queryWord.length > 2) { + for (const descWord of descriptionWords) { + if (descWord.length > 2 && (descWord.includes(queryWord) || queryWord.includes(descWord))) { + score += this.descriptionWeight * 0.5; // Lower weight for partial matches + break; // Only count once per query word + } + } + } + } } return { tool, score }; @@ -116,7 +145,6 @@ export class TagSearchStrategy implements ToolSearchStrategy { const sortedTools = toolScores .sort((a, b) => b.score - a.score) - .filter(item => item.score > 0) .map(item => item.tool); return limit > 0 ? sortedTools.slice(0, limit) : sortedTools; @@ -126,7 +154,11 @@ export class TagSearchStrategy implements ToolSearchStrategy { export class TagSearchStrategyConfigSerializer extends Serializer { toDict(obj: TagSearchStrategy): { [key: string]: any } { - return { ...obj }; + return { + tool_search_strategy_type: obj.tool_search_strategy_type, + description_weight: obj.descriptionWeight, + tag_weight: obj.tagWeight + } } validateDict(data: { [key: string]: any }): TagSearchStrategy { diff --git a/packages/core/src/plugins/plugin_loader.ts b/packages/core/src/plugins/plugin_loader.ts index 6f479e8..7c5808c 100644 --- a/packages/core/src/plugins/plugin_loader.ts +++ b/packages/core/src/plugins/plugin_loader.ts @@ -5,10 +5,6 @@ import { BasicAuthSerializer } from '../data/auth_implementations/basic_auth'; import { OAuth2AuthSerializer } from '../data/auth_implementations/oauth2_auth'; import { setPluginInitializer } from '../interfaces/serializer'; -// Core Variable Loaders -import { VariableLoaderSerializer } from '../data/variable_loader'; -import { DotEnvVariableLoaderSerializer } from '../data/variable_loader_implementations/dotenv_variable_loader'; - // Core Tool Repository import { InMemConcurrentToolRepositorySerializer } from '../implementations/in_mem_concurrent_tool_repository'; import { ConcurrentToolRepositoryConfigSerializer } from '../interfaces/concurrent_tool_repository'; @@ -34,9 +30,6 @@ function _registerCorePlugins(): void { AuthSerializer.registerAuth('basic', new BasicAuthSerializer()); AuthSerializer.registerAuth('oauth2', new OAuth2AuthSerializer()); - // Register Core Variable Loader Serializers - VariableLoaderSerializer.registerVariableLoader('dotenv', new DotEnvVariableLoaderSerializer()); - // Register Tool Repository Serializers ConcurrentToolRepositoryConfigSerializer.registerRepository('in_memory', new InMemConcurrentToolRepositorySerializer()); @@ -49,72 +42,32 @@ function _registerCorePlugins(): void { } /** - * Attempts to auto-register optional UTCP plugins (HTTP, MCP, Text, Direct Call, CLI, etc.) - * if they are available in the project. This is a best-effort approach that - * silently ignores plugins that are not installed. - */ -function _tryRegisterOptionalPlugins(): void { - // Try to register HTTP plugin - try { - const httpPlugin = require('@utcp/http'); - if (httpPlugin && typeof httpPlugin.register === 'function') { - httpPlugin.register(); - } - } catch (e) { - // HTTP plugin not available, skip - } - - // Try to register MCP plugin - try { - const mcpPlugin = require('@utcp/mcp'); - if (mcpPlugin && typeof mcpPlugin.register === 'function') { - mcpPlugin.register(); - } - } catch (e) { - // MCP plugin not available, skip - } - - // Try to register Text plugin - try { - const textPlugin = require('@utcp/text'); - if (textPlugin && typeof textPlugin.register === 'function') { - textPlugin.register(); - } - } catch (e) { - // Text plugin not available, skip - } - - // Try to register Direct Call plugin - try { - const directCallPlugin = require('@utcp/direct-call'); - if (directCallPlugin && typeof directCallPlugin.register === 'function') { - directCallPlugin.register(); - } - } catch (e) { - // Direct Call plugin not available, skip - } - - // Try to register CLI plugin - try { - const cliPlugin = require('@utcp/cli'); - if (cliPlugin && typeof cliPlugin.register === 'function') { - cliPlugin.register(); - } - } catch (e) { - // CLI plugin not available, skip - } -} - -/** - * Ensures that all core UTCP plugins (default repository, search strategy, - * and post-processors) are registered with the plugin registry. - * This function should be called once at application startup. + * Ensures that all core UTCP plugins (auth serializers, default repository, + * search strategy, and post-processors) are registered with the plugin registry. + * + * This function is called automatically when needed and should not be called manually. + * + * Note: Optional plugins like HTTP, MCP, Text, File, etc. are NOT auto-registered. + * Users must explicitly import the plugins they need: + * + * @example + * // Browser application + * import { UtcpClient } from '@utcp/sdk'; + * import '@utcp/http'; // Auto-registers HTTP protocol + * import '@utcp/text'; // Auto-registers text content protocol + * + * @example + * // Node.js application + * import { UtcpClient } from '@utcp/sdk'; + * import '@utcp/http'; + * import '@utcp/mcp'; + * import '@utcp/file'; + * import '@utcp/dotenv-loader'; */ export function ensureCorePluginsInitialized(): void { if (!corePluginsInitialized && !initializing) { initializing = true; _registerCorePlugins(); - _tryRegisterOptionalPlugins(); corePluginsInitialized = true; initializing = false; } diff --git a/packages/core/src/utils/platform.ts b/packages/core/src/utils/platform.ts new file mode 100644 index 0000000..5ee52cd --- /dev/null +++ b/packages/core/src/utils/platform.ts @@ -0,0 +1,6 @@ +// Platform detection utility to determine if we're in Node.js or browser +export const isNode = typeof process !== 'undefined' && + process.versions != null && + process.versions.node != null; + +export const isBrowser = typeof window !== 'undefined' && typeof window.document !== 'undefined'; diff --git a/packages/direct-call/package.json b/packages/direct-call/package.json index cd0df55..8d30603 100644 --- a/packages/direct-call/package.json +++ b/packages/direct-call/package.json @@ -1,6 +1,6 @@ { "name": "@utcp/direct-call", - "version": "1.0.1", + "version": "1.0.12", "description": "Direct callable functions plugin for UTCP", "main": "dist/index.cjs", "module": "dist/index.js", @@ -44,7 +44,7 @@ } }, "dependencies": { - "@utcp/sdk": "^1.0.6" + "@utcp/sdk": "^1.0.15" }, "devDependencies": { "bun-types": "latest", diff --git a/packages/dotenv-loader/.gitignore b/packages/dotenv-loader/.gitignore new file mode 100644 index 0000000..62ccde4 --- /dev/null +++ b/packages/dotenv-loader/.gitignore @@ -0,0 +1,4 @@ +node_modules/ +dist/ +*.tsbuildinfo +.DS_Store diff --git a/packages/dotenv-loader/README.md b/packages/dotenv-loader/README.md new file mode 100644 index 0000000..e047b2d --- /dev/null +++ b/packages/dotenv-loader/README.md @@ -0,0 +1,64 @@ +# @utcp/dotenv-loader + +DotEnv Variable Loader plugin for the Universal Tool Calling Protocol (UTCP). + +## Overview + +This plugin provides support for loading environment variables from `.env` files in UTCP applications. It's useful for Node.js environments where you want to manage configuration through environment files. + +## Installation + +```bash +npm install @utcp/dotenv-loader +``` + +## Usage + +The plugin automatically registers itself when imported: + +```typescript +import '@utcp/dotenv-loader'; +import { UtcpClient } from '@utcp/sdk'; + +// Now you can use dotenv variable loaders in your UTCP configuration +const config = { + load_variables_from: [ + { + variable_loader_type: 'dotenv', + env_file_path: '.env' + } + ] +}; + +const client = await UtcpClient.create(process.cwd(), config); +``` + +## Configuration + +The DotEnv Variable Loader accepts the following configuration: + +- `variable_loader_type`: Must be set to `'dotenv'` +- `env_file_path`: Path to the `.env` file (relative to the root directory) + +## Features + +- **Automatic Registration**: Plugin registers itself on import +- **Dynamic Loading**: Reads .env file on every variable access +- **Silent Failure**: Returns `null` if file doesn't exist or can't be read +- **Standard .env Format**: Supports standard dotenv file format + +## Example .env File + +```env +API_KEY=your-api-key-here +DATABASE_URL=postgresql://localhost:5432/mydb +DEBUG=true +``` + +## Note + +This plugin requires Node.js as it uses the `fs` module. It's not suitable for browser environments. For browser applications, use inline variable configuration instead. + +## License + +MPL-2.0 diff --git a/packages/dotenv-loader/package.json b/packages/dotenv-loader/package.json new file mode 100644 index 0000000..d3b636d --- /dev/null +++ b/packages/dotenv-loader/package.json @@ -0,0 +1,53 @@ +{ + "name": "@utcp/dotenv-loader", + "version": "1.0.1", + "description": "DotEnv Variable Loader plugin for UTCP", + "main": "dist/index.cjs", + "module": "dist/index.js", + "types": "dist/index.d.ts", + "type": "module", + "license": "MPL-2.0", + "author": "UTCP Contributors", + "repository": { + "type": "git", + "url": "https://github.com/universal-tool-calling-protocol/typescript-utcp.git", + "directory": "packages/dotenv-loader" + }, + "keywords": [ + "utcp", + "dotenv", + "environment", + "variables", + "configuration" + ], + "publishConfig": { + "access": "public" + }, + "scripts": { + "prebuild": "rm -rf dist || rmdir /s /q dist || true", + "build": "tsup" + }, + "files": [ + "dist", + "README.md" + ], + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js", + "require": "./dist/index.cjs", + "default": "./dist/index.js" + } + }, + "dependencies": { + "dotenv": "^17.2.1", + "zod": "^3.23.8" + }, + "peerDependencies": { + "@utcp/sdk": "^1.0.15" + }, + "devDependencies": { + "bun-types": "latest", + "typescript": "^5.0.0" + } +} diff --git a/packages/core/src/data/variable_loader_implementations/dotenv_variable_loader.ts b/packages/dotenv-loader/src/dotenv_variable_loader.ts similarity index 92% rename from packages/core/src/data/variable_loader_implementations/dotenv_variable_loader.ts rename to packages/dotenv-loader/src/dotenv_variable_loader.ts index 984566b..edb9114 100644 --- a/packages/core/src/data/variable_loader_implementations/dotenv_variable_loader.ts +++ b/packages/dotenv-loader/src/dotenv_variable_loader.ts @@ -1,7 +1,6 @@ -// packages/core/src/data/variable_loader_implementations/dotenv_variable_loader.ts +// packages/dotenv-loader/src/dotenv_variable_loader.ts import { z } from 'zod'; -import { VariableLoader } from '../variable_loader'; -import { Serializer } from '../../interfaces/serializer'; +import { VariableLoader, Serializer } from '@utcp/sdk'; import { promises as fs } from 'fs'; import { parse as parseDotEnv } from 'dotenv'; import * as path from 'path'; diff --git a/packages/dotenv-loader/src/index.ts b/packages/dotenv-loader/src/index.ts new file mode 100644 index 0000000..ef76cbc --- /dev/null +++ b/packages/dotenv-loader/src/index.ts @@ -0,0 +1,26 @@ +/** + * DotEnv Variable Loader plugin for UTCP. + * Provides support for loading environment variables from .env files. + */ +// packages/dotenv-loader/src/index.ts +import { VariableLoaderSerializer, ensureCorePluginsInitialized } from '@utcp/sdk'; +import { DotEnvVariableLoaderSerializer } from './dotenv_variable_loader'; + +/** + * Registers the DotEnv Variable Loader with the UTCP plugin system. + * This function is called automatically when the package is imported. + * @param override Whether to override an existing registration + */ +export function register(override: boolean = false): void { + // Ensure core plugins are initialized first + ensureCorePluginsInitialized(); + + // Register the DotEnv Variable Loader serializer + VariableLoaderSerializer.registerVariableLoader('dotenv', new DotEnvVariableLoaderSerializer(), override); +} + +// Automatically register DotEnv loader plugin on import +register(); + +// Export all public APIs +export * from './dotenv_variable_loader'; diff --git a/packages/dotenv-loader/tsconfig.json b/packages/dotenv-loader/tsconfig.json new file mode 100644 index 0000000..29b79c5 --- /dev/null +++ b/packages/dotenv-loader/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "./dist", + "rootDir": "./src", + "composite": true + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist", "tests"], + "references": [ + { "path": "../core" } + ] +} diff --git a/packages/dotenv-loader/tsup.config.ts b/packages/dotenv-loader/tsup.config.ts new file mode 100644 index 0000000..3b36f94 --- /dev/null +++ b/packages/dotenv-loader/tsup.config.ts @@ -0,0 +1,17 @@ +import { defineConfig } from 'tsup'; + +export default defineConfig({ + entry: ['src/index.ts'], + format: ['esm', 'cjs'], + dts: { + compilerOptions: { + composite: false, + paths: {}, + }, + }, + splitting: false, + sourcemap: true, + clean: true, + outDir: 'dist', + external: ['@utcp/sdk', 'dotenv', 'zod'], +}); diff --git a/packages/file/README.md b/packages/file/README.md new file mode 100644 index 0000000..81dcd92 --- /dev/null +++ b/packages/file/README.md @@ -0,0 +1,137 @@ +# @utcp/file: File System Communication Protocol Plugin for UTCP + +The `@utcp/file` package provides a straightforward communication protocol for the Universal Tool Calling Protocol (UTCP) client to interact with local files. It's primarily used for loading static UTCP Manuals or OpenAPI specifications directly from local JSON or YAML files, without needing a network request. **Node.js only** - requires file system access. + +## Features + +* **File `CallTemplate`**: Defines the configuration for file-based tool definitions (`FileCallTemplate`), specifying the `file_path` to the local manual or spec. Authentication is explicitly `undefined` as file access typically relies on local permissions. +* **`FileCommunicationProtocol`**: Implements the `CommunicationProtocol` interface for file-based interactions: + * **Tool Discovery**: Reads and parses local JSON or YAML files. It can directly interpret UTCP Manuals or automatically convert OpenAPI (v2/v3) specifications into UTCP `Tool` definitions (by utilizing the `OpenApiConverter` from `@utcp/http`). + * **Tool Execution**: When a tool associated with a `FileCallTemplate` is "called", the protocol simply returns the raw content of the configured `file_path` as a string. This is useful for retrieving static data, configuration snippets, or even full documentation embedded as a tool. + * **Stateless**: This protocol does not maintain any persistent connections or external resources, making it very lightweight. + * **Path Resolution**: Resolves relative file paths using the `UtcpClient`'s configured root directory (`_rootPath`), ensuring flexibility in project structure. + +## Installation + +```bash +bun add @utcp/file @utcp/sdk + +# Or using npm +npm install @utcp/file @utcp/sdk +``` + +Note: `@utcp/sdk` is a peer dependency. `@utcp/http` and `js-yaml` dependencies are included automatically for OpenAPI conversion and YAML parsing. + +## Usage + +The File plugin registers automatically when you import it—no manual registration needed. Simply import from `@utcp/file` to enable file system support. + +```typescript +// From your application's entry point + +import { UtcpClient } from '@utcp/sdk'; +import { FileCallTemplateSerializer } from '@utcp/file'; +import * as path from 'path'; +import * as fs from 'fs/promises'; // For creating dummy files + +async function main() { + // Create a dummy UTCP manual file for demonstration + const manualContent = { + "utcp_version": "1.0.0", + "manual_version": "1.0.0", + "tools": [ + { + "name": "read_static_data", + "description": "Reads static data from a local file.", + "inputs": {}, + "outputs": { "type": "string", "description": "The content of the file." }, + "tags": ["file", "static"], + "tool_call_template": { + "name": "static_file_reader", + "call_template_type": "file", + "file_path": "./config/static_data.txt" // The file path for the tool's content + } + }, + { + "name": "describe_project", + "description": "Provides a description of the project from a local markdown file.", + "inputs": {}, + "outputs": { "type": "string" }, + "tags": ["documentation"], + "tool_call_template": { + "name": "project_readme_reader", + "call_template_type": "file", + "file_path": "./README.md" // Example: reads the project's README + } + } + ] + }; + const configDirPath = path.resolve(process.cwd(), './config'); + await fs.mkdir(configDirPath, { recursive: true }); + + const dummyManualPath = path.resolve(configDirPath, './my_local_manual.json'); + await fs.writeFile(dummyManualPath, JSON.stringify(manualContent, null, 2)); + + const staticDataPath = path.resolve(configDirPath, './static_data.txt'); + await fs.writeFile(staticDataPath, 'Hello from UTCP File Plugin static data!'); + + // Define a CallTemplate to load the local UTCP manual from the 'config' directory + const serializer = new FileCallTemplateSerializer(); + const fileCallTemplate = serializer.validateDict({ + name: 'local_manual_loader', + call_template_type: 'file', + file_path: './config/my_local_manual.json', // Path relative to client's root_path + }); + + const client = await UtcpClient.create(process.cwd(), { + manual_call_templates: [fileCallTemplate] // Register the file manual at client startup + }); + + console.log('File Plugin active. Searching for tools...'); + + // Example: Call 'read_static_data' tool. This will return the content of 'static_data.txt'. + try { + const staticDataReaderTool = await client.searchTools('read static data'); + if (staticDataReaderTool.length > 0) { + const result = await client.callTool(staticDataReaderTool.name, {}); + console.log('Result from "read_static_data" tool:', result); + } + } catch (error) { + console.error('Error calling "read_static_data" tool:', error); + } + + // Example: Call 'describe_project' tool. This will return the content of the project's README.md. + try { + const projectDescTool = await client.searchTools('project description'); + if (projectDescTool.length > 0) { + const result = await client.callTool(projectDescTool.name, {}); + console.log('Result from "describe_project" tool (first 100 chars):', String(result).substring(0, 100) + '...'); + } + } catch (error) { + console.error('Error calling "describe_project" tool:', error); + } finally { + // Clean up dummy files + await fs.unlink(dummyManualPath); + await fs.unlink(staticDataPath); + await fs.rmdir(configDirPath); // Remove the config directory + } + + await client.close(); // No-op for file protocol, but good practice +} + +main().catch(console.error); +``` + +## Comparison with @utcp/text + +| Feature | @utcp/file | @utcp/text | +|---------|------------|-----------| +| Browser compatible | ❌ No | ✅ Yes | +| Node.js compatible | ✅ Yes | ✅ Yes | +| File system access | ✅ Yes | ❌ No | +| Direct content | ❌ No | ✅ Yes | +| Use case | Server-side file reading | Web apps, inline content | + +## Development + +Refer to the root `README.md` for monorepo development and testing instructions. diff --git a/packages/file/package.json b/packages/file/package.json new file mode 100644 index 0000000..53f9b6f --- /dev/null +++ b/packages/file/package.json @@ -0,0 +1,55 @@ +{ + "name": "@utcp/file", + "version": "1.0.1", + "description": "File system protocol for UTCP - reads UTCP manuals from local files (Node.js only)", + "main": "dist/index.cjs", + "module": "dist/index.js", + "types": "dist/index.d.ts", + "type": "module", + "license": "MPL-2.0", + "author": "UTCP Contributors", + "repository": { + "type": "git", + "url": "https://github.com/universal-tool-calling-protocol/typescript-utcp.git", + "directory": "packages/file" + }, + "keywords": [ + "utcp", + "universal-tool-calling-protocol", + "tools", + "api", + "typescript", + "tool calling", + "file", + "agent", + "ai", + "llm" + ], + "publishConfig": { + "access": "public" + }, + "scripts": { + "build": "tsup" + }, + "files": [ + "dist" + ], + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js", + "require": "./dist/index.cjs", + "default": "./dist/index.js" + } + }, + "dependencies": { + "@utcp/sdk": "^1.0.15", + "@utcp/http": "^1.0.13", + "js-yaml": "^4.1.0" + }, + "devDependencies": { + "bun-types": "latest", + "typescript": "^5.0.0", + "@types/bun": "latest" + } +} diff --git a/packages/file/src/file_call_template.ts b/packages/file/src/file_call_template.ts new file mode 100644 index 0000000..ed8e0dc --- /dev/null +++ b/packages/file/src/file_call_template.ts @@ -0,0 +1,75 @@ +// packages/file/src/file_call_template.ts +import { z } from 'zod'; +import { CallTemplate } from '@utcp/sdk'; +import { Auth, AuthSchema, AuthSerializer } from '@utcp/sdk'; +import { Serializer } from '@utcp/sdk'; + +/** + * REQUIRED + * Call template for file-based manuals and tools. + * + * Reads UTCP manuals or tool definitions from local JSON/YAML files. + * Useful for static tool configurations or environments where manuals are distributed as files. + * For direct text content, use @utcp/text instead. + * + * Attributes: + * call_template_type: Always "file" for file call templates. + * file_path: Path to the file containing the UTCP manual or tool definitions (required). + * auth: Always undefined - file call templates don't support authentication for file access. + * auth_tools: Optional authentication to apply to generated tools from OpenAPI specs. + */ +export interface FileCallTemplate extends CallTemplate { + call_template_type: 'file'; + file_path: string; + auth?: undefined; + auth_tools?: Auth | null; +} + +/** + * Zod schema for FileCallTemplate. + */ +export const FileCallTemplateSchema: z.ZodType = z.object({ + name: z.string().optional(), + call_template_type: z.literal('file'), + file_path: z.string().describe('The path to the file containing the UTCP manual or tool definitions.'), + auth: z.undefined().optional(), + auth_tools: AuthSchema.nullable().optional().transform((val) => { + if (val === null || val === undefined) return null; + if (typeof val === 'object' && 'auth_type' in val) { + return new AuthSerializer().validateDict(val as any); + } + return val as Auth; + }).describe('Authentication to apply to generated tools from OpenAPI specs.'), +}).strict() as z.ZodType; + +/** + * REQUIRED + * Serializer for FileCallTemplate. + */ +export class FileCallTemplateSerializer extends Serializer { + /** + * REQUIRED + * Convert a FileCallTemplate to a dictionary. + */ + toDict(obj: FileCallTemplate): Record { + return { + name: obj.name, + call_template_type: obj.call_template_type, + file_path: obj.file_path, + auth: obj.auth, + auth_tools: obj.auth_tools ? new AuthSerializer().toDict(obj.auth_tools) : null, + }; + } + + /** + * REQUIRED + * Validate and convert a dictionary to a FileCallTemplate. + */ + validateDict(obj: Record): FileCallTemplate { + try { + return FileCallTemplateSchema.parse(obj); + } catch (e: any) { + throw new Error(`Invalid FileCallTemplate: ${e.message}\n${e.stack || ''}`); + } + } +} diff --git a/packages/file/src/file_communication_protocol.ts b/packages/file/src/file_communication_protocol.ts new file mode 100644 index 0000000..fb3784f --- /dev/null +++ b/packages/file/src/file_communication_protocol.ts @@ -0,0 +1,141 @@ +/** + * File communication protocol for UTCP client. + * + * This protocol reads UTCP manuals (or OpenAPI specs) from local files to register + * tools. It does not maintain any persistent connections. + * For direct text content, use @utcp/text instead. + */ +// packages/file/src/file_communication_protocol.ts +import { promises as fs } from 'fs'; +import * as path from 'path'; +import * as yaml from 'js-yaml'; +import { CommunicationProtocol, RegisterManualResult, CallTemplate, UtcpManual, UtcpManualSerializer, IUtcpClient } from '@utcp/sdk'; +import { OpenApiConverter } from '@utcp/http'; +import { FileCallTemplate, FileCallTemplateSchema } from './file_call_template'; + +/** + * REQUIRED + * Communication protocol for file-based UTCP manuals and tools. + */ +export class FileCommunicationProtocol implements CommunicationProtocol { + private _log_info(message: string): void { + console.log(`[FileCommunicationProtocol] ${message}`); + } + + private _log_error(message: string): void { + console.error(`[FileCommunicationProtocol Error] ${message}`); + } + + /** + * REQUIRED + * Register a file manual and return its tools as a UtcpManual. + */ + public async registerManual(caller: IUtcpClient, manualCallTemplate: CallTemplate): Promise { + const fileCallTemplate = FileCallTemplateSchema.parse(manualCallTemplate); + + try { + let filePath = path.resolve(fileCallTemplate.file_path); + if (!path.isAbsolute(fileCallTemplate.file_path) && caller.root_dir) { + filePath = path.resolve(caller.root_dir, fileCallTemplate.file_path); + } + + this._log_info(`Reading manual from '${filePath}'`); + + // Check if file exists + try { + await fs.access(filePath); + } catch (err: any) { + throw new Error(`ENOENT: no such file or directory, open '${filePath}'`); + } + + const content = await fs.readFile(filePath, 'utf-8'); + const fileExt = path.extname(filePath).toLowerCase(); + let data: any; + + // Parse based on extension + if (fileExt === '.yaml' || fileExt === '.yml') { + data = yaml.load(content); + } else { + // Try JSON + data = JSON.parse(content); + } + + let utcpManual: UtcpManual; + if (data && typeof data === 'object' && (data.openapi || data.swagger || data.paths)) { + this._log_info('Detected OpenAPI specification. Converting to UTCP manual.'); + const converter = new OpenApiConverter(data, { + specUrl: `file://${filePath}`, + callTemplateName: fileCallTemplate.name, + authTools: fileCallTemplate.auth_tools || undefined, + }); + utcpManual = converter.convert(); + } else { + // Try to validate as UTCP manual directly + utcpManual = new UtcpManualSerializer().validateDict(data); + } + + this._log_info(`Loaded ${utcpManual.tools.length} tools from ${filePath}`); + return { + manualCallTemplate, + manual: utcpManual, + success: true, + errors: [], + }; + } catch (error: any) { + const source = fileCallTemplate.file_path || 'unknown'; + this._log_error(`Failed to parse manual from '${source}': ${error.stack || error.message}`); + return { + manualCallTemplate, + manual: new UtcpManualSerializer().validateDict({ tools: [] }), + success: false, + errors: [error.stack || error.message], + }; + } + } + + /** + * REQUIRED + * Deregister a file manual (no-op). + */ + public async deregisterManual(caller: IUtcpClient, manualCallTemplate: CallTemplate): Promise { + this._log_info(`Deregistering file manual '${manualCallTemplate.name}' (no-op)`); + } + + /** + * REQUIRED + * Call a tool: for file templates, return content from the file path. + */ + public async callTool(caller: IUtcpClient, toolName: string, toolArgs: Record, toolCallTemplate: CallTemplate): Promise { + const fileCallTemplate = FileCallTemplateSchema.parse(toolCallTemplate); + + let filePath = path.resolve(fileCallTemplate.file_path); + if (!path.isAbsolute(fileCallTemplate.file_path) && caller.root_dir) { + filePath = path.resolve(caller.root_dir, fileCallTemplate.file_path); + } + + this._log_info(`Reading content from '${filePath}' for tool '${toolName}'`); + + try { + const content = await fs.readFile(filePath, 'utf-8'); + return content; + } catch (error: any) { + if (error.code === 'ENOENT') { + this._log_error(`File not found for tool '${toolName}': ${filePath}`); + } + throw error; + } + } + + /** + * REQUIRED + * Streaming variant: yields the full content as a single chunk. + */ + public async *callToolStreaming(caller: IUtcpClient, toolName: string, toolArgs: Record, toolCallTemplate: CallTemplate): AsyncGenerator { + const result = await this.callTool(caller, toolName, toolArgs, toolCallTemplate); + yield result; + } + + public async close(): Promise { + this._log_info('File Communication Protocol closed (no-op).'); + } +} diff --git a/packages/file/src/index.ts b/packages/file/src/index.ts new file mode 100644 index 0000000..9b5b707 --- /dev/null +++ b/packages/file/src/index.ts @@ -0,0 +1,30 @@ +/** + * File Communication Protocol plugin for UTCP. + */ +// packages/file/src/index.ts +import { CommunicationProtocol, CallTemplateSerializer, ensureCorePluginsInitialized } from '@utcp/sdk'; +import { FileCallTemplateSerializer } from './file_call_template'; +import { FileCommunicationProtocol } from './file_communication_protocol'; + +/** + * Registers the File protocol's CallTemplate serializer + * and its CommunicationProtocol implementation. + * This function is called automatically when the package is imported. + */ +export function register(override: boolean = false): void { + // Ensure core plugins are initialized first + ensureCorePluginsInitialized(); + + // Register the CallTemplate serializer + CallTemplateSerializer.registerCallTemplate('file', new FileCallTemplateSerializer(), override); + + // Register the CommunicationProtocol instance + CommunicationProtocol.communicationProtocols['file'] = new FileCommunicationProtocol(); +} + +// Automatically register File plugin on import +register(); + +// Export all public APIs +export * from './file_call_template'; +export * from './file_communication_protocol'; \ No newline at end of file diff --git a/packages/text/tests/text_communication_protocol.test.ts b/packages/file/tests/file_communication_protocol.test.ts similarity index 57% rename from packages/text/tests/text_communication_protocol.test.ts rename to packages/file/tests/file_communication_protocol.test.ts index 0645cde..cae2fea 100644 --- a/packages/text/tests/text_communication_protocol.test.ts +++ b/packages/file/tests/file_communication_protocol.test.ts @@ -1,11 +1,11 @@ -// packages/text/tests/text_communication_protocol.test.ts +// packages/file/tests/file_communication_protocol.test.ts import { test, expect, describe, afterEach } from "bun:test"; import { unlink, writeFile } from 'fs/promises'; import path from 'path'; // Import from package indices to trigger auto-registration -import { TextCommunicationProtocol, TextCallTemplate } from "@utcp/text"; +import { FileCommunicationProtocol, FileCallTemplate } from "@utcp/file"; import "@utcp/http"; // Needed for OpenAPI conversion -import { IUtcpClient } from "@utcp/sdk"; +import { IUtcpClient, ApiKeyAuth } from "@utcp/sdk"; const tempFiles: string[] = []; const mockClient = {} as IUtcpClient; @@ -26,8 +26,8 @@ const createTempFile = async (fileName: string, content: string): Promise { - const protocol = new TextCommunicationProtocol(); +describe("FileCommunicationProtocol", () => { + const protocol = new FileCommunicationProtocol(); const sampleUtcpManual = { utcp_version: "1.0.1", @@ -38,7 +38,7 @@ describe("TextCommunicationProtocol", () => { description: "A test tool.", tool_call_template: { name: "test_manual", - call_template_type: "text", + call_template_type: "file", file_path: "./dummy.json" } } @@ -62,9 +62,9 @@ describe("TextCommunicationProtocol", () => { describe("registerManual", () => { test("should correctly load a UTCP manual from a JSON file", async () => { const filePath = await createTempFile("manual.json", JSON.stringify(sampleUtcpManual)); - const callTemplate: TextCallTemplate = { + const callTemplate: FileCallTemplate = { name: "test_manual", - call_template_type: 'text', + call_template_type: 'file', file_path: filePath }; @@ -79,9 +79,9 @@ describe("TextCommunicationProtocol", () => { test("should correctly load and convert an OpenAPI spec from a YAML file", async () => { const yaml = await import("js-yaml"); const filePath = await createTempFile("openapi.yaml", yaml.dump(sampleOpenApiSpec)); - const callTemplate: TextCallTemplate = { + const callTemplate: FileCallTemplate = { name: "openapi_manual", - call_template_type: 'text', + call_template_type: 'file', file_path: filePath }; @@ -93,9 +93,9 @@ describe("TextCommunicationProtocol", () => { }); test("should return a failure result for a non-existent file", async () => { - const callTemplate: TextCallTemplate = { + const callTemplate: FileCallTemplate = { name: "nonexistent_manual", - call_template_type: 'text', + call_template_type: 'file', file_path: "/path/to/nonexistent/file.json" }; @@ -108,9 +108,9 @@ describe("TextCommunicationProtocol", () => { test("should return a failure result for a malformed JSON file", async () => { const filePath = await createTempFile("malformed.json", "{ not json }"); - const callTemplate: TextCallTemplate = { + const callTemplate: FileCallTemplate = { name: "malformed_manual", - call_template_type: 'text', + call_template_type: 'file', file_path: filePath }; @@ -121,61 +121,50 @@ describe("TextCommunicationProtocol", () => { expect(result.errors[0]).toMatch(/JSON/i); }); - test("should load a UTCP manual from direct JSON content", async () => { - const callTemplate: TextCallTemplate = { - name: "direct_content_manual", - call_template_type: 'text', - content: JSON.stringify(sampleUtcpManual) - }; - - const result = await protocol.registerManual(mockClient, callTemplate); - - expect(result.success).toBe(true); - expect(result.errors).toEqual([]); - expect(result.manual.tools).toHaveLength(1); - expect(result.manual.tools[0]?.name).toBe("test.tool"); - }); - - test("should load and convert an OpenAPI spec from direct YAML content", async () => { - const yaml = await import("js-yaml"); - const yamlContent = yaml.dump(sampleOpenApiSpec); - const callTemplate: TextCallTemplate = { - name: "direct_openapi_manual", - call_template_type: 'text', - content: yamlContent + test("should correctly load and convert an OpenAPI spec from a JSON file", async () => { + const filePath = await createTempFile("openapi.json", JSON.stringify(sampleOpenApiSpec)); + const callTemplate: FileCallTemplate = { + name: "openapi_json_manual", + call_template_type: 'file', + file_path: filePath }; const result = await protocol.registerManual(mockClient, callTemplate); expect(result.success).toBe(true); expect(result.manual.tools).toHaveLength(1); - expect(result.manual.tools[0]?.name).toBe("getTest"); + expect(result.manual.tools[0]?.name).toContain("getTest"); }); - test("should prefer content over file_path when both are provided", async () => { - const filePath = await createTempFile("unused.json", JSON.stringify({ tools: [] })); - const callTemplate: TextCallTemplate = { - name: "content_precedence_manual", - call_template_type: 'text', + test("should support auth_tools parameter for OpenAPI conversion", async () => { + const filePath = await createTempFile("openapi_auth.json", JSON.stringify(sampleOpenApiSpec)); + const callTemplate: FileCallTemplate = { + name: "openapi_with_auth", + call_template_type: 'file', file_path: filePath, - content: JSON.stringify(sampleUtcpManual) + auth_tools: { + auth_type: 'api_key', + var_name: 'X-API-Key', + api_key: '${API_KEY}', + location: 'header' + } as ApiKeyAuth }; const result = await protocol.registerManual(mockClient, callTemplate); expect(result.success).toBe(true); expect(result.manual.tools).toHaveLength(1); - expect(result.manual.tools[0]?.name).toBe("test.tool"); }); - test("should fail validation when neither file_path nor content is provided", async () => { + test("should fail validation when file_path is not provided", async () => { const callTemplate = { name: "invalid_manual", - call_template_type: 'text' - }; + call_template_type: 'file' + } as any; - const action = async () => await protocol.registerManual(mockClient, callTemplate); - await expect(action()).rejects.toThrow(/Either file_path or content must be provided/); + await expect(async () => { + await protocol.registerManual(mockClient, callTemplate); + }).toThrow(); }); }); @@ -183,9 +172,9 @@ describe("TextCommunicationProtocol", () => { test("should return the raw content of the specified file", async () => { const fileContent = "This is the raw content of the file."; const filePath = await createTempFile("content.txt", fileContent); - const callTemplate: TextCallTemplate = { + const callTemplate: FileCallTemplate = { name: "file_content_tool", - call_template_type: 'text', + call_template_type: 'file', file_path: filePath }; @@ -194,9 +183,9 @@ describe("TextCommunicationProtocol", () => { }); test("should throw an error if the file does not exist", async () => { - const callTemplate: TextCallTemplate = { + const callTemplate: FileCallTemplate = { name: "nonexistent_file_tool", - call_template_type: 'text', + call_template_type: 'file', file_path: "/path/to/nonexistent/file.txt" }; @@ -204,41 +193,15 @@ describe("TextCommunicationProtocol", () => { await expect(action()).rejects.toThrow(/no such file or directory/); }); - test("should return direct content when content is provided", async () => { - const directContent = "This is direct content."; - const callTemplate: TextCallTemplate = { - name: "direct_content_tool", - call_template_type: 'text', - content: directContent - }; - - const result = await protocol.callTool(mockClient, "any.tool", {}, callTemplate); - expect(result).toBe(directContent); - }); - - test("should prefer content over file_path when both are provided", async () => { - const fileContent = "File content."; - const directContent = "Direct content wins."; - const filePath = await createTempFile("ignored.txt", fileContent); - const callTemplate: TextCallTemplate = { - name: "precedence_tool", - call_template_type: 'text', - file_path: filePath, - content: directContent - }; - - const result = await protocol.callTool(mockClient, "any.tool", {}, callTemplate); - expect(result).toBe(directContent); - }); - - test("should throw an error when neither file_path nor content is provided", async () => { + test("should throw an error when file_path is not provided", async () => { const callTemplate = { name: "invalid_tool", - call_template_type: 'text' - }; + call_template_type: 'file' + } as any; - const action = async () => await protocol.callTool(mockClient, "any.tool", {}, callTemplate); - await expect(action()).rejects.toThrow(/Either file_path or content must be provided/); + await expect(async () => { + await protocol.callTool(mockClient, "any.tool", {}, callTemplate); + }).toThrow(); }); }); @@ -246,9 +209,9 @@ describe("TextCommunicationProtocol", () => { test("should yield the file content as a single chunk", async () => { const fileContent = JSON.stringify({ data: "stream content" }); const filePath = await createTempFile("stream.json", fileContent); - const callTemplate: TextCallTemplate = { + const callTemplate: FileCallTemplate = { name: "streaming_file_tool", - call_template_type: 'text', + call_template_type: 'file', file_path: filePath }; diff --git a/packages/file/tsconfig.json b/packages/file/tsconfig.json new file mode 100644 index 0000000..b1f2ef1 --- /dev/null +++ b/packages/file/tsconfig.json @@ -0,0 +1,22 @@ +// packages/file/tsconfig.json +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "./dist", + "rootDir": "./src", + "composite": true + }, + "include": [ + "src/**/*" + ], + "exclude": [ + "node_modules", + "dist", + "**/*.test.ts", + "**/*.spec.ts" + ], + "references": [ + { "path": "../core" }, + { "path": "../http" } + ] +} \ No newline at end of file diff --git a/packages/file/tsup.config.ts b/packages/file/tsup.config.ts new file mode 100644 index 0000000..7436d15 --- /dev/null +++ b/packages/file/tsup.config.ts @@ -0,0 +1,17 @@ +import { defineConfig } from 'tsup'; + +export default defineConfig({ + entry: ['src/index.ts'], + format: ['esm', 'cjs'], + dts: { + compilerOptions: { + composite: false, + paths: {}, + }, + }, + splitting: false, + sourcemap: true, + clean: true, + outDir: 'dist', + external: ['@utcp/sdk', '@utcp/http', 'js-yaml'], +}); diff --git a/packages/http/package.json b/packages/http/package.json index 9ba25b0..80b6d6f 100644 --- a/packages/http/package.json +++ b/packages/http/package.json @@ -1,6 +1,6 @@ { "name": "@utcp/http", - "version": "1.0.6", + "version": "1.0.13", "description": "HTTP utilities for UTCP", "main": "dist/index.cjs", "module": "dist/index.js", @@ -43,7 +43,7 @@ } }, "dependencies": { - "@utcp/sdk": "^1.0.6", + "@utcp/sdk": "^1.0.15", "axios": "^1.11.0", "js-yaml": "^4.1.0" }, diff --git a/packages/http/src/http_communication_protocol.ts b/packages/http/src/http_communication_protocol.ts index d97f476..f548be4 100644 --- a/packages/http/src/http_communication_protocol.ts +++ b/packages/http/src/http_communication_protocol.ts @@ -1,7 +1,6 @@ // packages/http/src/http_communication_protocol.ts import axios, { AxiosInstance, AxiosRequestConfig, Method } from 'axios'; import * as yaml from 'js-yaml'; -import { URLSearchParams } from 'url'; import { CommunicationProtocol } from '@utcp/sdk'; import { RegisterManualResult } from '@utcp/sdk'; import { CallTemplate } from '@utcp/sdk'; @@ -244,11 +243,13 @@ export class HttpCommunicationProtocol implements CommunicationProtocol { if (!apiKeyAuth.api_key) { throw new Error("API key for ApiKeyAuth is empty."); } - if (apiKeyAuth.location === 'header') { + // Default to 'header' if location is not specified + const location = apiKeyAuth.location || 'header'; + if (location === 'header') { requestConfig.headers = { ...requestConfig.headers, [apiKeyAuth.var_name]: apiKeyAuth.api_key }; - } else if (apiKeyAuth.location === 'query') { + } else if (location === 'query') { requestConfig.params = { ...requestConfig.params, [apiKeyAuth.var_name]: apiKeyAuth.api_key }; - } else if (apiKeyAuth.location === 'cookie') { + } else if (location === 'cookie') { cookies[apiKeyAuth.var_name] = apiKeyAuth.api_key; } } else if (httpCallTemplate.auth.auth_type === 'basic') { diff --git a/packages/http/src/index.ts b/packages/http/src/index.ts index 7afb9ba..91d42b0 100644 --- a/packages/http/src/index.ts +++ b/packages/http/src/index.ts @@ -41,4 +41,5 @@ export * from './streamable_http_call_template'; export * from './streamable_http_communication_protocol'; export * from './sse_call_template'; export * from './sse_communication_protocol'; -export * from './openapi_converter'; \ No newline at end of file +export { OpenApiConverter } from './openapi_converter'; +export type { OpenApiConverterOptions } from './openapi_converter'; \ No newline at end of file diff --git a/packages/http/src/openapi_converter.ts b/packages/http/src/openapi_converter.ts index 6cecd5f..f6f67f7 100644 --- a/packages/http/src/openapi_converter.ts +++ b/packages/http/src/openapi_converter.ts @@ -26,10 +26,14 @@ import { BasicAuth } from '@utcp/sdk'; import { OAuth2Auth } from '@utcp/sdk'; import { HttpCallTemplate } from './http_call_template'; -interface OpenApiConverterOptions { +/** + * Options for the OpenAPI converter. + */ +export interface OpenApiConverterOptions { specUrl?: string; callTemplateName?: string; authTools?: Auth; + baseUrl?: string; } /** @@ -44,6 +48,7 @@ interface OpenApiConverterOptions { export class OpenApiConverter { private spec: Record; private spec_url: string | undefined; + private base_url: string | undefined; private auth_tools: Auth | undefined; private placeholder_counter: number = 0; private call_template_name: string; @@ -52,14 +57,16 @@ export class OpenApiConverter { * Initializes the OpenAPI converter. * * @param openapi_spec Parsed OpenAPI specification as a dictionary. - * @param options Optional settings including spec_url, call_template_name, and auth_tools. + * @param options Optional settings including spec_url, call_template_name, auth_tools, and baseUrl. * - specUrl: URL where the specification was retrieved from. * - callTemplateName: Custom name for the call_template if spec title not provided. * - authTools: Optional auth configuration for generated tools. + * - baseUrl: Optional base URL override for all API endpoints. */ constructor(openapi_spec: Record, options?: OpenApiConverterOptions) { this.spec = openapi_spec; this.spec_url = options?.specUrl; + this.base_url = options?.baseUrl; this.auth_tools = options?.authTools; this.placeholder_counter = 0; @@ -102,10 +109,22 @@ export class OpenApiConverter { const tools: Tool[] = []; let baseUrl = '/'; - const servers = this.spec.servers; - if (servers && Array.isArray(servers) && servers.length > 0 && servers[0].url) { - baseUrl = servers[0].url; - } else if (this.spec_url) { + // 1. Explicit baseUrl option (highest priority) + if (this.base_url) { + baseUrl = this.base_url; + } + // 2. OpenAPI 3.0 servers field + else if (this.spec.servers && Array.isArray(this.spec.servers) && this.spec.servers.length > 0 && this.spec.servers[0].url) { + baseUrl = this.spec.servers[0].url; + } + // 3. OpenAPI 2.0 host and basePath fields + else if (this.spec.host) { + const scheme = this.spec.schemes?.[0] || 'https'; + const basePath = this.spec.basePath || ''; + baseUrl = `${scheme}://${this.spec.host}${basePath}`; + } + // 4. Fallback to spec_url ONLY if it's an HTTP/HTTPS URL + else if (this.spec_url && (this.spec_url.startsWith('http://') || this.spec_url.startsWith('https://'))) { try { const parsedUrl = new URL(this.spec_url); baseUrl = `${parsedUrl.protocol}//${parsedUrl.host}`; @@ -113,7 +132,7 @@ export class OpenApiConverter { console.error(`[OpenApiConverter] Invalid spec_url provided: ${this.spec_url}`); } } else { - console.warn("[OpenApiConverter] No server info or spec URL provided. Using fallback base URL: '/'"); + console.warn("[OpenApiConverter] No server info found in OpenAPI spec. Using fallback base URL: '/'. Tools may not work correctly without a valid base URL."); } const paths = this.spec.paths || {}; @@ -220,7 +239,7 @@ export class OpenApiConverter { const { inputs, headerFields, bodyField } = this._extractInputs(path, operation); const outputs = this._extractOutputs(operation); const auth = this._extractAuth(operation); - const fullUrl = `${baseUrl.replace(/\/$/, '')}/${path.lstrip('/')}`; + const fullUrl = `${baseUrl.replace(/\/$/, '')}/${path.replace(/^\/+/, '')}`; const callTemplate: HttpCallTemplate = { name: this.call_template_name, @@ -594,20 +613,4 @@ export class OpenApiConverter { return undefined; } -} - -declare global { - interface String { - lstrip(chars: string): string; - } -} - -if (!String.prototype.lstrip) { - String.prototype.lstrip = function (this: string, chars: string): string { - let result = this; - while (result.startsWith(chars)) { - result = result.substring(chars.length); - } - return result; - }; } \ No newline at end of file diff --git a/packages/http/src/sse_communication_protocol.ts b/packages/http/src/sse_communication_protocol.ts index 61a5805..9f44076 100644 --- a/packages/http/src/sse_communication_protocol.ts +++ b/packages/http/src/sse_communication_protocol.ts @@ -43,11 +43,13 @@ export class SseCommunicationProtocol implements CommunicationProtocol { if ('api_key' in provider.auth) { const apiKeyAuth = provider.auth as ApiKeyAuth; if (apiKeyAuth.api_key) { - if (apiKeyAuth.location === 'header') { + // Default to 'header' if location is not specified + const location = apiKeyAuth.location || 'header'; + if (location === 'header') { headers[apiKeyAuth.var_name] = apiKeyAuth.api_key; - } else if (apiKeyAuth.location === 'query') { + } else if (location === 'query') { queryParams[apiKeyAuth.var_name] = apiKeyAuth.api_key; - } else if (apiKeyAuth.location === 'cookie') { + } else if (location === 'cookie') { cookies[apiKeyAuth.var_name] = apiKeyAuth.api_key; } } else { diff --git a/packages/http/src/streamable_http_communication_protocol.ts b/packages/http/src/streamable_http_communication_protocol.ts index 324ed2c..ebdd9c1 100644 --- a/packages/http/src/streamable_http_communication_protocol.ts +++ b/packages/http/src/streamable_http_communication_protocol.ts @@ -43,11 +43,13 @@ export class StreamableHttpCommunicationProtocol implements CommunicationProtoco if ('api_key' in provider.auth) { const apiKeyAuth = provider.auth as ApiKeyAuth; if (apiKeyAuth.api_key) { - if (apiKeyAuth.location === 'header') { + // Default to 'header' if location is not specified + const location = apiKeyAuth.location || 'header'; + if (location === 'header') { headers[apiKeyAuth.var_name] = apiKeyAuth.api_key; - } else if (apiKeyAuth.location === 'query') { + } else if (location === 'query') { queryParams[apiKeyAuth.var_name] = apiKeyAuth.api_key; - } else if (apiKeyAuth.location === 'cookie') { + } else if (location === 'cookie') { cookies[apiKeyAuth.var_name] = apiKeyAuth.api_key; } } else { diff --git a/packages/mcp/package.json b/packages/mcp/package.json index f5d23ba..7e63cf1 100644 --- a/packages/mcp/package.json +++ b/packages/mcp/package.json @@ -1,6 +1,6 @@ { "name": "@utcp/mcp", - "version": "1.0.6", + "version": "1.0.12", "description": "Model Context Protocol integration for UTCP", "main": "dist/index.cjs", "module": "dist/index.js", @@ -45,7 +45,7 @@ }, "dependencies": { "@modelcontextprotocol/sdk": "^1.17.4", - "@utcp/sdk": "^1.0.6", + "@utcp/sdk": "^1.0.15", "axios": "^1.11.0" }, "devDependencies": { diff --git a/packages/mcp/src/mcp_communication_protocol.ts b/packages/mcp/src/mcp_communication_protocol.ts index 9095dbb..307e135 100644 --- a/packages/mcp/src/mcp_communication_protocol.ts +++ b/packages/mcp/src/mcp_communication_protocol.ts @@ -3,7 +3,6 @@ import { Client as McpClient } from '@modelcontextprotocol/sdk/client/index.js'; import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js'; import { StreamableHTTPClientTransport, StreamableHTTPClientTransportOptions } from '@modelcontextprotocol/sdk/client/streamableHttp.js'; import axios, { AxiosInstance } from 'axios'; -import { URLSearchParams } from 'url'; import { CommunicationProtocol } from '@utcp/sdk'; import { RegisterManualResult } from '@utcp/sdk'; import { CallTemplate } from '@utcp/sdk'; diff --git a/packages/text/.gitignore b/packages/text/.gitignore new file mode 100644 index 0000000..62ccde4 --- /dev/null +++ b/packages/text/.gitignore @@ -0,0 +1,4 @@ +node_modules/ +dist/ +*.tsbuildinfo +.DS_Store diff --git a/packages/text/README.md b/packages/text/README.md index 759dbf1..5096b00 100644 --- a/packages/text/README.md +++ b/packages/text/README.md @@ -1,127 +1,118 @@ -# @utcp/text: Text File Communication Protocol Plugin for UTCP +# @utcp/text -The `@utcp/text` package provides a straightforward communication protocol for the Universal Tool Calling Protocol (UTCP) client to interact with local text files. It's primarily used for loading static UTCP Manuals or OpenAPI specifications directly from local JSON or YAML files, without needing a network request. +Text Content Communication Protocol plugin for the Universal Tool Calling Protocol (UTCP). -## Features +## Overview -* **Text `CallTemplate`**: Defines the configuration for file-based tool definitions (`TextCallTemplate`), specifying the `file_path` to the local manual or spec. Authentication is explicitly `null` as file access typically relies on local permissions. -* **`TextCommunicationProtocol`**: Implements the `CommunicationProtocol` interface for file-based interactions: - * **Tool Discovery**: Reads and parses local JSON or YAML files. It can directly interpret UTCP Manuals or automatically convert OpenAPI (v2/v3) specifications into UTCP `Tool` definitions (by utilizing the `OpenApiConverter` from `@utcp/http`). - * **Tool Execution**: When a tool associated with a `TextCallTemplate` is "called", the protocol simply returns the raw content of the configured `file_path` as a string. This is useful for retrieving static data, configuration snippets, or even full documentation embedded as a tool. - * **Stateless**: This protocol does not maintain any persistent connections or external resources, making it very lightweight. - * **Path Resolution**: Resolves relative file paths using the `UtcpClient`'s configured root directory (`_rootPath`), ensuring flexibility in project structure. +This plugin provides support for loading UTCP manuals and tool definitions from direct text/string content. Unlike `@utcp/file` which reads from files, this plugin is **browser-compatible** and works in any JavaScript environment. ## Installation ```bash -bun add @utcp/text @utcp/sdk +npm install @utcp/text +``` + +## Usage -# Or using npm -npm install @utcp/text @utcp/sdk +The plugin automatically registers itself when imported: + +```typescript +import '@utcp/text'; +import { UtcpClient } from '@utcp/sdk'; + +const utcpManualContent = JSON.stringify({ + tools: [ + { + name: 'my_tool', + description: 'A sample tool', + inputs: { type: 'object', properties: {} }, + outputs: { type: 'object', properties: {} } + } + ] +}); + +const client = await UtcpClient.create(); +await client.registerCallTemplate({ + call_template_type: 'text', + name: 'my-manual', + content: utcpManualContent +}); ``` -Note: `@utcp/sdk` is a peer dependency. `@utcp/http` and `js-yaml` dependencies are included automatically for OpenAPI conversion and YAML parsing. +## Features + +- **Browser-Compatible**: No file system dependencies +- **Multiple Formats**: Supports both JSON and YAML content +- **OpenAPI Conversion**: Automatically converts OpenAPI specs to UTCP manuals +- **Type-Safe**: Full TypeScript support with Zod validation -## Usage +## Text Call Template + +The text call template accepts the following configuration: -The Text plugin registers automatically when you import it—no manual registration needed. Simply import from `@utcp/text` to enable text file support. +- `call_template_type`: Must be set to `'text'` +- `name`: Unique identifier for this manual +- `content`: String content containing the UTCP manual or OpenAPI spec (required) +- `auth_tools`: Optional authentication to apply to tools from OpenAPI specs + +## Example: OpenAPI Spec ```typescript -// From your application's entry point +import '@utcp/text'; +import { UtcpClient } from '@utcp/sdk'; + +const openApiSpec = ` +openapi: 3.0.0 +info: + title: My API + version: 1.0.0 +paths: + /users: + get: + summary: Get users + responses: + '200': + description: Success +`; + +const client = await UtcpClient.create(); +await client.registerCallTemplate({ + call_template_type: 'text', + name: 'my-api', + content: openApiSpec +}); +``` +## Browser Usage + +Perfect for web applications: + +```typescript +import '@utcp/text'; import { UtcpClient } from '@utcp/sdk'; -import { TextCallTemplateSerializer } from '@utcp/text'; -import * as path from 'path'; -import * as fs from 'fs/promises'; // For creating dummy files - -async function main() { - // Create a dummy UTCP manual file for demonstration - const manualContent = { - "utcp_version": "1.0.0", - "manual_version": "1.0.0", - "tools": [ - { - "name": "read_static_data", - "description": "Reads static data from a local file.", - "inputs": {}, - "outputs": { "type": "string", "description": "The content of the file." }, - "tags": ["file", "static"], - "tool_call_template": { - "name": "static_file_reader", - "call_template_type": "text", - "file_path": "./config/static_data.txt" // The file path for the tool's content - } - }, - { - "name": "describe_project", - "description": "Provides a description of the project from a local markdown file.", - "inputs": {}, - "outputs": { "type": "string" }, - "tags": ["documentation"], - "tool_call_template": { - "name": "project_readme_reader", - "call_template_type": "text", - "file_path": "./README.md" // Example: reads the project's README - } - } - ] - }; - const configDirPath = path.resolve(process.cwd(), './config'); - await fs.mkdir(configDirPath, { recursive: true }); - - const dummyManualPath = path.resolve(configDirPath, './my_local_manual.json'); - await fs.writeFile(dummyManualPath, JSON.stringify(manualContent, null, 2)); - - const staticDataPath = path.resolve(configDirPath, './static_data.txt'); - await fs.writeFile(staticDataPath, 'Hello from UTCP Text Plugin static data!'); - - // Define a CallTemplate to load the local UTCP manual from the 'config' directory - const serializer = new TextCallTemplateSerializer(); - const textCallTemplate = serializer.validateDict({ - name: 'local_manual_loader', - call_template_type: 'text', - file_path: './config/my_local_manual.json', // Path relative to client's root_path - }); - - const client = await UtcpClient.create(process.cwd(), { - manual_call_templates: [textCallTemplate] // Register the text manual at client startup - }); - - console.log('Text Plugin active. Searching for tools...'); - - // Example: Call 'read_static_data' tool. This will return the content of 'static_data.txt'. - try { - const staticDataReaderTool = await client.searchTools('read static data'); - if (staticDataReaderTool.length > 0) { - const result = await client.callTool(staticDataReaderTool.name, {}); - console.log('Result from "read_static_data" tool:', result); - } - } catch (error) { - console.error('Error calling "read_static_data" tool:', error); - } - - // Example: Call 'describe_project' tool. This will return the content of the project's README.md. - try { - const projectDescTool = await client.searchTools('project description'); - if (projectDescTool.length > 0) { - const result = await client.callTool(projectDescTool.name, {}); - console.log('Result from "describe_project" tool (first 100 chars):', String(result).substring(0, 100) + '...'); - } - } catch (error) { - console.error('Error calling "describe_project" tool:', error); - } finally { - // Clean up dummy files - await fs.unlink(dummyManualPath); - await fs.unlink(staticDataPath); - await fs.rmdir(configDirPath); // Remove the config directory - } - - await client.close(); // No-op for text protocol, but good practice -} - -main().catch(console.error); + +// Load from API or inline +const response = await fetch('/api/utcp-manual'); +const manualContent = await response.text(); + +const client = await UtcpClient.create(); +await client.registerCallTemplate({ + call_template_type: 'text', + name: 'remote-manual', + content: manualContent +}); ``` -## Development +## Comparison with @utcp/file + +| Feature | @utcp/text | @utcp/file | +|---------|-------------|-----------| +| Browser compatible | ✅ Yes | ❌ No | +| Node.js compatible | ✅ Yes | ✅ Yes | +| File system access | ❌ No | ✅ Yes | +| Direct content | ✅ Yes | ❌ No | +| Use case | Web apps, inline content | Server-side file reading | + +## License -Refer to the root `README.md` for monorepo development and testing instructions. +MPL-2.0 diff --git a/packages/text/package.json b/packages/text/package.json index 7d82670..d882c02 100644 --- a/packages/text/package.json +++ b/packages/text/package.json @@ -1,7 +1,7 @@ { "name": "@utcp/text", - "version": "1.0.8", - "description": "Text utilities for UTCP", + "version": "1.0.12", + "description": "Text content protocol for UTCP - handles direct text content (browser-compatible)", "main": "dist/index.cjs", "module": "dist/index.js", "types": "dist/index.d.ts", @@ -15,15 +15,10 @@ }, "keywords": [ "utcp", - "universal-tool-calling-protocol", - "tools", - "api", - "typescript", - "tool calling", "text", - "agent", - "ai", - "llm" + "content", + "browser", + "tool calling" ], "publishConfig": { "access": "public" @@ -43,11 +38,12 @@ } }, "dependencies": { - "@utcp/sdk": "^1.0.6" + "@utcp/http": "^1.0.13", + "@utcp/sdk": "^1.0.15", + "js-yaml": "^4.1.0" }, "devDependencies": { "bun-types": "latest", - "typescript": "^5.0.0", - "@types/bun": "latest" + "typescript": "^5.0.0" } } diff --git a/packages/text/src/index.ts b/packages/text/src/index.ts index 3a441fb..fe403e5 100644 --- a/packages/text/src/index.ts +++ b/packages/text/src/index.ts @@ -1,5 +1,6 @@ /** - * Text Communication Protocol plugin for UTCP. + * Text Content Communication Protocol plugin for UTCP. + * Handles direct text content (browser-compatible). */ // packages/text/src/index.ts import { CommunicationProtocol, CallTemplateSerializer, ensureCorePluginsInitialized } from '@utcp/sdk'; @@ -14,12 +15,17 @@ import { TextCommunicationProtocol } from './text_communication_protocol'; export function register(override: boolean = false): void { // Ensure core plugins are initialized first ensureCorePluginsInitialized(); + + // Register the CallTemplate serializer CallTemplateSerializer.registerCallTemplate('text', new TextCallTemplateSerializer(), override); + + // Register the CommunicationProtocol instance CommunicationProtocol.communicationProtocols['text'] = new TextCommunicationProtocol(); } // Automatically register Text plugin on import register(); +// Export all public APIs export * from './text_call_template'; -export * from './text_communication_protocol'; \ No newline at end of file +export * from './text_communication_protocol'; diff --git a/packages/text/src/text_call_template.ts b/packages/text/src/text_call_template.ts index 548b8d8..8a47a5e 100644 --- a/packages/text/src/text_call_template.ts +++ b/packages/text/src/text_call_template.ts @@ -1,30 +1,25 @@ // packages/text/src/text_call_template.ts import { z } from 'zod'; -import { CallTemplate } from '@utcp/sdk'; -import { Auth, AuthSchema, AuthSerializer } from '@utcp/sdk'; +import { CallTemplate, Auth, AuthSchema, AuthSerializer } from '@utcp/sdk'; import { Serializer } from '@utcp/sdk'; /** - * REQUIRED - * Call template for text-based manuals and tools. + * Text call template for UTCP client. * - * Supports both reading UTCP manuals or tool definitions from local JSON/YAML files - * or directly from string content. Useful for static tool configurations or environments - * where manuals are distributed as files or dynamically generated strings. + * This template allows passing UTCP manuals or tool definitions directly as text content. + * It supports both JSON and YAML formats and can convert OpenAPI specifications to UTCP manuals. * * Attributes: * call_template_type: Always "text" for text call templates. - * file_path: Path to the file containing the UTCP manual or tool definitions (optional if content is provided). - * content: Direct string content of the UTCP manual or tool definitions (optional if file_path is provided). - * auth: Always None - text call templates don't support authentication for file access. + * content: Direct text content of the UTCP manual or tool definitions (required). + * base_url: Optional base URL for API endpoints when converting OpenAPI specs. Overrides spec's server configuration. + * auth: Always undefined - text call templates don't support authentication. * auth_tools: Optional authentication to apply to generated tools from OpenAPI specs. - * - * Note: At least one of file_path or content must be provided. If both are provided, content takes precedence. */ export interface TextCallTemplate extends CallTemplate { call_template_type: 'text'; - file_path?: string; - content?: string; + content: string; + base_url?: string; auth?: undefined; auth_tools?: Auth | null; } @@ -35,8 +30,8 @@ export interface TextCallTemplate extends CallTemplate { export const TextCallTemplateSchema: z.ZodType = z.object({ name: z.string().optional(), call_template_type: z.literal('text'), - file_path: z.string().optional().describe('The path to the file containing the UTCP manual or tool definitions.'), - content: z.string().optional().describe('Direct string content of the UTCP manual or tool definitions.'), + content: z.string().describe('Direct text content of the UTCP manual or tool definitions'), + base_url: z.string().optional().describe('Optional base URL for API endpoints when converting OpenAPI specs'), auth: z.undefined().optional(), auth_tools: AuthSchema.nullable().optional().transform((val) => { if (val === null || val === undefined) return null; @@ -44,36 +39,24 @@ export const TextCallTemplateSchema: z.ZodType = z.object({ return new AuthSerializer().validateDict(val as any); } return val as Auth; - }).describe('Authentication to apply to generated tools from OpenAPI specs.'), -}).strict().refine( - (data) => data.file_path !== undefined || data.content !== undefined, - { message: 'Either file_path or content must be provided' } -) as z.ZodType; + }).describe('Optional authentication to apply to generated tools from OpenAPI specs'), +}).strict() as z.ZodType; /** - * REQUIRED - * Serializer for TextCallTemplate. + * Serializer for TextCallTemplate objects. */ export class TextCallTemplateSerializer extends Serializer { - /** - * REQUIRED - * Convert a TextCallTemplate to a dictionary. - */ toDict(obj: TextCallTemplate): Record { return { name: obj.name, call_template_type: obj.call_template_type, - file_path: obj.file_path, content: obj.content, + base_url: obj.base_url, auth: obj.auth, auth_tools: obj.auth_tools ? new AuthSerializer().toDict(obj.auth_tools) : null, }; } - /** - * REQUIRED - * Validate and convert a dictionary to a TextCallTemplate. - */ validateDict(obj: Record): TextCallTemplate { try { return TextCallTemplateSchema.parse(obj); @@ -81,4 +64,4 @@ export class TextCallTemplateSerializer extends Serializer { throw new Error(`Invalid TextCallTemplate: ${e.message}\n${e.stack || ''}`); } } -} \ No newline at end of file +} diff --git a/packages/text/src/text_communication_protocol.ts b/packages/text/src/text_communication_protocol.ts index 779c4fd..0c656c5 100644 --- a/packages/text/src/text_communication_protocol.ts +++ b/packages/text/src/text_communication_protocol.ts @@ -1,12 +1,10 @@ /** * Text communication protocol for UTCP client. * - * This protocol reads UTCP manuals (or OpenAPI specs) from local files to register - * tools. It does not maintain any persistent connections. + * This protocol parses UTCP manuals (or OpenAPI specs) from direct text content. + * It's browser-compatible and requires no file system access. */ // packages/text/src/text_communication_protocol.ts -import { promises as fs } from 'fs'; -import * as path from 'path'; import * as yaml from 'js-yaml'; import { CommunicationProtocol, RegisterManualResult, CallTemplate, UtcpManual, UtcpManualSerializer, IUtcpClient } from '@utcp/sdk'; import { OpenApiConverter } from '@utcp/http'; @@ -14,7 +12,7 @@ import { TextCallTemplate, TextCallTemplateSchema } from './text_call_template'; /** * REQUIRED - * Communication protocol for file-based UTCP manuals and tools. + * Communication protocol for text-based UTCP manuals and tools. */ export class TextCommunicationProtocol implements CommunicationProtocol { private _log_info(message: string): void { @@ -33,57 +31,18 @@ export class TextCommunicationProtocol implements CommunicationProtocol { const textCallTemplate = TextCallTemplateSchema.parse(manualCallTemplate); try { - let content: string; - let fileExt: string = '.json'; // Default extension - let sourceInfo: string; - - // Prefer content over file_path if both are provided - if (textCallTemplate.content) { - this._log_info('Using direct content for manual'); - content = textCallTemplate.content; - sourceInfo = 'direct content'; - // Try to infer format from content structure - } else if (textCallTemplate.file_path) { - let filePath = path.resolve(textCallTemplate.file_path); - if (!path.isAbsolute(textCallTemplate.file_path) && caller.root_dir) { - filePath = path.resolve(caller.root_dir, textCallTemplate.file_path); - } - sourceInfo = filePath; - this._log_info(`Reading manual from '${filePath}'`); - - // Check if file exists - try { - await fs.access(filePath); - } catch (err: any) { - throw new Error(`ENOENT: no such file or directory, open '${filePath}'`); - } - - content = await fs.readFile(filePath, 'utf-8'); - fileExt = path.extname(filePath).toLowerCase(); - } else { - throw new Error('Either file_path or content must be provided'); - } - + this._log_info('Parsing direct content for manual'); + const content = textCallTemplate.content; let data: any; - // Parse based on extension or content format - if (fileExt === '.yaml' || fileExt === '.yml') { - data = yaml.load(content); - } else { - // Try JSON first + // Try JSON first, then YAML + try { + data = JSON.parse(content); + } catch (jsonError) { try { - data = JSON.parse(content); - } catch (jsonError) { - // If JSON fails and we're using direct content, try YAML - if (textCallTemplate.content) { - try { - data = yaml.load(content); - } catch (yamlError) { - throw jsonError; // Throw original JSON error if YAML also fails - } - } else { - throw jsonError; - } + data = yaml.load(content); + } catch (yamlError) { + throw new Error(`Failed to parse content as JSON or YAML: ${(jsonError as Error).message}`); } } @@ -91,31 +50,33 @@ export class TextCommunicationProtocol implements CommunicationProtocol { if (data && typeof data === 'object' && (data.openapi || data.swagger || data.paths)) { this._log_info('Detected OpenAPI specification. Converting to UTCP manual.'); const converter = new OpenApiConverter(data, { - specUrl: textCallTemplate.content ? 'direct-content://' : `file://${sourceInfo}`, + specUrl: 'text://content', callTemplateName: textCallTemplate.name, authTools: textCallTemplate.auth_tools || undefined, + baseUrl: textCallTemplate.base_url, }); utcpManual = converter.convert(); } else { // Try to validate as UTCP manual directly + this._log_info('Validating content as UTCP manual.'); utcpManual = new UtcpManualSerializer().validateDict(data); } - this._log_info(`Loaded ${utcpManual.tools.length} tools from ${sourceInfo}`); + this._log_info(`Successfully registered manual with ${utcpManual.tools.length} tools.`); return { - manualCallTemplate, + manualCallTemplate: textCallTemplate, manual: utcpManual, success: true, errors: [], }; - } catch (error: any) { - const source = textCallTemplate.content ? 'direct content' : textCallTemplate.file_path || 'unknown'; - this._log_error(`Failed to parse manual from '${source}': ${error.stack || error.message}`); + } catch (err: any) { + const errMsg = `Failed to register text manual: ${err.message}`; + this._log_error(errMsg); return { - manualCallTemplate, + manualCallTemplate: textCallTemplate, manual: new UtcpManualSerializer().validateDict({ tools: [] }), success: false, - errors: [error.stack || error.message], + errors: [errMsg], }; } } @@ -130,35 +91,12 @@ export class TextCommunicationProtocol implements CommunicationProtocol { /** * REQUIRED - * Call a tool: for text templates, return content from either the direct content or file path. + * Execute a tool call. Text protocol returns the content directly. */ public async callTool(caller: IUtcpClient, toolName: string, toolArgs: Record, toolCallTemplate: CallTemplate): Promise { const textCallTemplate = TextCallTemplateSchema.parse(toolCallTemplate); - - // Prefer content over file_path if both are provided - if (textCallTemplate.content) { - this._log_info(`Returning direct content for tool '${toolName}'`); - return textCallTemplate.content; - } else if (textCallTemplate.file_path) { - let filePath = path.resolve(textCallTemplate.file_path); - if (!path.isAbsolute(textCallTemplate.file_path) && caller.root_dir) { - filePath = path.resolve(caller.root_dir, textCallTemplate.file_path); - } - - this._log_info(`Reading content from '${filePath}' for tool '${toolName}'`); - - try { - const content = await fs.readFile(filePath, 'utf-8'); - return content; - } catch (error: any) { - if (error.code === 'ENOENT') { - this._log_error(`File not found for tool '${toolName}': ${filePath}`); - } - throw error; - } - } else { - throw new Error('Either file_path or content must be provided in TextCallTemplate for tool calls'); - } + this._log_info(`Returning direct content for tool '${toolName}'`); + return textCallTemplate.content; } /** @@ -170,7 +108,11 @@ export class TextCommunicationProtocol implements CommunicationProtocol { yield result; } + /** + * REQUIRED + * Close the protocol connection (no-op for text protocol). + */ public async close(): Promise { - this._log_info('Text Communication Protocol closed (no-op).'); + // No cleanup needed for text protocol } -} \ No newline at end of file +} diff --git a/packages/text/tsconfig.json b/packages/text/tsconfig.json index 4ad43aa..d94ef45 100644 --- a/packages/text/tsconfig.json +++ b/packages/text/tsconfig.json @@ -1,4 +1,3 @@ -// packages/http/tsconfig.json { "extends": "../../tsconfig.json", "compilerOptions": { @@ -6,17 +5,10 @@ "rootDir": "./src", "composite": true }, - "include": [ - "src/**/*" - ], - "exclude": [ - "node_modules", - "dist", - "**/*.test.ts", - "**/*.spec.ts" - ], + "include": ["src/**/*"], + "exclude": ["node_modules", "dist"], "references": [ { "path": "../core" }, - { "path": "../http" } + { "path": "../http" } ] -} \ No newline at end of file +} diff --git a/packages/text/tsup.config.ts b/packages/text/tsup.config.ts index 165c8e4..7436d15 100644 --- a/packages/text/tsup.config.ts +++ b/packages/text/tsup.config.ts @@ -13,5 +13,5 @@ export default defineConfig({ sourcemap: true, clean: true, outDir: 'dist', - external: ['@utcp/sdk'], + external: ['@utcp/sdk', '@utcp/http', 'js-yaml'], }); diff --git a/scripts/update-versions.js b/scripts/update-versions.js index 61b99c6..8e1fffe 100644 --- a/scripts/update-versions.js +++ b/scripts/update-versions.js @@ -2,13 +2,13 @@ /** * Update versions across all packages in the monorepo - * Usage: node scripts/update-versions.js [major|minor|patch] [version] + * Usage: node scripts/update-versions.js [major|minor|patch|set] [version] * * Examples: - * node scripts/update-versions.js patch # Bump patch version (1.0.0 -> 1.0.1) - * node scripts/update-versions.js minor # Bump minor version (1.0.0 -> 1.1.0) - * node scripts/update-versions.js major # Bump major version (1.0.0 -> 2.0.0) - * node scripts/update-versions.js set 1.2.3 # Set specific version + * node scripts/update-versions.js patch # Bump each package patch version independently + * node scripts/update-versions.js minor # Bump each package minor version independently + * node scripts/update-versions.js major # Bump each package major version independently + * node scripts/update-versions.js set 1.2.3 # Set all packages to same specific version */ import fs from 'fs'; @@ -22,7 +22,7 @@ const PACKAGES_DIR = path.resolve(__dirname, '../packages'); const ROOT_DIR = path.resolve(__dirname, '..'); // Package directories -const PACKAGES = ['core', 'http', 'mcp', 'text', 'cli', 'direct-call']; +const PACKAGES = ['core', 'http', 'mcp', 'text', 'file', 'cli', 'direct-call', 'dotenv-loader']; /** * Parse semantic version string @@ -70,7 +70,7 @@ function bumpVersion(currentVersion, bumpType) { /** * Update package.json version */ -function updatePackageVersion(packagePath, newVersion) { +function updatePackageVersion(packagePath, newVersion, bumpType = null) { const packageJsonPath = path.join(packagePath, 'package.json'); if (!fs.existsSync(packageJsonPath)) { @@ -81,11 +81,14 @@ function updatePackageVersion(packagePath, newVersion) { const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); const oldVersion = packageJson.version; - packageJson.version = newVersion; + // If bumpType is provided, calculate new version from current version + const finalVersion = bumpType ? bumpVersion(oldVersion, bumpType) : newVersion; + + packageJson.version = finalVersion; fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n'); - return { name: packageJson.name, oldVersion, newVersion }; + return { name: packageJson.name, oldVersion, newVersion: finalVersion }; } /** @@ -139,22 +142,16 @@ function main() { if (args.length === 0) { console.error('❌ Usage: node scripts/update-versions.js [major|minor|patch|set] [version?]'); console.error('\nExamples:'); - console.error(' node scripts/update-versions.js patch'); - console.error(' node scripts/update-versions.js minor'); - console.error(' node scripts/update-versions.js major'); - console.error(' node scripts/update-versions.js set 1.2.3'); + console.error(' node scripts/update-versions.js patch # Bump each package by one patch'); + console.error(' node scripts/update-versions.js minor # Bump each package by one minor'); + console.error(' node scripts/update-versions.js major # Bump each package by one major'); + console.error(' node scripts/update-versions.js set 1.2.3 # Set all packages to same version'); process.exit(1); } const command = args[0]; let newVersion; - - // Get current version from core package - const corePackagePath = path.join(PACKAGES_DIR, 'core', 'package.json'); - const corePackage = JSON.parse(fs.readFileSync(corePackagePath, 'utf8')); - const currentVersion = corePackage.version; - - console.log(`📦 Current version: ${currentVersion}\n`); + let bumpType = null; if (command === 'set') { if (args.length < 2) { @@ -171,16 +168,17 @@ function main() { console.error(`❌ ${error.message}`); process.exit(1); } + + console.log(`🎯 Setting all packages to version: ${newVersion}\n`); } else if (['major', 'minor', 'patch'].includes(command)) { - newVersion = bumpVersion(currentVersion, command); + bumpType = command; + console.log(`🎯 Bumping each package by one ${command} version\n`); } else { console.error(`❌ Unknown command: ${command}`); console.error(' Valid commands: major, minor, patch, set'); process.exit(1); } - console.log(`🎯 New version: ${newVersion}\n`); - // Update all package versions const updates = []; @@ -188,7 +186,8 @@ function main() { for (const pkg of PACKAGES) { const packagePath = path.join(PACKAGES_DIR, pkg); - const result = updatePackageVersion(packagePath, newVersion); + // Pass bumpType for incremental bumps, or newVersion for set command + const result = updatePackageVersion(packagePath, newVersion, bumpType); if (result) { updates.push(result); @@ -207,12 +206,11 @@ function main() { console.log('\n✨ Version update complete!'); console.log(`\n📋 Summary:`); - console.log(` Old version: ${currentVersion}`); - console.log(` New version: ${newVersion}`); console.log(` Updated ${updates.length} packages`); + updates.forEach(u => console.log(` - ${u.name}: ${u.oldVersion} -> ${u.newVersion}`)); console.log('\n💡 Next steps:'); console.log(' 1. Review the changes: git diff'); - console.log(' 2. Commit the changes: git commit -am "chore: bump version to ${newVersion}"'); + console.log(' 2. Commit the changes: git commit -am "chore: bump package versions"'); console.log(' 3. Build the packages: bun run build'); console.log(' 4. Publish: bun run publish:all'); } diff --git a/tests/utcp_client.test.ts b/tests/utcp_client.test.ts index f08b67f..8415098 100644 --- a/tests/utcp_client.test.ts +++ b/tests/utcp_client.test.ts @@ -5,9 +5,15 @@ import path from "path"; import { writeFile, unlink } from "fs/promises"; import { UtcpClient } from "@utcp/sdk"; -import { McpCallTemplate } from "@utcp/mcp"; -import { HttpCallTemplate } from "@utcp/http"; -import { TextCallTemplate } from "@utcp/text"; +// Import protocol packages to register their serializers and communication protocols +import "@utcp/http"; +import "@utcp/file"; +import "@utcp/mcp"; +import "@utcp/dotenv-loader"; +// Import types after registering the packages +import type { McpCallTemplate } from "@utcp/mcp"; +import type { HttpCallTemplate } from "@utcp/http"; +import type { FileCallTemplate } from "@utcp/file"; let httpManualServerProcess: Subprocess | null = null; let mcpStdioServerProcess: Subprocess | null = null; @@ -181,10 +187,10 @@ describe("UtcpClient End-to-End Tests", () => { outputs: { type: 'object', properties: { content: { type: 'string' } } }, tags: ["file", "io", "dummy"], tool_call_template: { - name: "text_manual", - call_template_type: "text", + name: "file_manual", + call_template_type: "file", file_path: dummyTextFilePath - } as TextCallTemplate + } as FileCallTemplate }] }; const textManualConfigPath = path.join(import.meta.dir, "test_text_manual.json"); @@ -201,10 +207,10 @@ describe("UtcpClient End-to-End Tests", () => { url: "http://localhost:9998/utcp", } as HttpCallTemplate, { - name: "local_text_manual", - call_template_type: "text", + name: "local_file_manual", + call_template_type: "file", file_path: textManualConfigPath, - } as TextCallTemplate, + } as FileCallTemplate, { name: "mcp_stdio_client_manual", call_template_type: "mcp", @@ -229,12 +235,12 @@ describe("UtcpClient End-to-End Tests", () => { // Expected tools: 1 from HTTP, 1 from Text, 2 from MCP = 4 tools expect(allTools.length).toBe(4); const httpTool = await client.config.tool_repository.getTool("http_server_manual.get_user"); - const textTool = await client.config.tool_repository.getTool("local_text_manual.read_dummy_file"); + const fileTool = await client.config.tool_repository.getTool("local_file_manual.read_dummy_file"); const mcpEchoTool = await client.config.tool_repository.getTool("mcp_stdio_client_manual.mock_stdio_server.echo"); const mcpAddTool = await client.config.tool_repository.getTool("mcp_stdio_client_manual.mock_stdio_server.add"); expect(httpTool).toBeDefined(); - expect(textTool).toBeDefined(); + expect(fileTool).toBeDefined(); expect(mcpEchoTool).toBeDefined(); expect(mcpAddTool).toBeDefined(); @@ -244,10 +250,10 @@ describe("UtcpClient End-to-End Tests", () => { expect(httpResult).toEqual({ id: 123, name: "Alice" }); console.log(`[Test] HTTP tool result: ${JSON.stringify(httpResult)}`); - console.log("\n[Test] Calling Text tool..."); - const textResult = await client.callTool("local_text_manual.read_dummy_file", {}); - expect(textResult).toBe("This is dummy content for the text file tool."); - console.log(`[Test] Text tool result: "${textResult.substring(0, 30)}..."`); + console.log("\n[Test] Calling File tool..."); + const fileResult = await client.callTool("local_file_manual.read_dummy_file", {}); + expect(fileResult).toBe("This is dummy content for the text file tool."); + console.log(`[Test] File tool result: "${fileResult.substring(0, 30)}..."`); console.log("\n[Test] Calling MCP tool..."); @@ -346,8 +352,9 @@ describe("UtcpClient End-to-End Tests", () => { const searchResults = await client.searchTools("echo", 5); // 3. Assert: Verify the search results + // The search should return the "echo" tool as the first (highest scored) result console.log(`[Test] Search results for "echo": ${searchResults.map(t => t.name).join(', ')}`); - expect(searchResults.length).toBe(1); + expect(searchResults.length).toBeGreaterThan(0); expect(searchResults[0]?.name).toBe("mcp_search_manual.mock_stdio_server.echo"); await client.close();