diff --git a/README.md b/README.md index a674833..870eea2 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ A Model Context Protocol (MCP) server for Genesys Cloud's Platform API. ## Features | Tool | Description | -| ----------------------------------------------------------------------------- | ------------------------------------------------------------------------ | +|-------------------------------------------------------------------------------|--------------------------------------------------------------------------| | [Search Queues](/docs/tools.md#search-queues) | Searches for queues by their name (supports wildcards) | | [Query Queue Volumes](/docs/tools.md#query-queue-volumes) | Retrieves conversation volumes and member count by Queue IDs | | [Sample Conversations By Queue](/docs/tools.md#sample-conversations-by-queue) | Retrieves a representative sample of Conversation IDs for a Queue ID | @@ -17,6 +17,8 @@ A Model Context Protocol (MCP) server for Genesys Cloud's Platform API. | [Conversation Topics](/docs/tools.md#conversation-topics) | Retrieves the topics for a conversation by ID | | [Search Voice Conversation](/docs/tools.md#search-voice-conversations) | Searches voice conversations by optional criteria | | [Conversation Transcript](/docs/tools.md#conversation-transcript) | Retrieves conversation transcript | +| [OAuth Clients](/docs/tools.md#oauth-clients) | Retrieves a list of all the OAuth clients | +| [OAuth Client Usage](/docs/tools.md#oauth-client-usage) | Retrieves usage of an OAuth client | ## Usage with Claude Desktop diff --git a/docs/tools.md b/docs/tools.md index e351dfe..bf6513a 100644 --- a/docs/tools.md +++ b/docs/tools.md @@ -30,7 +30,7 @@ Platform API endpoint used: **Tool name:** `query_queue_volumes` -Returns a breakdown of how many conversations occurred in each specified queue between two dates. Useful for comparing workload across queues. +Returns a breakdown of how many conversations occurred in each specified queue between two dates. Useful for comparing workload across queues. MAX 300 queue IDs. [Source file](/src/tools/queryQueueVolumes/queryQueueVolumes.ts). @@ -215,3 +215,53 @@ Platform API endpoints used: - [GET /api/v2/conversations/{conversationId}/recordings](https://developer.genesys.cloud/devapps/api-explorer-standalone#get-api-v2-conversations--conversationId--recordings) - [GET /api/v2/speechandtextanalytics/conversations/{conversationId}/communications/{communicationId}/transcripturl](https://developer.genesys.cloud/devapps/api-explorer-standalone#get-api-v2-speechandtextanalytics-conversations--conversationId--communications--communicationId--transcripturl) + +## OAuth Clients + +**Tool name:** `oauth_clients` + +Retrieves a list of all OAuth clients, including their associated roles and divisions. This tool is useful for auditing and managing OAuth clients in the Genesys Cloud organization. + +[Source file](/src/tools/oauthClients/oauthClients.ts). + +### Security + +Required Permissions: + +- `oauth:client:view` +- `authorization:role:view` + - Optional: Used to populate names of roles used by OAuth Client + +Platform API endpoints used: + +- [GET /api/v2/oauth/clients](https://developer.genesys.cloud/devapps/api-explorer-standalone#get-api-v2-oauth-clients) +- [GET /api/v2/authorization/divisions](https://developer.genesys.cloud/devapps/api-explorer-standalone#get-api-v2-authorization-divisions) +- [GET /api/v2/authorization/roles](https://developer.genesys.cloud/devapps/api-explorer-standalone#get-api-v2-authorization-roles) + +## OAuth Client Usage + +**Tool name:** `oauth_client_usage` + +Retrieves the usage of an OAuth Client for a given period. It returns the total number of requests and a breakdown of requests per organization. + +[Source file](/src/tools/oauthClientUsage/oauthClientUsage.ts). + +### Input + +- `oauthClientId` + - The UUID of the OAuth Client to retrieve the usage for (e.g., 00000000-0000-0000-0000-000000000000) +- `startDate` + - The start date/time in ISO-8601 format (e.g., '2024-01-01T00:00:00Z') +- `endDate` + - The end date/time in ISO-8601 format (e.g., '2024-01-07T23:59:59Z') + +### Security + +Required Permissions: + +- `usage:client:view` + +Platform API endpoints used: + +- [POST /api/v2/oauth/clients/{clientId}/usage/query](https://developer.genesys.cloud/devapps/api-explorer-standalone#post-api-v2-oauth-clients--clientId--usage-query) +- [GET /api/v2/oauth/clients/{clientId}/usage/query/results/{executionId}](https://developer.genesys.cloud/devapps/api-explorer-standalone#get-api-v2-oauth-clients--clientId--usage-query-results--executionId-) diff --git a/package-lock.json b/package-lock.json index 868b87f..e1289fe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,29 +1,29 @@ { "name": "@makingchatbots/genesys-cloud-mcp-server", - "version": "0.0.16", + "version": "1.0.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@makingchatbots/genesys-cloud-mcp-server", - "version": "0.0.16", + "version": "1.0.1", "license": "MIT", "dependencies": { "@modelcontextprotocol/sdk": "^1.18.2", "date-fns": "^4.1.0", - "purecloud-platform-client-v2": "^231.0.0", + "purecloud-platform-client-v2": "^239.0.0", "zod": "^3.23.8" }, "bin": { "genesys-cloud-mcp-server": "dist/cli.js" }, "devDependencies": { - "@anthropic-ai/mcpb": "^1.1.1", + "@anthropic-ai/mcpb": "^2.0.1", "@biomejs/biome": "2.2.4", "@google/genai": "^1.12.0", - "@types/node": "^22.16.4", + "@types/node": "^18.19.130", "dotenv": "^17.2.2", - "lint-staged": "^16.1.2", + "lint-staged": "^16.2.7", "tsx": "^4.20.6", "typescript": "^5.8.3", "vitest": "^3.2.4" @@ -33,9 +33,9 @@ } }, "node_modules/@anthropic-ai/mcpb": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@anthropic-ai/mcpb/-/mcpb-1.1.1.tgz", - "integrity": "sha512-a8R5pQcPPwUfuswR2of4tHDd/NJHv1L6mKJ97hfqE/gZq/xaqL12mDUtQVcyl3g1BV8csi9JOpyf15jLvfIXvQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@anthropic-ai/mcpb/-/mcpb-2.0.1.tgz", + "integrity": "sha512-ktKzkt7w41MMqaVXt7E99dVPYPE5X5Glz32X2XEiVr14TYv0g+40/nbmIlM4gaAu5EWsWSWElAcyXENKV6t+lQ==", "dev": true, "license": "MIT", "dependencies": { @@ -46,7 +46,8 @@ "ignore": "^7.0.5", "node-forge": "^1.3.1", "pretty-bytes": "^5.6.0", - "zod": "^3.25.67" + "zod": "^3.25.67", + "zod-to-json-schema": "^3.24.6" }, "bin": { "mcpb": "dist/cli/cli.js" @@ -771,6 +772,15 @@ "node": ">=18" } }, + "node_modules/@inquirer/core/node_modules/@types/node": { + "version": "22.19.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.1.tgz", + "integrity": "sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ==", + "dev": true, + "dependencies": { + "undici-types": "~6.21.0" + } + }, "node_modules/@inquirer/core/node_modules/ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -842,6 +852,12 @@ "node": ">=8" } }, + "node_modules/@inquirer/core/node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true + }, "node_modules/@inquirer/core/node_modules/wrap-ansi": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", @@ -1086,280 +1102,286 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.45.1.tgz", - "integrity": "sha512-NEySIFvMY0ZQO+utJkgoMiCAjMrGvnbDLHvcmlA33UXJpYBCvlBEbMMtV837uCkS+plG2umfhn0T5mMAxGrlRA==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.3.tgz", + "integrity": "sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==", "cpu": [ "arm" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.45.1.tgz", - "integrity": "sha512-ujQ+sMXJkg4LRJaYreaVx7Z/VMgBBd89wGS4qMrdtfUFZ+TSY5Rs9asgjitLwzeIbhwdEhyj29zhst3L1lKsRQ==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.3.tgz", + "integrity": "sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.45.1.tgz", - "integrity": "sha512-FSncqHvqTm3lC6Y13xncsdOYfxGSLnP+73k815EfNmpewPs+EyM49haPS105Rh4aF5mJKywk9X0ogzLXZzN9lA==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.3.tgz", + "integrity": "sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.45.1.tgz", - "integrity": "sha512-2/vVn/husP5XI7Fsf/RlhDaQJ7x9zjvC81anIVbr4b/f0xtSmXQTFcGIQ/B1cXIYM6h2nAhJkdMHTnD7OtQ9Og==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.3.tgz", + "integrity": "sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.45.1.tgz", - "integrity": "sha512-4g1kaDxQItZsrkVTdYQ0bxu4ZIQ32cotoQbmsAnW1jAE4XCMbcBPDirX5fyUzdhVCKgPcrwWuucI8yrVRBw2+g==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.3.tgz", + "integrity": "sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "freebsd" ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.45.1.tgz", - "integrity": "sha512-L/6JsfiL74i3uK1Ti2ZFSNsp5NMiM4/kbbGEcOCps99aZx3g8SJMO1/9Y0n/qKlWZfn6sScf98lEOUe2mBvW9A==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.3.tgz", + "integrity": "sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "freebsd" ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.45.1.tgz", - "integrity": "sha512-RkdOTu2jK7brlu+ZwjMIZfdV2sSYHK2qR08FUWcIoqJC2eywHbXr0L8T/pONFwkGukQqERDheaGTeedG+rra6Q==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.3.tgz", + "integrity": "sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==", "cpu": [ "arm" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.45.1.tgz", - "integrity": "sha512-3kJ8pgfBt6CIIr1o+HQA7OZ9mp/zDk3ctekGl9qn/pRBgrRgfwiffaUmqioUGN9hv0OHv2gxmvdKOkARCtRb8Q==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.3.tgz", + "integrity": "sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==", "cpu": [ "arm" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.45.1.tgz", - "integrity": "sha512-k3dOKCfIVixWjG7OXTCOmDfJj3vbdhN0QYEqB+OuGArOChek22hn7Uy5A/gTDNAcCy5v2YcXRJ/Qcnm4/ma1xw==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.3.tgz", + "integrity": "sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.45.1.tgz", - "integrity": "sha512-PmI1vxQetnM58ZmDFl9/Uk2lpBBby6B6rF4muJc65uZbxCs0EA7hhKCk2PKlmZKuyVSHAyIw3+/SiuMLxKxWog==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.3.tgz", + "integrity": "sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, - "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.45.1.tgz", - "integrity": "sha512-9UmI0VzGmNJ28ibHW2GpE2nF0PBQqsyiS4kcJ5vK+wuwGnV5RlqdczVocDSUfGX/Na7/XINRVoUgJyFIgipoRg==", + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.3.tgz", + "integrity": "sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==", "cpu": [ "loong64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.45.1.tgz", - "integrity": "sha512-7nR2KY8oEOUTD3pBAxIBBbZr0U7U+R9HDTPNy+5nVVHDXI4ikYniH1oxQz9VoB5PbBU1CZuDGHkLJkd3zLMWsg==", + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.3.tgz", + "integrity": "sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==", "cpu": [ "ppc64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.45.1.tgz", - "integrity": "sha512-nlcl3jgUultKROfZijKjRQLUu9Ma0PeNv/VFHkZiKbXTBQXhpytS8CIj5/NfBeECZtY2FJQubm6ltIxm/ftxpw==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.3.tgz", + "integrity": "sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==", "cpu": [ "riscv64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.45.1.tgz", - "integrity": "sha512-HJV65KLS51rW0VY6rvZkiieiBnurSzpzore1bMKAhunQiECPuxsROvyeaot/tcK3A3aGnI+qTHqisrpSgQrpgA==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.3.tgz", + "integrity": "sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==", "cpu": [ "riscv64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.45.1.tgz", - "integrity": "sha512-NITBOCv3Qqc6hhwFt7jLV78VEO/il4YcBzoMGGNxznLgRQf43VQDae0aAzKiBeEPIxnDrACiMgbqjuihx08OOw==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.3.tgz", + "integrity": "sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==", "cpu": [ "s390x" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.45.1.tgz", - "integrity": "sha512-+E/lYl6qu1zqgPEnTrs4WysQtvc/Sh4fC2nByfFExqgYrqkKWp1tWIbe+ELhixnenSpBbLXNi6vbEEJ8M7fiHw==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.3.tgz", + "integrity": "sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.45.1.tgz", - "integrity": "sha512-a6WIAp89p3kpNoYStITT9RbTbTnqarU7D8N8F2CV+4Cl9fwCOZraLVuVFvlpsW0SbIiYtEnhCZBPLoNdRkjQFw==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.3.tgz", + "integrity": "sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.3.tgz", + "integrity": "sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openharmony" + ] + }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.45.1.tgz", - "integrity": "sha512-T5Bi/NS3fQiJeYdGvRpTAP5P02kqSOpqiopwhj0uaXB6nzs5JVi2XMJb18JUSKhCOX8+UE1UKQufyD6Or48dJg==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.3.tgz", + "integrity": "sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.45.1.tgz", - "integrity": "sha512-lxV2Pako3ujjuUe9jiU3/s7KSrDfH6IgTSQOnDWr9aJ92YsFd7EurmClK0ly/t8dzMkDtd04g60WX6yl0sGfdw==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.3.tgz", + "integrity": "sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==", "cpu": [ "ia32" ], "dev": true, - "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.3.tgz", + "integrity": "sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==", + "cpu": [ + "x64" + ], + "dev": true, "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.45.1.tgz", - "integrity": "sha512-M/fKi4sasCdM8i0aWJjCSFm2qEnYRR8AMLG2kxp6wD13+tMGA4Z1tVAuHkNRjud5SW2EM3naLuK35w9twvf6aA==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.3.tgz", + "integrity": "sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "win32" @@ -1400,13 +1422,12 @@ } }, "node_modules/@types/node": { - "version": "22.16.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.16.4.tgz", - "integrity": "sha512-PYRhNtZdm2wH/NT2k/oAJ6/f2VD2N2Dag0lGlx2vWgMSJXGNmlce5MiTQzoWAiIJtso30mjnfQCOKVH+kAQC/g==", + "version": "18.19.130", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.130.tgz", + "integrity": "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==", "dev": true, - "license": "MIT", "dependencies": { - "undici-types": "~6.21.0" + "undici-types": "~5.26.4" } }, "node_modules/@types/triple-beam": { @@ -1438,33 +1459,6 @@ "url": "https://opencollective.com/vitest" } }, - "node_modules/@vitest/mocker": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.4.tgz", - "integrity": "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/spy": "3.2.4", - "estree-walker": "^3.0.3", - "magic-string": "^0.30.17" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "msw": "^2.4.9", - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" - }, - "peerDependenciesMeta": { - "msw": { - "optional": true - }, - "vite": { - "optional": true - } - } - }, "node_modules/@vitest/pretty-format": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz", @@ -1576,9 +1570,9 @@ } }, "node_modules/ansi-escapes": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz", - "integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.2.0.tgz", + "integrity": "sha512-g6LhBsl+GBPRWGWsBtutpzBYuIIdBkLEvad5C/va/74Db018+5TZiyA26cZJAr3Rft5lprVqOIPxf5Vid6tqAw==", "dev": true, "license": "MIT", "dependencies": { @@ -1592,9 +1586,9 @@ } }, "node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "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": { @@ -1683,23 +1677,27 @@ } }, "node_modules/body-parser": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", - "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.1.tgz", + "integrity": "sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw==", "license": "MIT", "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", - "debug": "^4.4.0", + "debug": "^4.4.3", "http-errors": "^2.0.0", - "iconv-lite": "^0.6.3", + "iconv-lite": "^0.7.0", "on-finished": "^2.4.1", "qs": "^6.14.0", - "raw-body": "^3.0.0", - "type-is": "^2.0.0" + "raw-body": "^3.0.1", + "type-is": "^2.0.1" }, "engines": { "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/braces": { @@ -1821,17 +1819,17 @@ } }, "node_modules/cli-truncate": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", - "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-5.1.1.tgz", + "integrity": "sha512-SroPvNHxUnk+vIW/dOSfNqdy1sPEFkrTk6TUtqLCnBlo3N7TNYYkzzN7uSD6+jVjrdO4+p8nH7JzH6cIvUem6A==", "dev": true, "license": "MIT", "dependencies": { - "slice-ansi": "^5.0.0", - "string-width": "^7.0.0" + "slice-ansi": "^7.1.0", + "string-width": "^8.0.0" }, "engines": { - "node": ">=18" + "node": ">=20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -1925,9 +1923,9 @@ } }, "node_modules/commander": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.0.tgz", - "integrity": "sha512-2uM9rYjPvyq39NwLRqaiLtWHyDC1FvryJDa2ATTVims5YAS4PupsEQsDvP14FqhFr0P49CYDugi59xaxJlTXRA==", + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.2.tgz", + "integrity": "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==", "dev": true, "license": "MIT", "engines": { @@ -2019,9 +2017,9 @@ } }, "node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -2106,9 +2104,9 @@ "license": "MIT" }, "node_modules/emoji-regex": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", - "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", "dev": true, "license": "MIT" }, @@ -2242,7 +2240,6 @@ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", "dev": true, - "license": "MIT", "dependencies": { "@types/estree": "^1.0.0" } @@ -2617,9 +2614,9 @@ } }, "node_modules/get-east-asian-width": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz", - "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz", + "integrity": "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==", "dev": true, "license": "MIT", "engines": { @@ -2779,19 +2776,23 @@ } }, "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", "license": "MIT", "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" }, "engines": { "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/https-proxy-agent": { @@ -2809,15 +2810,19 @@ } }, "node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", + "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/inherits": { @@ -2841,13 +2846,16 @@ "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" }, "node_modules/is-fullwidth-code-point": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", - "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz", + "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==", "dev": true, "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.3.1" + }, "engines": { - "node": ">=12" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -2950,36 +2958,20 @@ "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" }, - "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/lint-staged": { - "version": "16.1.2", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-16.1.2.tgz", - "integrity": "sha512-sQKw2Si2g9KUZNY3XNvRuDq4UJqpHwF0/FQzZR2M7I5MvtpWvibikCjUVJzZdGE0ByurEl3KQNvsGetd1ty1/Q==", + "version": "16.2.7", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-16.2.7.tgz", + "integrity": "sha512-lDIj4RnYmK7/kXMya+qJsmkRFkGolciXjrsZ6PC25GdTfWOAWetR0ZbsNXRAj1EHHImRSalc+whZFg56F5DVow==", "dev": true, "license": "MIT", "dependencies": { - "chalk": "^5.4.1", - "commander": "^14.0.0", - "debug": "^4.4.1", - "lilconfig": "^3.1.3", - "listr2": "^8.3.3", + "commander": "^14.0.2", + "listr2": "^9.0.5", "micromatch": "^4.0.8", - "nano-spawn": "^1.0.2", + "nano-spawn": "^2.0.0", "pidtree": "^0.6.0", "string-argv": "^0.3.2", - "yaml": "^2.8.0" + "yaml": "^2.8.1" }, "bin": { "lint-staged": "bin/lint-staged.js" @@ -2991,27 +2983,14 @@ "url": "https://opencollective.com/lint-staged" } }, - "node_modules/lint-staged/node_modules/chalk": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", - "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, "node_modules/listr2": { - "version": "8.3.3", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.3.3.tgz", - "integrity": "sha512-LWzX2KsqcB1wqQ4AHgYb4RsDXauQiqhjLk+6hjbaeHG4zpjjVAB6wC/gz6X0l+Du1cN3pUB5ZlrvTbhGSNnUQQ==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-9.0.5.tgz", + "integrity": "sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==", "dev": true, "license": "MIT", "dependencies": { - "cli-truncate": "^4.0.0", + "cli-truncate": "^5.0.0", "colorette": "^2.0.20", "eventemitter3": "^5.0.1", "log-update": "^6.1.0", @@ -3019,7 +2998,7 @@ "wrap-ansi": "^9.0.0" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/log-update": { @@ -3042,52 +3021,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/log-update/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/log-update/node_modules/is-fullwidth-code-point": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz", - "integrity": "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==", - "dev": true, - "license": "MIT", - "dependencies": { - "get-east-asian-width": "^1.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-update/node_modules/slice-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.0.tgz", - "integrity": "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.2.1", - "is-fullwidth-code-point": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, "node_modules/logform": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz", @@ -3235,9 +3168,9 @@ } }, "node_modules/nano-spawn": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/nano-spawn/-/nano-spawn-1.0.2.tgz", - "integrity": "sha512-21t+ozMQDAL/UGgQVBbZ/xXvNO10++ZPuTmKRO8k9V3AClVRht49ahtDjfY8l1q6nSHOrE5ASfthzH3ol6R/hg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/nano-spawn/-/nano-spawn-2.0.0.tgz", + "integrity": "sha512-tacvGzUY5o2D8CBh2rrwxyNojUsZNU2zjNTzKQrkgGJQTbGAfArVWXSKMBokBeeg6C7OLRGUEyoFlYbfeWQIqw==", "dev": true, "license": "MIT", "engines": { @@ -3258,7 +3191,6 @@ "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -3297,9 +3229,9 @@ } }, "node_modules/node-forge": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", - "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.2.tgz", + "integrity": "sha512-6xKiQ+cph9KImrRh0VsjH2d8/GXA4FIMlgU4B757iI1ApvcyA9VlouP0yZJha01V+huImO+kKMU7ih+2+E14fw==", "dev": true, "license": "(BSD-3-Clause OR GPL-2.0)", "engines": { @@ -3430,8 +3362,7 @@ "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" + "dev": true }, "node_modules/picomatch": { "version": "2.3.1", @@ -3487,7 +3418,6 @@ "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -3538,9 +3468,9 @@ } }, "node_modules/purecloud-platform-client-v2": { - "version": "231.0.0", - "resolved": "https://registry.npmjs.org/purecloud-platform-client-v2/-/purecloud-platform-client-v2-231.0.0.tgz", - "integrity": "sha512-1KSja1kZH53rzlOBsIC0CkSY0knF4PbDr+YslL447azfThkcQB+pP3H6zchaRjZM70QsjSiFozl+vmpNge/m9w==", + "version": "239.0.0", + "resolved": "https://registry.npmjs.org/purecloud-platform-client-v2/-/purecloud-platform-client-v2-239.0.0.tgz", + "integrity": "sha512-qSABlrj12NMtcuup4pzuK46fIIFQZfK28S7rSmdOIwER83jrbv0MtK/rLtrl7QTIsyfjjyIsriLbKao/ORpZhw==", "license": "MIT", "dependencies": { "axios": "^1.11.0", @@ -3574,18 +3504,18 @@ } }, "node_modules/raw-body": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz", - "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", "license": "MIT", "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.6.3", - "unpipe": "1.0.0" + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" }, "engines": { - "node": ">= 0.8" + "node": ">= 0.10" } }, "node_modules/readable-stream": { @@ -3636,11 +3566,10 @@ "license": "MIT" }, "node_modules/rollup": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.45.1.tgz", - "integrity": "sha512-4iya7Jb76fVpQyLoiVpzUrsjQ12r3dM7fIVz+4NwoYvZOShknRmiv+iu9CClZml5ZLGb0XMcYLutK6w9tgxHDw==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.3.tgz", + "integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==", "dev": true, - "license": "MIT", "dependencies": { "@types/estree": "1.0.8" }, @@ -3652,26 +3581,28 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.45.1", - "@rollup/rollup-android-arm64": "4.45.1", - "@rollup/rollup-darwin-arm64": "4.45.1", - "@rollup/rollup-darwin-x64": "4.45.1", - "@rollup/rollup-freebsd-arm64": "4.45.1", - "@rollup/rollup-freebsd-x64": "4.45.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.45.1", - "@rollup/rollup-linux-arm-musleabihf": "4.45.1", - "@rollup/rollup-linux-arm64-gnu": "4.45.1", - "@rollup/rollup-linux-arm64-musl": "4.45.1", - "@rollup/rollup-linux-loongarch64-gnu": "4.45.1", - "@rollup/rollup-linux-powerpc64le-gnu": "4.45.1", - "@rollup/rollup-linux-riscv64-gnu": "4.45.1", - "@rollup/rollup-linux-riscv64-musl": "4.45.1", - "@rollup/rollup-linux-s390x-gnu": "4.45.1", - "@rollup/rollup-linux-x64-gnu": "4.45.1", - "@rollup/rollup-linux-x64-musl": "4.45.1", - "@rollup/rollup-win32-arm64-msvc": "4.45.1", - "@rollup/rollup-win32-ia32-msvc": "4.45.1", - "@rollup/rollup-win32-x64-msvc": "4.45.1", + "@rollup/rollup-android-arm-eabi": "4.53.3", + "@rollup/rollup-android-arm64": "4.53.3", + "@rollup/rollup-darwin-arm64": "4.53.3", + "@rollup/rollup-darwin-x64": "4.53.3", + "@rollup/rollup-freebsd-arm64": "4.53.3", + "@rollup/rollup-freebsd-x64": "4.53.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.53.3", + "@rollup/rollup-linux-arm-musleabihf": "4.53.3", + "@rollup/rollup-linux-arm64-gnu": "4.53.3", + "@rollup/rollup-linux-arm64-musl": "4.53.3", + "@rollup/rollup-linux-loong64-gnu": "4.53.3", + "@rollup/rollup-linux-ppc64-gnu": "4.53.3", + "@rollup/rollup-linux-riscv64-gnu": "4.53.3", + "@rollup/rollup-linux-riscv64-musl": "4.53.3", + "@rollup/rollup-linux-s390x-gnu": "4.53.3", + "@rollup/rollup-linux-x64-gnu": "4.53.3", + "@rollup/rollup-linux-x64-musl": "4.53.3", + "@rollup/rollup-openharmony-arm64": "4.53.3", + "@rollup/rollup-win32-arm64-msvc": "4.53.3", + "@rollup/rollup-win32-ia32-msvc": "4.53.3", + "@rollup/rollup-win32-x64-gnu": "4.53.3", + "@rollup/rollup-win32-x64-msvc": "4.53.3", "fsevents": "~2.3.2" } }, @@ -3889,26 +3820,26 @@ } }, "node_modules/slice-ansi": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", - "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.2.tgz", + "integrity": "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^6.0.0", - "is-fullwidth-code-point": "^4.0.0" + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" }, "engines": { - "node": ">=12" + "node": ">=18" }, "funding": { "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, "node_modules/slice-ansi/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "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": { @@ -3923,7 +3854,6 @@ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "dev": true, - "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -3943,9 +3873,9 @@ "dev": true }, "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -3976,27 +3906,26 @@ } }, "node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.1.0.tgz", + "integrity": "sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg==", "dev": true, "license": "MIT", "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", + "get-east-asian-width": "^1.3.0", "strip-ansi": "^7.1.0" }, "engines": { - "node": ">=18" + "node": ">=20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "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": { @@ -4229,11 +4158,10 @@ } }, "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true, - "license": "MIT" + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true }, "node_modules/universalify": { "version": "2.0.1", @@ -4291,12 +4219,82 @@ "node": ">= 0.8" } }, - "node_modules/vite": { - "version": "7.1.11", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.11.tgz", - "integrity": "sha512-uzcxnSDVjAopEUjljkWh8EIrg6tlzrjFUfMcR1EVsRDGwf/ccef0qQPRyOrROwhrTDaApueq+ja+KLPlzR/zdg==", + "node_modules/vite-node": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz", + "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==", "dev": true, "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.4.1", + "es-module-lexer": "^1.7.0", + "pathe": "^2.0.3", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vite-node/node_modules/@types/node": { + "version": "24.10.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz", + "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/vite-node/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, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/vite-node/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/vite-node/node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "optional": true, + "peer": true + }, + "node_modules/vite-node/node_modules/vite": { + "version": "7.2.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.2.6.tgz", + "integrity": "sha512-tI2l/nFHC5rLh7+5+o7QjKjSR04ivXDF4jcgV0f/bTQ+OJiITy5S6gaynVsEM+7RqzufMnVbIon6Sr5x1SDYaQ==", + "dev": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", @@ -4366,60 +4364,6 @@ } } }, - "node_modules/vite-node": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz", - "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cac": "^6.7.14", - "debug": "^4.4.1", - "es-module-lexer": "^1.7.0", - "pathe": "^2.0.3", - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" - }, - "bin": { - "vite-node": "vite-node.mjs" - }, - "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/vite/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/vite/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/vitest": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz", @@ -4493,12 +4437,54 @@ } } }, + "node_modules/vitest/node_modules/@vitest/mocker": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.4.tgz", + "integrity": "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==", + "dev": true, + "dependencies": { + "@vitest/spy": "3.2.4", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.17" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/vitest/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, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, "node_modules/vitest/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "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" }, @@ -4506,6 +4492,80 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/vitest/node_modules/vite": { + "version": "7.2.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.2.6.tgz", + "integrity": "sha512-tI2l/nFHC5rLh7+5+o7QjKjSR04ivXDF4jcgV0f/bTQ+OJiITy5S6gaynVsEM+7RqzufMnVbIon6Sr5x1SDYaQ==", + "dev": true, + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", @@ -4590,9 +4650,9 @@ } }, "node_modules/wrap-ansi": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", - "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", "dev": true, "license": "MIT", "dependencies": { @@ -4608,9 +4668,9 @@ } }, "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "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": { @@ -4620,6 +4680,24 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -4649,9 +4727,9 @@ } }, "node_modules/yaml": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.0.tgz", - "integrity": "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==", + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", + "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", "dev": true, "license": "ISC", "bin": { @@ -4659,6 +4737,9 @@ }, "engines": { "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" } }, "node_modules/yoctocolors-cjs": { @@ -4684,12 +4765,12 @@ } }, "node_modules/zod-to-json-schema": { - "version": "3.24.5", - "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.5.tgz", - "integrity": "sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==", + "version": "3.25.0", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.0.tgz", + "integrity": "sha512-HvWtU2UG41LALjajJrML6uQejQhNJx+JBO9IflpSja4R03iNWfKXrj6W2h7ljuLyc1nKS+9yDyL/9tD1U/yBnQ==", "license": "ISC", "peerDependencies": { - "zod": "^3.24.1" + "zod": "^3.25 || ^4" } } } diff --git a/package.json b/package.json index 671fc76..8197cc2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@makingchatbots/genesys-cloud-mcp-server", - "version": "1.0.0", + "version": "1.0.1", "description": "A Model Context Protocol (MCP) server exposing Genesys Cloud tools for LLMs, including sentiment analysis, conversation search, topic detection and more.", "bin": "./dist/cli.js", "type": "module", @@ -39,15 +39,15 @@ "dependencies": { "@modelcontextprotocol/sdk": "^1.18.2", "date-fns": "^4.1.0", - "purecloud-platform-client-v2": "^231.0.0", + "purecloud-platform-client-v2": "^239.0.0", "zod": "^3.23.8" }, "devDependencies": { "dotenv": "^17.2.2", - "@anthropic-ai/mcpb": "^1.1.1", + "@anthropic-ai/mcpb": "^2.0.1", "@google/genai": "^1.12.0", - "@types/node": "^22.16.4", - "lint-staged": "^16.1.2", + "@types/node": "^18.19.130", + "lint-staged": "^16.2.7", "tsx": "^4.20.6", "typescript": "^5.8.3", "vitest": "^3.2.4", diff --git a/src/index.ts b/src/index.ts index cb84187..49c937a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,6 +6,8 @@ import { createConfigRetriever } from "./createConfigRetriever.js"; import { conversationSentiment } from "./tools/conversationSentiment/conversationSentiment.js"; import { conversationTopics } from "./tools/conversationTopics/conversationTopics.js"; import { conversationTranscription } from "./tools/conversationTranscription/conversationTranscription.js"; +import { oauthClients } from "./tools/oauthClients/oauthClients.js"; +import { oauthClientUsage } from "./tools/oauthClientUsage/oauthClientUsage.js"; import { queryQueueVolumes } from "./tools/queryQueueVolumes/queryQueueVolumes.js"; import { sampleConversationsByQueue } from "./tools/sampleConversationsByQueue/sampleConversationsByQueue.js"; import { searchQueues } from "./tools/searchQueues.js"; @@ -19,13 +21,15 @@ const withAuth = OAuthClientCredentialsWrapper( const server: McpServer = new McpServer({ name: "Genesys Cloud", - version: "1.0.0", // Same version as version in package.json + version: "1.0.1", // Same version as version in package.json }); const routingApi = new platformClient.RoutingApi(); const analyticsApi = new platformClient.AnalyticsApi(); const speechTextAnalyticsApi = new platformClient.SpeechTextAnalyticsApi(); const recordingApi = new platformClient.RecordingApi(); +const oauthApi = new platformClient.OAuthApi(); +const authorizationApi = new platformClient.AuthorizationApi(); const searchQueuesTool = searchQueues({ routingApi }); server.registerTool( @@ -128,6 +132,33 @@ server.registerTool( withAuth(conversationTranscriptTool.call), ); +const oauthClientsTool = oauthClients({ + oauthApi, + authorizationApi, +}); +server.registerTool( + oauthClientsTool.schema.name, + { + description: oauthClientsTool.schema.description, + inputSchema: oauthClientsTool.schema.paramsSchema.shape, + annotations: oauthClientsTool.schema.annotations, + }, + withAuth(oauthClientsTool.call), +); + +const oauthClientUsageTool = oauthClientUsage({ + oauthApi, +}); +server.registerTool( + oauthClientUsageTool.schema.name, + { + description: oauthClientUsageTool.schema.description, + inputSchema: oauthClientUsageTool.schema.paramsSchema.shape, + annotations: oauthClientUsageTool.schema.annotations, + }, + withAuth(oauthClientUsageTool.call), +); + const transport = new StdioServerTransport(); await server.connect(transport); console.error("Genesys Cloud MCP Server running on stdio"); diff --git a/src/tools/oauthClientUsage/oauthClientUsage.test.ts b/src/tools/oauthClientUsage/oauthClientUsage.test.ts new file mode 100644 index 0000000..746a966 --- /dev/null +++ b/src/tools/oauthClientUsage/oauthClientUsage.test.ts @@ -0,0 +1,200 @@ +import { randomUUID } from "node:crypto"; +import { Client } from "@modelcontextprotocol/sdk/client/index.js"; +import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js"; +import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { McpError } from "@modelcontextprotocol/sdk/types.js"; +import type { MockedObjectDeep } from "@vitest/spy"; +import { beforeEach, describe, expect, test, vi } from "vitest"; +import { oauthClientUsage, type ToolDependencies } from "./oauthClientUsage.js"; + +describe("OAuth Client Usage", () => { + let toolDeps: MockedObjectDeep; + let client: Client; + let toolName: string; + + beforeEach(async () => { + toolDeps = { + oauthApi: { + postOauthClientUsageQuery: vi.fn(), + getOauthClientUsageQueryResult: vi.fn(), + }, + }; + const toolDefinition = oauthClientUsage(toolDeps); + toolName = toolDefinition.schema.name; + + const server = new McpServer({ name: "TestServer", version: "test" }); + server.tool( + toolDefinition.schema.name, + toolDefinition.schema.description, + toolDefinition.schema.paramsSchema.shape, + toolDefinition.schema.annotations, + toolDefinition.call, + ); + + const [serverTransport, clientTransport] = + InMemoryTransport.createLinkedPair(); + + await server.connect(serverTransport); + + client = new Client({ name: "test-client", version: "1.0.0" }); + await client.connect(clientTransport); + }); + + test("schema describes tool", async () => { + const tools = await client.listTools(); + expect(tools.tools[0]).toStrictEqual({ + name: "oauth_client_usage", + title: undefined, + _meta: undefined, + annotations: { title: "OAuth Client Usage" }, + description: + "Retrieves the usage of an OAuth Client for a given period. It returns the total number of requests and a breakdown of requests per organization.", + inputSchema: { + properties: { + oauthClientId: { + description: + "The UUID of the OAuth Client to retrieve the usage for (e.g., 00000000-0000-0000-0000-000000000000)", + format: "uuid", + type: "string", + }, + endDate: { + description: + "The end date/time in ISO-8601 format (e.g., '2024-01-07T23:59:59Z')", + type: "string", + }, + startDate: { + description: + "The start date/time in ISO-8601 format (e.g., '2024-01-01T00:00:00Z')", + type: "string", + }, + }, + required: ["oauthClientId", "startDate", "endDate"], + type: "object", + additionalProperties: false, + $schema: "http://json-schema.org/draft-07/schema#", + }, + }); + }); + + test("errors when no OAuth Client ID is provided", async () => { + await expect( + client.callTool({ + name: toolName, + arguments: { + oauthClientId: "", + startDate: "2024-01-01T00:00:00Z", + endDate: "2024-01-02T00:00:00Z", + }, + }), + ).rejects.toSatisfy( + (error: McpError) => + error.name === "McpError" && + error.message.includes("oauthClientId") && + error.message.includes("Invalid uuid"), + ); + }); + + test("errors when dates are invalid", async () => { + await expect( + client.callTool({ + name: toolName, + arguments: { + oauthClientId: randomUUID(), + startDate: "invalid-date", + endDate: "2024-01-02T00:00:00Z", + }, + }), + ).resolves.toStrictEqual({ + isError: true, + content: [ + { + type: "text", + text: JSON.stringify({ + errorMessage: "startDate is not a valid ISO-8601 date", + }), + }, + ], + }); + + await expect( + client.callTool({ + name: toolName, + arguments: { + oauthClientId: randomUUID(), + startDate: "2024-01-01T00:00:00Z", + endDate: "invalid-date", + }, + }), + ).resolves.toStrictEqual({ + isError: true, + content: [ + { + type: "text", + text: JSON.stringify({ + errorMessage: "endDate is not a valid ISO-8601 date", + }), + }, + ], + }); + }); + + test("OAuth Client usage returned for date range", async () => { + const oauthClientId = randomUUID(); + const executionId = randomUUID(); + const organisationOneId = randomUUID(); + const organisationTwoId = randomUUID(); + + toolDeps.oauthApi.postOauthClientUsageQuery.mockResolvedValue({ + executionId, + resultsUri: `/api/v2/oauth/clients/${oauthClientId}/usage/query/results/${executionId}`, + }); + + toolDeps.oauthApi.getOauthClientUsageQueryResult.mockResolvedValue({ + results: [ + { + organizationId: organisationOneId, + requests: 5, + }, + { + organizationId: organisationTwoId, + requests: 10, + }, + ], + queryStatus: "Complete", + }); + + const startDate = "2024-01-01T00:00:00Z"; + const endDate = "2024-01-02T00:00:00Z"; + const result = await client.callTool({ + name: toolName, + arguments: { + oauthClientId, + startDate, + endDate, + }, + }); + + expect(result).toStrictEqual({ + content: [ + { + type: "text", + text: JSON.stringify({ + startDate, + endDate, + totalRequest: 15, + requestsPerOrganisation: [ + { + organisationId: organisationOneId, + requests: 5, + }, + { + organisationId: organisationTwoId, + requests: 10, + }, + ], + }), + }, + ], + }); + }); +}); diff --git a/src/tools/oauthClientUsage/oauthClientUsage.ts b/src/tools/oauthClientUsage/oauthClientUsage.ts new file mode 100644 index 0000000..a7646e8 --- /dev/null +++ b/src/tools/oauthClientUsage/oauthClientUsage.ts @@ -0,0 +1,143 @@ +import type { Models, OAuthApi } from "purecloud-platform-client-v2"; +import { z } from "zod"; +import { createTool, type ToolFactory } from "../utils/createTool.js"; +import { errorResult } from "../utils/errorResult.js"; +import { isUnauthorisedError } from "../utils/genesys/isUnauthorisedError.js"; +import { waitFor } from "../utils/waitFor.js"; + +const MAX_ATTEMPTS = 10; + +export interface ToolDependencies { + readonly oauthApi: Pick< + OAuthApi, + "postOauthClientUsageQuery" | "getOauthClientUsageQueryResult" + >; +} + +const paramsSchema = z.object({ + oauthClientId: z + .string() + .uuid() + .describe( + "The UUID of the OAuth Client to retrieve the usage for (e.g., 00000000-0000-0000-0000-000000000000)", + ), + startDate: z + .string() + .describe( + "The start date/time in ISO-8601 format (e.g., '2024-01-01T00:00:00Z')", + ), + endDate: z + .string() + .describe( + "The end date/time in ISO-8601 format (e.g., '2024-01-07T23:59:59Z')", + ), +}); + +export const oauthClientUsage: ToolFactory< + ToolDependencies, + typeof paramsSchema +> = ({ oauthApi }) => + createTool({ + schema: { + name: "oauth_client_usage", + annotations: { title: "OAuth Client Usage" }, + description: + "Retrieves the usage of an OAuth Client for a given period. It returns the total number of requests and a breakdown of requests per organization.", + paramsSchema, + }, + call: async ({ oauthClientId, startDate, endDate }) => { + const from = new Date(startDate); + const to = new Date(endDate); + + if (Number.isNaN(from.getTime())) + return errorResult("startDate is not a valid ISO-8601 date"); + if (Number.isNaN(to.getTime())) + return errorResult("endDate is not a valid ISO-8601 date"); + if (from >= to) return errorResult("Start date must be before end date"); + const now = new Date(); + if (to > now) { + to.setTime(now.getTime()); + } + + let result: Models.UsageExecutionResult; + try { + result = await oauthApi.postOauthClientUsageQuery(oauthClientId, { + interval: `${from.toISOString()}/${to.toISOString()}`, + metrics: ["Requests"], + groupBy: ["OrganizationId"], + }); + console.log(result); + } catch (error: unknown) { + const errorMessage = isUnauthorisedError(error) + ? "Failed to retrieve usage of OAuth client: Unauthorised access. Please check API credentials or permissions" + : `Failed to retrieve usage of OAuth client: ${error instanceof Error ? error.message : JSON.stringify(error)}`; + + return errorResult(errorMessage); + } + + if (!result.executionId) { + return errorResult( + "Failed to get an Execution ID from Genesys Cloud's Platform API", + ); + } + + let apiUsageQueryResult: Models.ApiUsageQueryResult | undefined; + + let state: string | undefined; + let attempts = 0; + while (attempts < MAX_ATTEMPTS) { + const executionResult = await oauthApi.getOauthClientUsageQueryResult( + result.executionId, + oauthClientId, + ); + state = executionResult?.queryStatus?.toUpperCase() ?? "UNKNOWN"; + + if (state === "COMPLETE") { + apiUsageQueryResult = executionResult; + break; + } + + switch (executionResult.queryStatus) { + case "FAILED": + return errorResult( + `Failed to get usage data for OAuth Client ${oauthClientId}.`, + ); + case "UNKNOWN": + return errorResult( + "Execution returned an unknown or undefined state.", + ); + } + + await waitFor(3000); + attempts++; + } + + if (state !== "COMPLETE") { + return errorResult( + `Timed out waiting for OAuth Client usage to complete for client ${oauthClientId}.`, + ); + } + + return { + content: [ + { + type: "text", + text: JSON.stringify({ + startDate, + endDate, + totalRequest: (apiUsageQueryResult?.results ?? []).reduce( + (acc, curr) => acc + (curr.requests ?? 0), + 0, + ), + requestsPerOrganisation: (apiUsageQueryResult?.results ?? []).map( + (result) => ({ + organisationId: result.organizationId, + requests: result.requests, + }), + ), + }), + }, + ], + }; + }, + }); diff --git a/src/tools/oauthClients/formatOAuthClientJson.ts b/src/tools/oauthClients/formatOAuthClientJson.ts new file mode 100644 index 0000000..e8cd100 --- /dev/null +++ b/src/tools/oauthClients/formatOAuthClientJson.ts @@ -0,0 +1,69 @@ +import type { Models } from "purecloud-platform-client-v2"; +import type { + OAuthClientResponse, + RoleToDivisionsAssociation, +} from "./oauthClients.js"; + +type RoleId = string; +type DivisionId = string; + +function combineRolesAndDivisions( + client: Models.OAuthClientListing, + availableDivisions: Models.AuthzDivision[], + availableRoles: Models.DomainOrganizationRole[], +): RoleToDivisionsAssociation[] { + // 1. Create pre-populated Role Map + const roles = new Map>( + client.roleIds?.map((r) => [r, new Set()]), + ); + + // 2. Enrich each Role ID with associated Divisions + for (const roleDivision of client.roleDivisions ?? []) { + const role = roles.get(roleDivision.roleId); + if (role) { + role.add(roleDivision.divisionId); + } else { + roles.set(roleDivision.roleId, new Set(roleDivision.divisionId)); + } + } + + // 3. Format into model tool will return + // 3.1 Try to associate names of role and division if available + return Array.from(roles.entries()).map( + ([roleId, divisionIds]): RoleToDivisionsAssociation => { + const roleName = availableRoles.find((d) => d.id === roleId)?.name; + + return { + id: roleId, + name: roleName, + divisions: Array.from(divisionIds.values()).map((divisionId) => { + const divisionName = availableDivisions.find( + (d) => d.id === divisionId, + )?.name; + + return { + id: divisionId, + name: divisionName, + }; + }), + }; + }, + ); +} + +export function formatOAuthClientJson( + client: Models.OAuthClientListing, + availableDivisions: Models.AuthzDivision[], + availableRoles: Models.DomainOrganizationRole[], +): OAuthClientResponse { + return { + id: client.id, + name: client.name, + description: client.description, + roles: combineRolesAndDivisions(client, availableDivisions, availableRoles), + dateCreated: client.dateCreated, + scope: client.scope, + state: client.state, + dateToDelete: client.dateToDelete, + }; +} diff --git a/src/tools/oauthClients/oauthClients.test.ts b/src/tools/oauthClients/oauthClients.test.ts new file mode 100644 index 0000000..5531aa3 --- /dev/null +++ b/src/tools/oauthClients/oauthClients.test.ts @@ -0,0 +1,240 @@ +import { randomUUID } from "node:crypto"; +import { Client } from "@modelcontextprotocol/sdk/client/index.js"; +import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js"; +import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { MockedObjectDeep } from "@vitest/spy"; +import { beforeEach, describe, expect, test, vi } from "vitest"; +import { oauthClients, type ToolDependencies } from "./oauthClients.js"; + +describe("List OAuth Clients", () => { + let toolDeps: MockedObjectDeep; + let client: Client; + let toolName: string; + + beforeEach(async () => { + toolDeps = { + oauthApi: { + getOauthClients: vi.fn(), + }, + authorizationApi: { + getAuthorizationDivisions: vi.fn(), + getAuthorizationRoles: vi.fn(), + }, + }; + + const toolDefinition = oauthClients(toolDeps); + toolName = toolDefinition.schema.name; + + const server = new McpServer({ name: "TestServer", version: "test" }); + server.tool( + toolDefinition.schema.name, + toolDefinition.schema.description, + toolDefinition.schema.paramsSchema.shape, + toolDefinition.schema.annotations, + toolDefinition.call, + ); + + const [serverTransport, clientTransport] = + InMemoryTransport.createLinkedPair(); + + await server.connect(serverTransport); + + client = new Client({ name: "test-client", version: "1.0.0" }); + await client.connect(clientTransport); + }); + + test("schema describes tool", async () => { + const tools = await client.listTools(); + expect(tools.tools[0]).toStrictEqual({ + name: "oauth_clients", + title: undefined, + _meta: undefined, + annotations: { + title: "List OAuth Clients", + }, + description: + "Retrieves a list of all OAuth clients, including their associated roles and divisions. This tool is useful for auditing and managing OAuth clients in the Genesys Cloud organization.", + inputSchema: { + properties: {}, + type: "object", + additionalProperties: false, + $schema: "http://json-schema.org/draft-07/schema#", + }, + }); + }); + + test("error from Genesys Cloud's Platform SDK returned when getting OAuth Clients", async () => { + toolDeps.oauthApi.getOauthClients.mockRejectedValue( + new Error("Test Error Message"), + ); + + const result = await client.callTool({ + name: toolName, + arguments: {}, + }); + + expect(result).toStrictEqual({ + isError: true, + content: [ + { + type: "text", + text: JSON.stringify({ + errorMessage: + "Failed to retrieve list of all OAuth clients: Test Error Message", + }), + }, + ], + }); + }); + + test("error from Genesys Cloud returned when getting OAuth Clients", async () => { + const userId = randomUUID(); + const clientId = randomUUID(); + const roleId = randomUUID(); + const divisionId = randomUUID(); + + toolDeps.oauthApi.getOauthClients.mockResolvedValue({ + entities: [ + { + id: clientId, + name: "Test OAuth Client", + description: "Test OAuth Client", + roleIds: [roleId], + dateCreated: "2024-01-01T00:00:00Z", + createdBy: { + id: userId, + selfUri: `/api/v2/users/${userId}`, + }, + roleDivisions: [ + { + roleId, + divisionId, + }, + ], + state: "active", + }, + ], + }); + + toolDeps.authorizationApi.getAuthorizationDivisions.mockRejectedValue( + new Error("Test Error Message"), + ); + toolDeps.authorizationApi.getAuthorizationRoles.mockRejectedValue( + new Error("Test Error Message"), + ); + + const result = await client.callTool({ + name: toolName, + arguments: {}, + }); + + expect(result).toStrictEqual({ + content: [ + { + type: "text", + text: JSON.stringify([ + { + id: clientId, + name: "Test OAuth Client", + description: "Test OAuth Client", + roles: [ + { + id: roleId, + divisions: [{ id: divisionId }], + }, + ], + dateCreated: "2024-01-01T00:00:00Z", + state: "active", + }, + ]), + }, + ], + }); + }); + + test("oauth clients listed with associated role/division names", async () => { + const userId = randomUUID(); + const clientId = randomUUID(); + const roleId = randomUUID(); + const divisionId = randomUUID(); + + toolDeps.oauthApi.getOauthClients.mockResolvedValue({ + entities: [ + { + id: clientId, + name: "Test OAuth Client", + description: "Test OAuth Client", + roleIds: [roleId], + dateCreated: "2024-01-01T00:00:00Z", + createdBy: { + id: userId, + selfUri: `/api/v2/users/${userId}`, + }, + roleDivisions: [ + { + roleId, + divisionId, + }, + ], + state: "active", + }, + ], + }); + + toolDeps.authorizationApi.getAuthorizationDivisions.mockResolvedValue({ + entities: [ + { + id: divisionId, + name: "Test Division", + description: "Test Division", + homeDivision: false, + selfUri: `/api/v2/authorization/divisions/${divisionId}`, + }, + ], + }); + + toolDeps.authorizationApi.getAuthorizationRoles.mockResolvedValue({ + entities: [ + { + id: roleId, + name: "Test Role", + description: "Test Role", + }, + ], + }); + + const result = await client.callTool({ + name: toolName, + arguments: {}, + }); + + expect(result).toStrictEqual({ + content: [ + { + type: "text", + text: JSON.stringify([ + { + id: clientId, + name: "Test OAuth Client", + description: "Test OAuth Client", + roles: [ + { + id: roleId, + name: "Test Role", + divisions: [ + { + id: divisionId, + name: "Test Division", + }, + ], + }, + ], + dateCreated: "2024-01-01T00:00:00Z", + state: "active", + }, + ]), + }, + ], + }); + }); +}); diff --git a/src/tools/oauthClients/oauthClients.ts b/src/tools/oauthClients/oauthClients.ts new file mode 100644 index 0000000..5d4ce5b --- /dev/null +++ b/src/tools/oauthClients/oauthClients.ts @@ -0,0 +1,122 @@ +import type { + AuthorizationApi, + Models, + OAuthApi, +} from "purecloud-platform-client-v2"; +import { z } from "zod"; +import { createTool, type ToolFactory } from "../utils/createTool.js"; +import { errorResult } from "../utils/errorResult.js"; +import { isMissingPermissionsError } from "../utils/genesys/isMissingPermissionsError.js"; +import { isUnauthorisedError } from "../utils/genesys/isUnauthorisedError.js"; +import { formatOAuthClientJson } from "./formatOAuthClientJson.js"; + +export interface ToolDependencies { + readonly oauthApi: Pick; + readonly authorizationApi: Pick< + AuthorizationApi, + "getAuthorizationDivisions" | "getAuthorizationRoles" + >; +} + +export interface RoleToDivisionsAssociation { + id: string; + name?: string; + divisions: Array<{ + id: string; + name?: string; + }>; +} + +export interface OAuthClientResponse { + id?: string; + name: string; + description?: string; + // I combine `roleIds` and `roleDivisions` from the Genesys Cloud Model into just `roles`. + // Since `roleIds` is the authoritative list of roles associated with the OAuth Client I always want + // `roles` to contain these IDs. I cannot guarantee there is a division associated with the role in `roleDivisions` + // so `divisionId` is optional. + roles?: Array; + dateCreated?: string; + scope?: Array; + state?: string; + dateToDelete?: string; +} + +const paramsSchema = z.object({}); + +export const oauthClients: ToolFactory< + ToolDependencies, + typeof paramsSchema +> = ({ oauthApi, authorizationApi }) => + createTool({ + schema: { + name: "oauth_clients", + annotations: { title: "List OAuth Clients" }, + description: + "Retrieves a list of all OAuth clients, including their associated roles and divisions. This tool is useful for auditing and managing OAuth clients in the Genesys Cloud organization.", + paramsSchema, + }, + call: async () => { + let result: Models.OAuthClientEntityListing; + try { + result = await oauthApi.getOauthClients(); + } catch (error: unknown) { + const errorMessage = isUnauthorisedError(error) + ? "Failed to retrieve list of all OAuth clients: Unauthorised access. Please check API credentials or permissions" + : `Failed to retrieve list of all OAuth clients: ${error instanceof Error ? error.message : JSON.stringify(error)}`; + + return errorResult(errorMessage); + } + + const entities = result.entities ?? []; + + let divisions: Models.AuthzDivisionEntityListing; + try { + // No logic to limit request size as divisions unlikely to be large + divisions = await authorizationApi.getAuthorizationDivisions({ + pageSize: 99999, + }); + } catch (error: unknown) { + console.warn( + isMissingPermissionsError(error) + ? "Division names will not be populated.\nReason: Missing necessary permission." + : `Division names will not be populated.\nReason: Failed to retrieve list of divisions: ${error instanceof Error ? error.message : JSON.stringify(error)}`, + ); + } + + const roleIds = entities + .flatMap((e) => e.roleIds) + .filter((id) => id !== undefined); + + let roles: Models.OrganizationRoleEntityListing | undefined; + try { + roles = await authorizationApi.getAuthorizationRoles({ + id: roleIds, + pageSize: roleIds.length, + }); + } catch (error: unknown) { + console.warn( + isMissingPermissionsError(error) + ? "Role names will not be populated.\nReason: Missing necessary permission." + : `Role names will not be populated.\nReason: Failed to retrieve list of roles: ${error instanceof Error ? error.message : JSON.stringify(error)}`, + ); + } + + const response = (result?.entities ?? []).map((e) => + formatOAuthClientJson( + e, + divisions?.entities ?? [], + roles?.entities ?? [], + ), + ); + + return { + content: [ + { + type: "text", + text: JSON.stringify(response), + }, + ], + }; + }, + }); diff --git a/src/tools/queryQueueVolumes/queryQueueVolumes.test.ts b/src/tools/queryQueueVolumes/queryQueueVolumes.test.ts index 2ea6ac9..3a08700 100644 --- a/src/tools/queryQueueVolumes/queryQueueVolumes.test.ts +++ b/src/tools/queryQueueVolumes/queryQueueVolumes.test.ts @@ -53,7 +53,7 @@ describe("Query Queue Volumes Tool", () => { _meta: undefined, annotations: { title: "Query Queue Volumes" }, description: - "Returns a breakdown of how many conversations occurred in each specified queue between two dates. Useful for comparing workload across queues. MAX 300 queue IDs", + "Returns a breakdown of how many conversations occurred in each specified queue between two dates. Useful for comparing workload across queues. MAX 300 queue IDs.", inputSchema: { type: "object", properties: { diff --git a/src/tools/queryQueueVolumes/queryQueueVolumes.ts b/src/tools/queryQueueVolumes/queryQueueVolumes.ts index 11a1839..a873a44 100644 --- a/src/tools/queryQueueVolumes/queryQueueVolumes.ts +++ b/src/tools/queryQueueVolumes/queryQueueVolumes.ts @@ -51,7 +51,7 @@ export const queryQueueVolumes: ToolFactory< name: "query_queue_volumes", annotations: { title: "Query Queue Volumes" }, description: - "Returns a breakdown of how many conversations occurred in each specified queue between two dates. Useful for comparing workload across queues. MAX 300 queue IDs", + "Returns a breakdown of how many conversations occurred in each specified queue between two dates. Useful for comparing workload across queues. MAX 300 queue IDs.", paramsSchema, }, call: async ({ queueIds, startDate, endDate }) => { diff --git a/src/tools/utils/genesys/isMissingPermissionsError.ts b/src/tools/utils/genesys/isMissingPermissionsError.ts new file mode 100644 index 0000000..8175e2d --- /dev/null +++ b/src/tools/utils/genesys/isMissingPermissionsError.ts @@ -0,0 +1,13 @@ +/** + * Checks if an object is a Missing Permissions error. + */ +export function isMissingPermissionsError(obj: unknown): boolean { + if (typeof obj === "object" && obj !== null) { + const error = obj as { code?: string; status?: number }; + + if (error.code && error.status) { + return error.code === "missing.any.permissions" && error.status === 403; + } + } + return false; +} diff --git a/tests/integration/serverRuns.test.ts b/tests/integration/serverRuns.test.ts index 7c272f0..c59a6c0 100644 --- a/tests/integration/serverRuns.test.ts +++ b/tests/integration/serverRuns.test.ts @@ -44,6 +44,8 @@ describe("Server Runs", () => { "conversation_topics", "search_voice_conversations", "conversation_transcript", + "oauth_clients", + "oauth_client_usage", ]); }); @@ -79,7 +81,7 @@ describe("Server Runs", () => { client = new Client({ name: "test-client", - version: "1.0.0", + version: "1.0.1", }); await client.connect(transport);